This incorporates some (but not all) of Justin's suggestions. This patch will handle request bodies (both c-l and chunk encoded). Not tested with pipelined requests but I think it will work.

Recap... ap_http_filter is now responsible for parsing the http protocol header fields. This patch gets its performance improvement by replacing multiple calls to ap_get_brigade(AP_MODE_GETLINE) made in read_request_line and ap_get_mime_headers with a single call to ap_get_brigade(AP_MODE_READBYTES) made from ap_http_filter (aka HTTP_IN). Because ap_get_brigade(AP_MODE_READBYTES) can fetch request body and/or a pipelined request bytes, the filter needs to be able to setaside bytes received on ap_get_brigade that do not pertain to this request (this is exactly what the core_input_filter does). ap_http_filter uses a state driven algorithm to deliniate between request headers, request bodies and pipelined requests.

Still to do:
- optionally refactor core_input_filter to account for ap_http_filter's new function
- cleanup the error paths (code uses two different ways to handle sending error responses to the client, which is goofy)
- handle the con_rec->r fooness a bit more cleanly
- get this patch working for proxy requests
- properly handle trailers (the code is commented out now)


Bill


Index: include/http_protocol.h
===================================================================
RCS file: /home/cvs/httpd-2.0/include/http_protocol.h,v
retrieving revision 1.84
diff -u -r1.84 http_protocol.h
--- include/http_protocol.h     3 Feb 2003 17:52:53 -0000       1.84
+++ include/http_protocol.h     3 Mar 2003 19:58:17 -0000
@@ -717,6 +717,9 @@
                                                               apr_bucket_brigade *);
 AP_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(ap_filter_t *f, 
apr_bucket_brigade *b);
 
+apr_status_t ap_http_header_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
+                                         ap_input_mode_t mode, apr_read_type_e block,
+                                         apr_off_t readbytes);
 /*
  * Setting up the protocol fields for subsidiary requests...
  * Also, a wrapup function to keep the internal accounting straight.
Index: include/httpd.h
===================================================================
RCS file: /home/cvs/httpd-2.0/include/httpd.h,v
retrieving revision 1.194
diff -u -r1.194 httpd.h
--- include/httpd.h     3 Feb 2003 17:52:53 -0000       1.194
+++ include/httpd.h     3 Mar 2003 19:58:17 -0000
@@ -1013,6 +1013,7 @@
     void *sbh;
     /** The bucket allocator to use for all bucket/brigade creations */
     struct apr_bucket_alloc_t *bucket_alloc;
+    request_rec *r;
 };
 
 /* Per-vhost config... */
Index: modules/http/http_core.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/http/http_core.c,v
retrieving revision 1.309
diff -u -r1.309 http_core.c
--- modules/http/http_core.c    3 Feb 2003 17:53:03 -0000       1.309
+++ modules/http/http_core.c    3 Mar 2003 19:58:17 -0000
@@ -281,7 +281,6 @@
      * Read and process each request found on our connection
      * until no requests are left or we decide to close.
      */
- 
     ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
     while ((r = ap_read_request(c)) != NULL) {
 
@@ -327,9 +326,16 @@
 
     return OK;
 }
-
+static int http_core_pre_connection(conn_rec *c, void *csd)
+{
+    ap_add_input_filter_handle(ap_http_input_filter_handle, 
+                               NULL, NULL, c);     
+    return OK;
+}
 static void register_hooks(apr_pool_t *p)
 {
+    ap_hook_pre_connection(http_core_pre_connection, NULL, NULL,
+                           APR_HOOK_LAST);
     ap_hook_process_connection(ap_process_http_connection,NULL,NULL,
                               APR_HOOK_REALLY_LAST);
     ap_hook_map_to_storage(ap_send_http_trace,NULL,NULL,APR_HOOK_MIDDLE);
@@ -338,7 +344,7 @@
     ap_hook_create_request(http_create_request, NULL, NULL, APR_HOOK_REALLY_LAST);
     ap_http_input_filter_handle =
         ap_register_input_filter("HTTP_IN", ap_http_filter,
-                                 NULL, AP_FTYPE_PROTOCOL);
+                                 NULL, AP_FTYPE_CONNECTION);
     ap_http_header_filter_handle =
         ap_register_output_filter("HTTP_HEADER", ap_http_header_filter, 
                                   NULL, AP_FTYPE_PROTOCOL);
Index: modules/http/http_protocol.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/http/http_protocol.c,v
retrieving revision 1.465
diff -u -r1.465 http_protocol.c
--- modules/http/http_protocol.c        19 Feb 2003 06:50:10 -0000      1.465
+++ modules/http/http_protocol.c        3 Mar 2003 19:58:18 -0000
@@ -740,296 +740,706 @@
     return NULL;
 }
 
-static long get_chunk_size(char *);
-
-typedef struct http_filter_ctx {
-    apr_off_t remaining;
-    apr_off_t limit;
-    apr_off_t limit_used;
-    enum {
-        BODY_NONE,
-        BODY_LENGTH,
-        BODY_CHUNK
-    } state;
-    int eos_sent;
-} http_ctx_t;
-
-/* This is the HTTP_INPUT filter for HTTP requests and responses from 
- * proxied servers (mod_proxy).  It handles chunked and content-length 
- * bodies.  This can only be inserted/used after the headers
- * are successfully parsed. 
+/*
+ * Iterate across bbIn looking for an APR_ASCII_LF. If no LF is found, attempt to 
+ * read more bytes from the filter stack and keep searching until a LF is found or 
+ * an error condition is encountered. Unnused (unconsumed?) buckets are returned to 
+ * the caller in bbIn.
  */
-apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
-                            ap_input_mode_t mode, apr_read_type_e block,
-                            apr_off_t readbytes)
+static apr_status_t ap_brigade_getline(ap_filter_t *f,
+                                       apr_bucket_brigade *bbIn,
+                                       apr_read_type_e block,
+                                       apr_off_t maxbytes,
+                                       char **s,
+                                       apr_size_t *slen,
+                                       apr_pool_t *p)
 {
-    apr_bucket *e;
-    http_ctx_t *ctx = f->ctx;
     apr_status_t rv;
-    apr_off_t totalread;
-
-    /* just get out of the way of things we don't want. */
-    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
-        return ap_get_brigade(f->next, b, mode, block, readbytes);
-    }
-
-    if (!ctx) {
-        const char *tenc, *lenp;
-        f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx));
-        ctx->state = BODY_NONE;
-        ctx->remaining = 0;
-        ctx->limit_used = 0;
-        ctx->eos_sent = 0;
-
-        /* LimitRequestBody does not apply to proxied responses.
-         * Consider implementing this check in its own filter. 
-         * Would adding a directive to limit the size of proxied 
-         * responses be useful?
-         */
-        if (!f->r->proxyreq) {
-            ctx->limit = ap_get_limit_req_body(f->r);
-        }
-        else {
-            ctx->limit = 0;
-        }
+    apr_size_t readbytes = 0;
+    char *pos;
+    char *cur = NULL;
+    const char *str;
+    apr_size_t len;
+    apr_bucket *e;
+    apr_bucket_brigade *bb = bbIn;
+    apr_bucket_brigade *bbNew = NULL;
 
-        tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding");
-        lenp = apr_table_get(f->r->headers_in, "Content-Length");
+    *s = NULL;
 
-        if (tenc) {
-            if (!strcasecmp(tenc, "chunked")) {
-                ctx->state = BODY_CHUNK;
+    while (1) {
+        for (e = APR_BRIGADE_FIRST(bb);  e != APR_BRIGADE_SENTINEL(bb); 
+             e = APR_BUCKET_NEXT(e)) {
+            /* This doesn't seem quite the right way to handle an EOS bucket */
+            if (APR_BUCKET_IS_EOS(e)) {
+                *slen = 0;
+                *s = '\0';
+                return APR_SUCCESS;
             }
-        }
-        else if (lenp) {
-            int conversion_error = 0;
-            char *endstr;
-
-            ctx->state = BODY_LENGTH;
-            errno = 0;
-            ctx->remaining = strtol(lenp, &endstr, 10);        /* we depend on ANSI */
-
-            /* This protects us from over/underflow (the errno check),
-             * non-digit chars in the string (excluding leading space)
-             * (the endstr checks) and a negative number. Depending
-             * on the strtol implementation, the errno check may also
-             * trigger on an all whitespace string */
-            if (errno || (endstr && *endstr) || (ctx->remaining < 0)) {
-                 conversion_error = 1; 
+            rv = apr_bucket_read(e, &str, &len, block);
+            if (rv != APR_SUCCESS) {
+                return rv;
+            }
+            if (!len) {
+                continue;
+            }
+            pos = memchr(str, APR_ASCII_LF, len);
+            if (!pos) {
+                readbytes = readbytes + len;
+                /* do a length check */
+                if (readbytes > maxbytes) {
+                    return APR_ENOSPC;
+                }
+                continue;
+            }
+            /* Found a LF */
+            if (!readbytes) {
+                /* The common case: the entire LF terminated string is in 
+                 * the first bucket.
+                 */
+                len = pos - str + 1;
+                /* Optimization to prevent leaving a 0 byte bucket in the brigade */
+                if (len != e->length) {
+                    apr_bucket_split(e, len);
+                }
+            }
+            else {
+                /* The uncommon case: The LF terminated string spans multiple
+                 * buckets.
+                 * XXX: pflatten allocates storage then apr_palloc below
+                 * allocates storage for the exact same content. Optimize 
+                 * away one of the pallocs and memcpy.
+                 */
+                char *tmp_str;
+                apr_bucket_brigade *tmpbb;
+                apr_brigade_partition(bb, readbytes + len, &e);
+                tmpbb = apr_brigade_split(bb, e);
+                apr_brigade_pflatten(bb, &tmp_str, &len, p);
+                apr_brigade_cleanup(bb);
+                APR_BRIGADE_CONCAT(bb, tmpbb);
+                apr_brigade_destroy(tmpbb);
+                apr_brigade_destroy(bbNew);
+                str = tmp_str;
+                pos = memchr(str, APR_ASCII_LF, len);
             }
 
-            if (conversion_error) {
-                apr_bucket_brigade *bb;
-
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
-                              "Invalid Content-Length");
-
-                bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-                e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
-                                           f->r->pool, f->c->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(bb, e);
-                e = apr_bucket_eos_create(f->c->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(bb, e);
-                ctx->eos_sent = 1;
-                return ap_pass_brigade(f->r->output_filters, bb);
+            /* Trim off the APR_ASCII_LF, APS_ASCII_CR and any extra trailing spaces 
+             * or tabs except for the first space or tab at the beginning of a blank 
+             * string.  This makes it much easier to check field values for exact 
matches.
+             */
+            if (pos > str && *(pos - 1) == APR_ASCII_CR) {
+                --pos;
             }
-            
-            /* If we have a limit in effect and we know the C-L ahead of
-             * time, stop it here if it is invalid. 
-             */ 
-            if (ctx->limit && ctx->limit < ctx->remaining) {
-                apr_bucket_brigade *bb;
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
-                          "Requested content-length of %" APR_OFF_T_FMT 
-                          " is larger than the configured limit"
-                          " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit);
-                bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-                e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
-                                           f->r->pool, f->c->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(bb, e);
-                e = apr_bucket_eos_create(f->c->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(bb, e);
-                ctx->eos_sent = 1;
-                return ap_pass_brigade(f->r->output_filters, bb);
+            while (pos > (str + 1)
+                   && (*(pos - 1) == APR_ASCII_BLANK || *(pos - 1) == APR_ASCII_TAB)) 
{
+                --pos;
+            }
+            /* Since we want to remove the LF from the line, we'll go ahead
+             * and set this last character to be the term NULL.
+             */
+            *pos = '\0';
+            *slen = len = pos - str;
+            if (len) {
+                *s = apr_palloc(p, len + 1);
+                memcpy(*s, str, len + 1);
+            }
+            if (!readbytes) {
+                apr_bucket_delete(e);
             }
+            return APR_SUCCESS;
         }
 
-        /* If we don't have a request entity indicated by the headers, EOS.
-         * (BODY_NONE is a valid intermediate state due to trailers,
-         *  but it isn't a valid starting state.)
-         *
-         * RFC 2616 Section 4.4 note 5 states that connection-close
-         * is invalid for a request entity - request bodies must be
-         * denoted by C-L or T-E: chunked.
-         *
-         * Note that since the proxy uses this filter to handle the
-         * proxied *response*, proxy responses MUST be exempt.
+        /* Oh jeesh, the brigade does not contain a LF. Fetch another brigade 
+         * and keep searching until we find one or hit an error. This should
+         * not be a common case.
          */
-        if (ctx->state == BODY_NONE && f->r->proxyreq != PROXYREQ_RESPONSE) {
-            e = apr_bucket_eos_create(f->c->bucket_alloc);
-            APR_BRIGADE_INSERT_TAIL(b, e);
-            ctx->eos_sent = 1;
-            return APR_SUCCESS;
+        if (!bbNew) {
+            bbNew = apr_brigade_create(p, f->c->bucket_alloc);
+        }
+        rv = ap_get_brigade(f, bbNew, AP_MODE_GETLINE, APR_BLOCK_READ, 0);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+        if (APR_BRIGADE_EMPTY(bbNew)) {
+            return APR_EGENERAL;
         }
+        APR_BRIGADE_CONCAT(bb, bbNew);
+    }
+    return APR_SUCCESS;
+}
+static apr_status_t read_request_line(ap_filter_t *fnext, request_rec *r, 
apr_bucket_brigade *b,
+                                      ap_input_mode_t mode)
+{
+    apr_status_t rv;
+    const char *ll;
+    const char *uri;
+    const char *pro;
+    int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
+    char http[5];
+    apr_size_t len = 0;
 
-        /* Since we're about to read data, send 100-Continue if needed.
-         * Only valid on chunked and C-L bodies where the C-L is > 0. */
-        if ((ctx->state == BODY_CHUNK || 
-            (ctx->state == BODY_LENGTH && ctx->remaining > 0)) &&
-            f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1)) {
-            char *tmp;
-            apr_bucket_brigade *bb;
+    r->the_request = NULL;
+    r->request_time = apr_time_now();
+    do {
+        /* Read past empty lines until we get a real request line,
+         * a read error, the connection closes (EOF), or we timeout.
+         * We skip empty lines because browsers have to tack a CRLF on to the end
+         * of POSTs to support old CERN webservers.  
+         */
+        rv = ap_brigade_getline(fnext, b, mode, DEFAULT_LIMIT_REQUEST_LINE+2,
+                                &r->the_request, &len, r->pool);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+    } while (len <= 0);
 
-            tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ",
-                              status_lines[0], CRLF CRLF, NULL);
-            bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-            e = apr_bucket_pool_create(tmp, strlen(tmp), f->r->pool,
-                                       f->c->bucket_alloc);
-            APR_BRIGADE_INSERT_HEAD(bb, e);
-            e = apr_bucket_flush_create(f->c->bucket_alloc);
-            APR_BRIGADE_INSERT_TAIL(bb, e);
+    ll = r->the_request;
+    r->method = ap_getword_white(r->pool, &ll);
+    uri = ap_getword_white(r->pool, &ll);
 
-            ap_pass_brigade(f->c->output_filters, bb);
-        }
+    /* Provide quick information about the request method as soon as known */
+    r->method_number = ap_method_number_of(r->method);
+    if (r->method_number == M_GET && r->method[0] == 'H') {
+        r->header_only = 1;
+    }
 
-        /* We can't read the chunk until after sending 100 if required. */
-        if (ctx->state == BODY_CHUNK) {
-            char line[30];
-            apr_bucket_brigade *bb;
-            apr_size_t len = 30;
-            apr_off_t brigade_length;
+    ap_parse_uri(r, uri);
 
-            bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+    /* ap_getline returns (size of max buffer - 1) if it fills up the
+     * buffer before finding the end-of-line.  This is only going to
+     * happen if it exceeds the configured limit for a request-line.
+     * The cast is safe, limit_req_line cannot be negative
+     */
+    if (len > (apr_size_t) r->server->limit_req_line) {
+        r->status    = HTTP_REQUEST_URI_TOO_LARGE;
+        r->proto_num = HTTP_VERSION(1,0);
+        r->protocol  = apr_pstrdup(r->pool, "HTTP/1.0");
+        return 0; /* XXX return an error code */
+    }
 
-            rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
-                                APR_BLOCK_READ, 0);
+    if (ll[0]) {
+        r->assbackwards = 0;
+        pro = ll;
+        len = strlen(ll);
+    } else {
+        r->assbackwards = 1;
+        pro = "HTTP/0.9";
+        len = 8;
+    }
+    r->protocol = apr_pstrmemdup(r->pool, pro, (apr_size_t) len);
+
+    /* Avoid sscanf in the common case */
+    if (len == 8
+        && pro[0] == 'H' && pro[1] == 'T' && pro[2] == 'T' && pro[3] == 'P'
+        && pro[4] == '/' && apr_isdigit(pro[5]) && pro[6] == '.'
+        && apr_isdigit(pro[7])) {
+        r->proto_num = HTTP_VERSION(pro[5] - '0', pro[7] - '0');
+    }
+    else if (3 == sscanf(r->protocol, "%4s/%u.%u", http, &major, &minor)
+             && (strcasecmp("http", http) == 0)
+             && (minor < HTTP_VERSION(1, 0)) ) /* don't allow HTTP/0.1000 */
+        r->proto_num = HTTP_VERSION(major, minor);
+    else
+        r->proto_num = HTTP_VERSION(1, 0);
 
-            if (rv == APR_SUCCESS) {
-                /* We have to check the length of the brigade we got back.
-                 * We will not accept partial lines.
-                 */
-                rv = apr_brigade_length(bb, 1, &brigade_length);
-                if (rv == APR_SUCCESS
-                    && brigade_length > f->r->server->limit_req_line) {
-                    rv = APR_ENOSPC;
-                }
-                if (rv == APR_SUCCESS) {
-                    rv = apr_brigade_flatten(bb, line, &len);
-                    if (rv == APR_SUCCESS) {
-                        ctx->remaining = get_chunk_size(line);
-                    }
-                }
-            }
-            apr_brigade_cleanup(bb);
+    return APR_SUCCESS;
+}
 
-            /* Detect chunksize error (such as overflow) */
-            if (rv != APR_SUCCESS || ctx->remaining < 0) {
-                ctx->remaining = 0; /* Reset it in case we have to
-                                     * come back here later */
-                e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
-                                           f->r->pool,
-                                           f->c->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(bb, e);
-                e = apr_bucket_eos_create(f->c->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(bb, e);
-                ctx->eos_sent = 1;
-                return ap_pass_brigade(f->r->output_filters, bb);
-            }
-
-            if (!ctx->remaining) {
-                /* Handle trailers by calling ap_get_mime_headers again! */
-                ctx->state = BODY_NONE;
-                ap_get_mime_headers(f->r);
-                e = apr_bucket_eos_create(f->c->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(b, e);
-                ctx->eos_sent = 1;
-                return APR_SUCCESS;
-            }
-        } 
-    }
+static apr_status_t read_header_fields(ap_filter_t *fnext, request_rec *r, 
+                                       apr_bucket_brigade *b, 
+                                       ap_input_mode_t mode)
+{
+    apr_status_t rv;
+    char *last_field = NULL;
+    apr_size_t last_len = 0;
+    apr_size_t alloc_len = 0;
+    char *field;
+    char *value;
+    apr_size_t len;
+    int fields_read = 0;
+    apr_table_t *tmp_headers;
 
-    if (ctx->eos_sent) {
-        e = apr_bucket_eos_create(f->c->bucket_alloc);
-        APR_BRIGADE_INSERT_TAIL(b, e);
+    if (r->assbackwards) {
+        if (r->header_only) {
+            /* Client asked for headers only with HTTP/0.9, which doesn't send
+             * headers! Have to dink things just to make sure the error message
+             * comes through...
+             */
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                          "client sent invalid HTTP/0.9 request: HEAD %s",
+                          r->uri);
+            r->header_only = 0;
+            r->status = HTTP_BAD_REQUEST;
+            ap_send_error_response(r, 0);
+            ap_run_log_transaction(r);
+            return APR_EGENERAL;
+        }
+
+        /* No headers on an HTTP/0.9 request */
         return APR_SUCCESS;
     }
-        
-    if (!ctx->remaining) {
-        switch (ctx->state) {
-        case BODY_NONE:
-            break;
-        case BODY_LENGTH:
-            e = apr_bucket_eos_create(f->c->bucket_alloc);
-            APR_BRIGADE_INSERT_TAIL(b, e);
-            ctx->eos_sent = 1;
-            return APR_SUCCESS;
-        case BODY_CHUNK:
-            {
-                char line[30];
-                apr_bucket_brigade *bb;
-                apr_size_t len = 30;
-
-                bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-
-                /* We need to read the CRLF after the chunk.  */
-                rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
-                                    APR_BLOCK_READ, 0);
-                apr_brigade_cleanup(bb);
 
-                if (rv == APR_SUCCESS) {
-                    /* Read the real chunk line. */
-                    rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
-                                        APR_BLOCK_READ, 0);
-                    if (rv == APR_SUCCESS) {
-                        rv = apr_brigade_flatten(bb, line, &len);
-                        if (rv == APR_SUCCESS) {
-                            ctx->remaining = get_chunk_size(line);
-                        }
+    /* We'll use apr_table_overlap later to merge these into r->headers_in. */
+    tmp_headers = apr_table_make(r->pool, 50);
+
+    /*
+     * Read header lines until we get the empty separator line, a read error,
+     * the connection closes (EOF), reach the server limit, or we timeout.
+     */
+    while (1) {
+        int folded = 0;
+        rv = ap_brigade_getline(fnext, b, mode, DEFAULT_LIMIT_REQUEST_LINE+2,
+                                &field, &len, r->pool);
+
+        /* ap_rgetline returns APR_ENOSPC if it fills up the buffer before
+         * finding the end-of-line.  This is only going to happen if it
+         * exceeds the configured limit for a field size.
+         * The cast is safe, limit_req_fieldsize cannot be negative
+         */
+        if (rv == APR_ENOSPC
+            || (rv == APR_SUCCESS 
+                && len > (apr_size_t)r->server->limit_req_fieldsize)) {
+            r->status = HTTP_BAD_REQUEST;
+            apr_table_setn(r->notes, "error-notes",
+                           apr_pstrcat(r->pool,
+                                       "Size of a request header field "
+                                       "exceeds server limit.<br />\n"
+                                       "<pre>\n",
+                                       ap_escape_html(r->pool, field),
+                                       "</pre>\n", NULL));
+            return APR_ENOSPC;
+        }
+
+        if (rv != APR_SUCCESS) {
+            r->status = HTTP_BAD_REQUEST;
+            return rv;
+        }
+
+        if (last_field != NULL) {
+            if ((len > 0) && ((*field == '\t') || *field == ' ')) {
+                /* This line is a continuation of the preceding line(s),
+                 * so append it to the line that we've set aside.
+                 * Note: this uses a power-of-two allocator to avoid
+                 * doing O(n) allocs and using O(n^2) space for
+                 * continuations that span many many lines.
+                 */
+                if (last_len + len > alloc_len) {
+                    char *fold_buf;
+                    alloc_len += alloc_len;
+                    if (last_len + len > alloc_len) {
+                        alloc_len = last_len + len;
                     }
-                    apr_brigade_cleanup(bb);
+                    fold_buf = (char *)apr_palloc(r->pool, alloc_len);
+                    memcpy(fold_buf, last_field, last_len);
+                    last_field = fold_buf;
+                }
+                memcpy(last_field + last_len, field, len +1); /* +1 for nul */
+                last_len += len;
+                folded = 1;
+            }
+            else {
+                if (r->server->limit_req_fields
+                    && (++fields_read > r->server->limit_req_fields)) {
+                    r->status = HTTP_BAD_REQUEST;
+                    apr_table_setn(r->notes, "error-notes",
+                                   "The number of request header fields "
+                                   "exceeds this server's limit.");
+                    return APR_ENOSPC;  /* XXX: ??? */
                 }
 
-                /* Detect chunksize error (such as overflow) */
-                if (rv != APR_SUCCESS || ctx->remaining < 0) {
-                    ctx->remaining = 0; /* Reset it in case we have to
-                                         * come back here later */
-                    e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE,
-                                               NULL, f->r->pool,
-                                               f->c->bucket_alloc);
-                    APR_BRIGADE_INSERT_TAIL(bb, e);
-                    e = apr_bucket_eos_create(f->c->bucket_alloc);
-                    APR_BRIGADE_INSERT_TAIL(bb, e);
-                    ctx->eos_sent = 1;
-                    return ap_pass_brigade(f->r->output_filters, bb);
+                if (!(value = strchr(last_field, ':'))) { /* Find ':' or    */
+                    r->status = HTTP_BAD_REQUEST;      /* abort bad request */
+                    apr_table_setn(r->notes, "error-notes",
+                                   apr_pstrcat(r->pool,
+                                               "Request header field is "
+                                               "missing ':' separator.<br />\n"
+                                               "<pre>\n",
+                                               ap_escape_html(r->pool,
+                                                              last_field),
+                                               "</pre>\n", NULL));
+                    return APR_ENOSPC; /* XXX ??? */
                 }
 
-                if (!ctx->remaining) {
-                    /* Handle trailers by calling ap_get_mime_headers again! */
-                    ctx->state = BODY_NONE;
-                    ap_get_mime_headers(f->r);
-                    e = apr_bucket_eos_create(f->c->bucket_alloc);
-                    APR_BRIGADE_INSERT_TAIL(b, e);
-                    ctx->eos_sent = 1;
-                    return APR_SUCCESS;
+                *value = '\0';
+                ++value;
+                while (*value == ' ' || *value == '\t') {
+                    ++value;            /* Skip to start of value   */
                 }
-            }
+
+                apr_table_addn(tmp_headers, last_field, value);
+
+                /* reset the alloc_len so that we'll allocate a new
+                 * buffer if we have to do any more folding: we can't
+                 * use the previous buffer because its contents are
+                 * now part of tmp_headers
+                 */
+                alloc_len = 0;
+
+            } /* end if current line is not a continuation starting with tab */
+        }
+
+        /* Found a blank line, stop. */
+        if (len == 0) {
             break;
         }
+
+        /* Keep track of this line so that we can parse it on
+         * the next loop iteration.  (In the folded case, last_field
+         * has been updated already.)
+         */
+        if (!folded) {
+            last_field = field;
+            last_len = len;
+        }
     }
 
-    /* Ensure that the caller can not go over our boundary point. */
-    if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) {
-        if (ctx->remaining < readbytes) {
-            readbytes = ctx->remaining;
+    apr_table_overlap(r->headers_in, tmp_headers, APR_OVERLAP_TABLES_MERGE);
+    return APR_SUCCESS;
+}
+
+static apr_status_t check_header_fields(request_rec *r)
+{
+    const char *expect;
+    if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1)))
+        || ((r->proto_num == HTTP_VERSION(1, 1))
+            && !apr_table_get(r->headers_in, "Host"))) {
+        /*
+         * Client sent us an HTTP/1.1 or later request without telling us the
+         * hostname, either with a full URL or a Host: header. We therefore
+         * need to (as per the 1.1 spec) send an error.  As a special case,
+         * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
+         * a Host: header, and the server MUST respond with 400 if it doesn't.
+         */
+        r->status = HTTP_BAD_REQUEST;
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "client sent HTTP/1.1 request without hostname "
+                      "(see RFC2616 section 14.23): %s", r->uri);
+    }
+
+    if (r->status != HTTP_OK) {
+        ap_send_error_response(r, 0);
+        ap_run_log_transaction(r);
+        return APR_EGENERAL;
+    }
+
+    if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
+        && (expect[0] != '\0')) {
+        /*
+         * The Expect header field was added to HTTP/1.1 after RFC 2068
+         * as a means to signal when a 100 response is desired and,
+         * unfortunately, to signal a poor man's mandatory extension that
+         * the server must understand or return 417 Expectation Failed.
+         */
+        if (strcasecmp(expect, "100-continue") == 0) {
+            r->expecting_100 = 1;
+        }
+        else {
+            r->status = HTTP_EXPECTATION_FAILED;
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+                          "client sent an unrecognized expectation value of "
+                          "Expect: %s", expect);
+            ap_send_error_response(r, 0);
+            ap_run_log_transaction(r);
+            return APR_EGENERAL;
         }
-        AP_DEBUG_ASSERT(readbytes > 0);
     }
+    return APR_SUCCESS;
+}
 
-    rv = ap_get_brigade(f->next, b, mode, block, readbytes);
+static long get_chunk_size(char *);
 
-    if (rv != APR_SUCCESS) {
+typedef struct http_input_filter_ctx {
+    apr_bucket_brigade *b;
+    enum {
+        HIF_READ_HEADER_FIELDS = 1,
+        HIF_READ_BODY_LENGTH,
+        HIF_READ_BODY_CHUNKED,
+        HIF_BODY_NONE,
+        HIF_EOS_SENT
+    } state;
+    apr_off_t remaining;
+    apr_off_t limit;
+    apr_off_t limit_used;
+    const char *tenc;
+    const char *lenp;
+} http_input_filter_ctx_t;
+static apr_status_t reset_state(void *arg)
+{
+    http_input_filter_ctx_t *ctx = (http_input_filter_ctx_t *) arg;
+    ctx->state = HIF_READ_HEADER_FIELDS;
+    return APR_SUCCESS;
+}
+apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
+                            ap_input_mode_t mode, apr_read_type_e block,
+                            apr_off_t readbytes)
+{
+    apr_status_t rv;
+    http_input_filter_ctx_t *ctx = f->ctx;
+    apr_bucket *e;
+    apr_off_t totalread;
+    request_rec *r;
+
+    /* TODO: How to get the request_rec pointer out of the conn_rec? */
+    r = f->r = f->c->r;
+    
+    if (!ctx) {
+        ctx = f->ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
+        ctx->state = HIF_READ_HEADER_FIELDS;
+    }
+
+    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
+        /* Pick up any buckets setaside in the ctx before returning */
+        rv = ap_get_brigade(f->next, b, mode, APR_BLOCK_READ, readbytes);
+        if (ctx->b && !APR_BRIGADE_EMPTY(ctx->b)) {
+            APR_BRIGADE_PREPEND(b, ctx->b);
+        }
         return rv;
     }
+    if (ctx->b && !APR_BRIGADE_EMPTY(ctx->b)) {
+        /* Pick up any setaside buckets and place them into brigede b */
+        APR_BRIGADE_PREPEND(b, ctx->b);
+    }
+
+    switch (ctx->state) {
+    case HIF_READ_HEADER_FIELDS: 
+      {
+          /* Read and parse the headers. Start by fetching and reading the 
+           * request line. Brigade b may not be empty if this request was 
+           * pipelined with the previous request on this connection.
+           */
+          if (APR_BRIGADE_EMPTY(b)) {
+              rv = ap_get_brigade(f->next, b, AP_MODE_READBYTES, APR_BLOCK_READ, 
readbytes);
+              if (rv != APR_SUCCESS) {
+                  return rv;
+              }
+          }
+          rv = read_request_line(f->next, r, b, mode);
+          if (rv != APR_SUCCESS) {
+              return rv;
+          }
+          rv = read_header_fields(f->next, r, b, mode);
+          if (rv != APR_SUCCESS) {
+              if (r->status != HTTP_REQUEST_TIME_OUT) {
+                  ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                                "request failed: error reading the headers");
+                  ap_send_error_response(r, 0);
+                  ap_run_log_transaction(r);
+              }
+              return rv;
+          }
+          /* Set aside any unused buckets */
+          if (!APR_BRIGADE_EMPTY(b)) {
+              if (!ctx->b) {
+                  ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
+              }
+              APR_BRIGADE_CONCAT(ctx->b, b);
+          }
+          /* Register a cleanup to reset the ctx->state to HIF_READ_HEADER_FIELDS upon
+           * destruction of the request pool
+           */
+          apr_pool_cleanup_register(r->pool, ctx, reset_state, apr_pool_cleanup_null);
+
+          r->status = HTTP_OK;
+          ap_update_vhost_from_headers(r);
+
+          /* we may have switched to another server */
+          r->per_dir_config = r->server->lookup_defaults;
+
+          rv = check_header_fields(r);
+          if (rv != APR_SUCCESS) {
+              return rv;
+          }
+
+          /* What we do next depends on whether the request includes a 
+           * body and if so, whether the body is transfer encoded or not
+           */
+          ctx->tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding");
+          ctx->lenp = apr_table_get(f->r->headers_in, "Content-Length");
+
+          /* If either tenc or lenp, then the request has a body */
+          if (ctx->lenp) {
+              char *endstr;
+              ctx->state = HIF_READ_BODY_LENGTH;
+
+              /* Perform one time checks on the content-length of the body */
+              errno = 0;
+              ctx->remaining = strtol(ctx->lenp, &endstr, 10); /* we depend on ANSI */
+
+              /* This protects us from over/underflow (the errno check),
+               * non-digit chars in the string (excluding leading space)
+               * (the endstr checks) and a negative number. Depending
+               * on the strtol implementation, the errno check may also
+               * trigger on an all whitespace string */
+              if (errno || (endstr && *endstr) || (ctx->remaining < 0)) {
+                  /* Conversion error */
+                  apr_bucket_brigade *bb;
+                  ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
+                                "Invalid Content-Length");
+                  bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+                  e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
+                                             f->r->pool, f->c->bucket_alloc);
+                  APR_BRIGADE_INSERT_TAIL(bb, e);
+                  e = apr_bucket_eos_create(f->c->bucket_alloc);
+                  APR_BRIGADE_INSERT_TAIL(bb, e);
+                  ctx->state = HIF_EOS_SENT;
+                  return ap_pass_brigade(f->r->output_filters, bb);
+              }
+              /* If we have a limit in effect and we know the C-L ahead of
+               * time, stop it here if it is invalid. 
+               */ 
+              if (ctx->limit && ctx->limit < ctx->remaining) {
+                  apr_bucket_brigade *bb;
+                  ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
+                                "Requested content-length of %" APR_OFF_T_FMT 
+                                " is larger than the configured limit"
+                                " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit);
+                  bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+                  e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
+                                             f->r->pool, f->c->bucket_alloc);
+                  APR_BRIGADE_INSERT_TAIL(bb, e);
+                  e = apr_bucket_eos_create(f->c->bucket_alloc);
+                  APR_BRIGADE_INSERT_TAIL(bb, e);
+                  ctx->state = HIF_EOS_SENT;
+                  return ap_pass_brigade(f->r->output_filters, bb);
+              }
+          }
+          else if (ctx->tenc) {
+              ctx->state = HIF_READ_BODY_CHUNKED;
+          }
+          else {
+              ctx->state = HIF_BODY_NONE;
+              /* XXX: proxy resp is broken... */
+            
+              /* If we don't have a request entity indicated by the headers, EOS.
+               * (BODY_NONE is a valid intermediate state due to trailers,
+               *  but it isn't a valid starting state.)
+               *
+               * RFC 2616 Section 4.4 note 5 states that connection-close
+               * is invalid for a request entity - request bodies must be
+               * denoted by C-L or T-E: chunked.
+               *
+               * Note that since the proxy uses this filter to handle the
+               * proxied *response*, proxy responses MUST be exempt.
+               */
+              if (f->r->proxyreq != PROXYREQ_RESPONSE) {
+                  e = apr_bucket_eos_create(f->c->bucket_alloc);
+                  APR_BRIGADE_INSERT_TAIL(b, e);
+                  ctx->state = HIF_EOS_SENT;
+                  return APR_SUCCESS;
+              }
+              return APR_SUCCESS;
+          }
+
+          /* We have a request body to handle */
+
+          /* LimitRequestBody does not apply to proxied responses.
+           * Consider implementing this check in its own filter. 
+           * Would adding a directive to limit the size of proxied 
+           * responses be useful?
+           */
+          if (!f->r->proxyreq) {
+              ctx->limit = ap_get_limit_req_body(f->r);
+          }
+          else {
+              ctx->limit = 0;
+          }
+          /* Since we're about to read data, send 100-Continue if needed.
+           * Only valid on chunked and C-L bodies where the C-L is > 0. 
+           */
+          if ((ctx->state == HIF_READ_BODY_CHUNKED ||
+               (ctx->state == HIF_READ_BODY_LENGTH && ctx->remaining > 0)) &&
+              f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1)) {
+              char *tmp;
+              apr_bucket_brigade *bb;
+
+              tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ",
+                                status_lines[0], CRLF CRLF, NULL);
+              bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+              e = apr_bucket_pool_create(tmp, strlen(tmp), f->r->pool,
+                                         f->c->bucket_alloc);
+              APR_BRIGADE_INSERT_HEAD(bb, e);
+              e = apr_bucket_flush_create(f->c->bucket_alloc);
+              APR_BRIGADE_INSERT_TAIL(bb, e);
+
+              ap_pass_brigade(f->c->output_filters, bb);
+          }
+
+          return APR_SUCCESS;
+          break;
+      }
+    case HIF_READ_BODY_LENGTH:  
+      {
+          if (!ctx->remaining) {
+              e = apr_bucket_eos_create(f->c->bucket_alloc);
+              APR_BRIGADE_INSERT_TAIL(b, e);
+              ctx->state = HIF_EOS_SENT;
+              return APR_SUCCESS;
+          }
+          break;
+      }
+    case HIF_READ_BODY_CHUNKED:
+      {
+          if (!ctx->remaining) {
+              char *line;
+              apr_size_t len = 0;
+              do {
+                  /* Read the CRLF after the chunk */
+                  rv = ap_brigade_getline(f->next, b, APR_BLOCK_READ, 30, &line, &len,
+                                          r->pool);
+              } while (rv == APR_SUCCESS && !len);
+              if (rv == APR_SUCCESS && len != 0) {
+                  ctx->remaining = get_chunk_size(line);
+              }
+              /* Detect chunksize error (such as overflow) 
+               * XXX: should we use this brigade to send a response?
+               * It should be emptied at least...
+               */
+              if (rv != APR_SUCCESS || ctx->remaining < 0) {
+                  /* Reset it in case we have to come back here later */
+                  ctx->remaining = 0;
+                  e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
+                                             f->r->pool,
+                                             f->c->bucket_alloc);
+                  APR_BRIGADE_INSERT_TAIL(b, e);
+                  e = apr_bucket_eos_create(f->c->bucket_alloc);
+                  APR_BRIGADE_INSERT_TAIL(b, e);
+                  ctx->state = HIF_EOS_SENT;
+                  return ap_pass_brigade(f->r->output_filters, b);
+              }
+              if (!ctx->remaining) {
+                  /* Handle trailers by calling ap_get_mime_headers again! */
+                  ctx->state = HIF_BODY_NONE;
+                  /* XXX: Humm, how to read the trailiers? */
+/* XXX BROKEN                   ap_get_mime_headers(f->r); */
+                  e = apr_bucket_eos_create(f->c->bucket_alloc);
+                  APR_BRIGADE_INSERT_TAIL(b, e);
+                  ctx->state = HIF_EOS_SENT;
+                  return APR_SUCCESS;
+              }
+          }
+          break;
+      }
+    case HIF_EOS_SENT:
+      {
+          e = apr_bucket_eos_create(f->c->bucket_alloc);
+          APR_BRIGADE_INSERT_TAIL(b, e);
+          return APR_SUCCESS;
+          break;
+      }
+    default:
+      {
+      }
+    }
+    /* Ensure that the caller can not go over our boundary point. */
+    if (ctx->remaining < readbytes) {
+        readbytes = ctx->remaining;
+    }
+    AP_DEBUG_ASSERT(readbytes > 0);        
+    if (APR_BRIGADE_EMPTY(b)) {
+        rv = ap_get_brigade(f->next, b, mode, APR_BLOCK_READ, readbytes);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+    }
 
     /* How many bytes did we just read? */
     apr_brigade_length(b, 0, &totalread);
@@ -1038,16 +1448,17 @@
      * it means our assumptions have changed. */
     AP_DEBUG_ASSERT(totalread >= 0);
 
-    if (ctx->state != BODY_NONE) {
+    if (ctx->state != HIF_BODY_NONE) {
         ctx->remaining -= totalread;
     }
 
     /* If we have no more bytes remaining on a C-L request, 
      * save the callter a roundtrip to discover EOS.
      */
-    if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
+    if (ctx->state == HIF_READ_BODY_LENGTH && ctx->remaining == 0) {
         e = apr_bucket_eos_create(f->c->bucket_alloc);
         APR_BRIGADE_INSERT_TAIL(b, e);
+        ctx->state = HIF_EOS_SENT;
     }
 
     /* We have a limit in effect. */
@@ -1069,7 +1480,7 @@
             APR_BRIGADE_INSERT_TAIL(bb, e);
             e = apr_bucket_eos_create(f->c->bucket_alloc);
             APR_BRIGADE_INSERT_TAIL(bb, e);
-            ctx->eos_sent = 1;
+            ctx->state = HIF_EOS_SENT;
             return ap_pass_brigade(f->r->output_filters, bb);
         }
     }
@@ -1542,6 +1953,7 @@
         ap_add_output_filters_by_type(r);
     }
 }
+
 
 typedef struct header_filter_ctx {
     int headers_sent;
Index: modules/http/mod_core.h
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/http/mod_core.h,v
retrieving revision 1.22
diff -u -r1.22 mod_core.h
--- modules/http/mod_core.h     3 Feb 2003 17:53:04 -0000       1.22
+++ modules/http/mod_core.h     3 Mar 2003 19:58:18 -0000
@@ -75,7 +75,7 @@
 extern AP_DECLARE_DATA ap_filter_rec_t *ap_http_header_filter_handle;
 extern AP_DECLARE_DATA ap_filter_rec_t *ap_chunk_filter_handle;
 extern AP_DECLARE_DATA ap_filter_rec_t *ap_byterange_filter_handle;
-
+extern AP_DECLARE_DATA ap_filter_rec_t *ap_http_header_input_filter_handle;
 /*
  * These (input) filters are internal to the mod_core operation.
  */
Index: server/protocol.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/protocol.c,v
retrieving revision 1.127
diff -u -r1.127 protocol.c
--- server/protocol.c   3 Feb 2003 17:53:19 -0000       1.127
+++ server/protocol.c   3 Mar 2003 19:58:18 -0000
@@ -245,7 +245,7 @@
                                           apr_size_t *read, request_rec *r,
                                           int fold, apr_bucket_brigade *bb)
 {
-    apr_status_t rv;
+    apr_status_t rv = APR_SUCCESS;
     apr_bucket *e;
     apr_size_t bytes_handled = 0, current_alloc = 0;
     char *pos, *last_char = *s;
@@ -742,7 +742,7 @@
 
     return 1;
 }
-
+#if 0
 AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb)
 {
     char *last_field = NULL;
@@ -880,13 +880,14 @@
     apr_brigade_destroy(tmp_bb);
 }
 
+#endif
 request_rec *ap_read_request(conn_rec *conn)
 {
+    apr_status_t rv;
     request_rec *r;
     apr_pool_t *p;
-    const char *expect;
     int access_status;
-    apr_bucket_brigade *tmp_bb;
+    apr_bucket_brigade *bb;
 
     apr_pool_create(&p, conn->pool);
     r = apr_pcalloc(p, sizeof(request_rec));
@@ -923,117 +924,16 @@
     r->status          = HTTP_REQUEST_TIME_OUT;  /* Until we get a request */
     r->the_request     = NULL;
 
-    tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
-
-    /* Get the request... */
-    if (!read_request_line(r, tmp_bb)) {
-        if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                          "request failed: URI too long");
-            ap_send_error_response(r, 0);
-            ap_run_log_transaction(r);
-            apr_brigade_destroy(tmp_bb);
-            return r;
-        }
-
-        apr_brigade_destroy(tmp_bb);
-        return NULL;
-    }
-
-    if (!r->assbackwards) {
-        ap_get_mime_headers_core(r, tmp_bb);
-        if (r->status != HTTP_REQUEST_TIME_OUT) {
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                          "request failed: error reading the headers");
-            ap_send_error_response(r, 0);
-            ap_run_log_transaction(r);
-            apr_brigade_destroy(tmp_bb);
-            return r;
-        }
-    }
-    else {
-        if (r->header_only) {
-            /*
-             * Client asked for headers only with HTTP/0.9, which doesn't send
-             * headers! Have to dink things just to make sure the error message
-             * comes through...
-             */
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                          "client sent invalid HTTP/0.9 request: HEAD %s",
-                          r->uri);
-            r->header_only = 0;
-            r->status = HTTP_BAD_REQUEST;
-            ap_send_error_response(r, 0);
-            ap_run_log_transaction(r);
-            apr_brigade_destroy(tmp_bb);
-            return r;
-        }
-    }
-
-    apr_brigade_destroy(tmp_bb);
-
-    r->status = HTTP_OK;                         /* Until further notice. */
-
-    /* update what we think the virtual host is based on the headers we've
-     * now read. may update status.
-     */
-    ap_update_vhost_from_headers(r);
-
-    /* we may have switched to another server */
-    r->per_dir_config = r->server->lookup_defaults;
-
-    if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1)))
-        || ((r->proto_num == HTTP_VERSION(1, 1))
-            && !apr_table_get(r->headers_in, "Host"))) {
-        /*
-         * Client sent us an HTTP/1.1 or later request without telling us the
-         * hostname, either with a full URL or a Host: header. We therefore
-         * need to (as per the 1.1 spec) send an error.  As a special case,
-         * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
-         * a Host: header, and the server MUST respond with 400 if it doesn't.
-         */
-        r->status = HTTP_BAD_REQUEST;
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                      "client sent HTTP/1.1 request without hostname "
-                      "(see RFC2616 section 14.23): %s", r->uri);
-    }
-
-    if (r->status != HTTP_OK) {
-        ap_send_error_response(r, 0);
-        ap_run_log_transaction(r);
-        return r;
-    }
-
+    r->connection->r = r;
+    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
+                        APR_BLOCK_READ, HUGE_STRING_LEN);
+    apr_brigade_destroy(bb);
     if ((access_status = ap_run_post_read_request(r))) {
         ap_die(access_status, r);
         ap_run_log_transaction(r);
         return NULL;
     }
-
-    if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
-        && (expect[0] != '\0')) {
-        /*
-         * The Expect header field was added to HTTP/1.1 after RFC 2068
-         * as a means to signal when a 100 response is desired and,
-         * unfortunately, to signal a poor man's mandatory extension that
-         * the server must understand or return 417 Expectation Failed.
-         */
-        if (strcasecmp(expect, "100-continue") == 0) {
-            r->expecting_100 = 1;
-        }
-        else {
-            r->status = HTTP_EXPECTATION_FAILED;
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
-                          "client sent an unrecognized expectation value of "
-                          "Expect: %s", expect);
-            ap_send_error_response(r, 0);
-            ap_run_log_transaction(r);
-            return r;
-        }
-    }
-
-    ap_add_input_filter_handle(ap_http_input_filter_handle,
-                               NULL, r, r->connection);
 
     return r;
 }

Reply via email to