]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Begin to move functions from the http module to the core. The goal is to
authorRyan Bloom <rbb@apache.org>
Mon, 26 Feb 2001 04:38:22 +0000 (04:38 +0000)
committerRyan Bloom <rbb@apache.org>
Mon, 26 Feb 2001 04:38:22 +0000 (04:38 +0000)
have only functions that are HTTP specific in the http directory.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88341 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
CHANGES
build/rules.mk
include/http_protocol.h
libhttpd.dsp
modules/generators/mod_asis.c
modules/generators/mod_autoindex.c
modules/generators/mod_cgid.c
modules/http/http_protocol.c
modules/http/mod_core.h
modules/mappers/mod_imap.c
server/Makefile.in
server/protocol.c [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index b5ed3ef3f0b6446886722c0637be609bcc246d4e..459fa0edabb35aa3d3cbd9abfbdcb801bf51d9ac 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,9 @@
 Changes with Apache 2.0.14-dev
 
+  *) Begin to move protocol independant functions out of mod_http.  The goal
+     is to have only functions that are HTTP specific in the http directory.
+     [Ryan Bloom]
+
 Changes with Apache 2.0.13
 
   *) Don't assume that there will always be multiple calls to the byterange 
index 9a5c0f2aff959cca264118d88fb6055101e0eda2..de0c5f400eef0f8a1c63f9dff9a894a895594a71 100644 (file)
@@ -84,7 +84,7 @@ INSTALL = $(abs_srcdir)/build/install.sh -c
 INSTALL_DATA = $(INSTALL) -m 644
 INSTALL_PROGRAM = $(INSTALL) -m 755
 
-DEFS = -I. -I$(srcdir) -I$(top_srcdir)/server/mpm/$(MPM_NAME)
+DEFS = -I. -I$(srcdir) -I$(top_srcdir)/server/mpm/$(MPM_NAME) -I$(top_srcdir)/modules/http
 
 # Suffixes
 
index 11d328d849ecb17333af1ce858f60a15beda2358..accbd10913730fcd7eafd0dd0d5503e5c96563af 100644 (file)
@@ -64,6 +64,7 @@
 #include "apr_portable.h"
 #include "apr_mmap.h"
 #include "apr_buckets.h"
+#include "util_filter.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -85,25 +86,6 @@ extern "C" {
  */ 
 request_rec *ap_read_request(conn_rec *c);
 
-/**
- * Send the minimal part of an HTTP response header.
- * @param r The current request
- * @param bb The brigade to add the header to.
- * @warning Modules should be very careful about using this, and should 
- *          prefer ap_send_http_header().  Much of the HTTP/1.1 implementation 
- *          correctness depends on code in ap_send_http_header().
- * @deffunc void ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb)
- */
-AP_DECLARE(void) ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb);
-
-/**
- * Send the Status-Line and header fields for HTTP response
- * @param r The current request
- * @deffunc void ap_send_http_header(request_rec *r)
- */
-AP_DECLARE(void) ap_send_http_header(request_rec *r);
-
-
 /* Finish up stuff after a request */
 
 /**
@@ -159,6 +141,19 @@ AP_DECLARE(int) ap_set_keepalive(request_rec *r);
  */
 AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime);
 
+/**
+ * Build the content-type that should be sent to the client from the
+ * content-type specified.  The following rules are followed:
+ *    - if type is NULL, type is set to ap_default_type(r)
+ *    - if charset adding is disabled, stop processing and return type.
+ *    - then, if there are no parameters on type, add the default charset
+ *    - return type
+ * @param r The current request
+ * @return The content-type
+ * @deffunc const char *ap_make_content_type(request_rec *r, const char *type);
+ */ 
+AP_DECLARE(const char *) ap_make_content_type(request_rec *r, const char *type);
+
 /**
  * Construct an entity tag from the resource information.  If it's a real
  * file, build in some of the file characteristics.
@@ -436,8 +431,6 @@ AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz);
 AP_DECLARE(int) ap_discard_request_body(request_rec *r);
 
 
-/* Sending a byterange */
-
 /**
  * Setup the output headers so that the client knows how to authenticate
  * itself the next time, if an authentication request failed.  This function
@@ -596,6 +589,19 @@ AP_DECLARE(apr_bucket *) ap_bucket_error_make(apr_bucket *b, int error,
 AP_DECLARE(apr_bucket *) ap_bucket_error_create(int error,
                 const char *buf, apr_pool_t *p);
 
+AP_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, apr_bucket_brigade *b);
+AP_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_bucket_brigade *b);
+AP_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *,
+                                                              apr_bucket_brigade *);
+AP_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(ap_filter_t *f, apr_bucket_brigade *b);
+
+/*
+ * Setting up the protocol fields for subsidiary requests...
+ * Also, a wrapup function to keep the internal accounting straight.
+ */
+void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r);
+void ap_finalize_sub_req_protocol(request_rec *sub_r);
+                                                                                
 #ifdef __cplusplus
 }
 #endif
index db1d3bbc6c35ccd079cfab7034d99eece86eb7c9..92332ab5ac5912a1722e3ce454e2f5d5a1acc11c 100644 (file)
@@ -354,6 +354,10 @@ SOURCE=.\server\error_bucket.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\server\protocol.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\server\rfc1413.c
 # End Source File
 # Begin Source File
index 498e7df14775b097d011679a6962f58d96500305..aa3567a844a358decd3198899887abf1e49f09ed 100644 (file)
@@ -66,6 +66,8 @@
 #include "http_main.h"
 #include "http_request.h"
 
+#include "mod_core.h"
+
 #define ASIS_MAGIC_TYPE "httpd/send-as-is"
 
 static int asis_handler(request_rec *r)
index 712b019ee5c79aaaeac5675b4f4332c14b803130..36584efe1104274ea4f00d6a8daa8f37b81f7449 100644 (file)
@@ -85,6 +85,7 @@
 #include "http_main.h"
 #include "util_script.h"
 
+#include "mod_core.h"
 
 module AP_MODULE_DECLARE_DATA autoindex_module;
 
index a7e1618f8568090cae28259a8050b9f75e810d21..03be51eb97aebec84d73efee283caaa7cf5bc99b 100644 (file)
 #include "mod_suexec.h"
 #include "../filters/mod_include.h"
 
+#include "mod_core.h"
+
+
 /* ### should be tossed in favor of APR */
 #include <sys/stat.h>
 #include <sys/un.h> /* for sockaddr_un */
index fa7b86ce08a1c24c532bd81b51d412e59e2ba27c..4d06da3c9673f325f14d9b1a2514b1388c5a13c6 100644 (file)
 #endif
 
 
-APR_HOOK_STRUCT(
-           APR_HOOK_LINK(post_read_request)
-           APR_HOOK_LINK(log_transaction)
-           APR_HOOK_LINK(http_method)
-           APR_HOOK_LINK(default_port)
-)
-
-/*
- * Builds the content-type that should be sent to the client from the
- * content-type specified.  The following rules are followed:
- *    - if type is NULL, type is set to ap_default_type(r)
- *    - if charset adding is disabled, stop processing and return type.
- *    - then, if there are no parameters on type, add the default charset
- *    - return type
- */
-static const char *make_content_type(request_rec *r, const char *type)
-{
-    static const char *needcset[] = {
-       "text/plain",
-       "text/html",
-       NULL };
-    const char **pcset;
-    core_dir_config *conf =
-       (core_dir_config *)ap_get_module_config(r->per_dir_config,
-                                               &core_module);
-
-    if (!type) {
-       type = ap_default_type(r);
-    }
-    if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) {
-       return type;
-    }
-
-    if (ap_strcasestr(type, "charset=") != NULL) {
-       /* already has parameter, do nothing */
-       /* XXX we don't check the validity */
-       ;
-    }
-    else {
-       /* see if it makes sense to add the charset. At present,
-        * we only add it if the Content-type is one of needcset[]
-        */
-       for (pcset = needcset; *pcset ; pcset++) {
-           if (ap_strcasestr(type, *pcset) != NULL) {
-               type = apr_pstrcat(r->pool, type, "; charset=", 
-                                  conf->add_default_charset_name, NULL);
-               break;
-           }
-       }
-    }
-    return type;
-}
-
-static int parse_byterange(char *range, apr_off_t clength,
-                           apr_off_t *start, apr_off_t *end)
-{
-    char *dash = strchr(range, '-');
-
-    if (!dash)
-        return 0;
-
-    if ((dash == range)) {
-        /* In the form "-5" */
-        *start = clength - atol(dash + 1);
-        *end = clength - 1;
-    }
-    else {
-        *dash = '\0';
-        dash++;
-        *start = atol(range);
-        if (*dash)
-            *end = atol(dash);
-        else                    /* "5-" */
-            *end = clength - 1;
-    }
-
-    if (*start < 0)
-       *start = 0;
-
-    if (*end >= clength)
-        *end = clength - 1;
-
-    if (*start > *end)
-       return -1;
-
-    return (*start > 0 || *end < clength);
-}
-
-static int ap_set_byterange(request_rec *r);
-
-typedef struct byterange_ctx {
-    apr_bucket_brigade *bb;
-    int num_ranges;
-    const char *orig_ct;
-} byterange_ctx;
-
-/*
- * Here we try to be compatible with clients that want multipart/x-byteranges
- * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
- * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
- * that the browser supports an older protocol. We also check User-Agent
- * for Microsoft Internet Explorer 3, which needs this as well.
- */
-static int use_range_x(request_rec *r)
-{
-    const char *ua;
-    return (apr_table_get(r->headers_in, "Request-Range") ||
-            ((ua = apr_table_get(r->headers_in, "User-Agent"))
-             && ap_strstr_c(ua, "MSIE 3")));
-}
-
-#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(
-    ap_filter_t *f,
-    apr_bucket_brigade *bb)
-{
-#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
-    request_rec *r = f->r;
-    byterange_ctx *ctx = f->ctx;
-    apr_bucket *e;
-    apr_bucket_brigade *bsend;
-    apr_off_t range_start;
-    apr_off_t range_end;
-    char *current;
-    char *bound_head;
-    apr_ssize_t bb_length;
-    apr_off_t clength = 0;
-    apr_status_t rv;
-    int found = 0;
-
-    if (!ctx) {
-        int num_ranges = ap_set_byterange(r);
-        if (num_ranges == -1) {
-            ap_remove_output_filter(f);
-            bsend = apr_brigade_create(r->pool);
-            e = ap_bucket_error_create(HTTP_RANGE_NOT_SATISFIABLE, NULL, r->pool);
-            APR_BRIGADE_INSERT_TAIL(bsend, e);
-            e = apr_bucket_eos_create();
-            APR_BRIGADE_INSERT_TAIL(bsend, e);
-            return ap_pass_brigade(f->next, bsend);
-        }
-        if (num_ranges == 0) {
-            ap_remove_output_filter(f);
-            return ap_pass_brigade(f->next, bb);
-        }
-
-        ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
-        ctx->num_ranges = num_ranges;
-
-        if (num_ranges > 1) {
-            ctx->orig_ct = r->content_type;
-            r->content_type = 
-                 apr_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
-                          "byteranges; boundary=", r->boundary, NULL);
-        }
-
-        /* create a brigade in case we never call ap_save_brigade() */
-        ctx->bb = apr_brigade_create(r->pool);
-    }
-
-    /* We can't actually deal with byte-ranges until we have the whole brigade
-     * because the byte-ranges can be in any order, and according to the RFC,
-     * we SHOULD return the data in the same order it was requested. 
-     */
-    if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
-        ap_save_brigade(f, &ctx->bb, &bb);
-        return APR_SUCCESS;
-    }
-
-    /* compute this once (it is an invariant) */
-    bound_head = apr_pstrcat(r->pool,
-                             CRLF "--", r->boundary,
-                             CRLF "Content-type: ",
-                             make_content_type(r, ctx->orig_ct),
-                             CRLF "Content-range: bytes ", 
-                             NULL);
-    ap_xlate_proto_to_ascii(bound_head, strlen(bound_head));
-
-    /* If we have a saved brigade from a previous run, concat the passed
-     * brigade with our saved brigade.  Otherwise just continue.  
-     */
-    if (ctx->bb) {
-        APR_BRIGADE_CONCAT(ctx->bb, bb);
-        bb = ctx->bb;
-        ctx->bb = NULL;     /* ### strictly necessary? call brigade_destroy? */
-    }
-
-    /* It is possible that we won't have a content length yet, so we have to
-     * compute the length before we can actually do the byterange work.
-     */
-    (void) apr_brigade_length(bb, 1, &bb_length);
-    clength = (apr_off_t)bb_length;
-
-    /* this brigade holds what we will be sending */
-    bsend = apr_brigade_create(r->pool);
-
-    while ((current = ap_getword(r->pool, &r->range, ',')) &&
-           (rv = parse_byterange(current, clength, &range_start, &range_end))) {
-        apr_bucket *e2;
-        apr_bucket *ec;
-
-        if (rv == -1) {
-            continue;
-        }        
-        else {
-            found = 1;
-        }
-
-        if (ctx->num_ranges > 1) {
-            char *ts;
-
-            e = apr_bucket_pool_create(bound_head,
-                                      strlen(bound_head), r->pool);
-            APR_BRIGADE_INSERT_TAIL(bsend, e);
-
-            ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
-                              range_start, range_end, clength);
-            ap_xlate_proto_to_ascii(ts, strlen(ts));
-            e = apr_bucket_pool_create(ts, strlen(ts), r->pool);
-            APR_BRIGADE_INSERT_TAIL(bsend, e);
-        }
-        
-        e = apr_brigade_partition(bb, range_start);
-        e2 = apr_brigade_partition(bb, range_end + 1);
-        
-        ec = e;
-        do {
-            apr_bucket *foo;
-            const char *str;
-            apr_size_t len;
-
-            if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
-                apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
-                foo = apr_bucket_heap_create(str, len, 0, NULL);
-            }
-            APR_BRIGADE_INSERT_TAIL(bsend, foo);
-            ec = APR_BUCKET_NEXT(ec);
-        } while (ec != e2);
-    }
-
-    if (found == 0) {
-        ap_remove_output_filter(f);
-        r->status = HTTP_OK;
-        return HTTP_RANGE_NOT_SATISFIABLE;
-    }
-
-    if (ctx->num_ranges > 1) {
-        char *end;
-
-        /* add the final boundary */
-        end = apr_pstrcat(r->pool, CRLF "--", r->boundary, "--" CRLF, NULL);
-        ap_xlate_proto_to_ascii(end, strlen(end));
-        e = apr_bucket_pool_create(end, strlen(end), r->pool);
-        APR_BRIGADE_INSERT_TAIL(bsend, e);
-    }
-
-    e = apr_bucket_eos_create();
-    APR_BRIGADE_INSERT_TAIL(bsend, e);
-
-    /* we're done with the original content */
-    apr_brigade_destroy(bb);
-
-    /* send our multipart output */
-    return ap_pass_brigade(f->next, bsend); 
-}    
-
-AP_DECLARE(void) ap_set_content_length(request_rec *r, apr_off_t clength)
-{
-    r->clength = clength;
-    apr_table_setn(r->headers_out, "Content-Length",
-                   apr_psprintf(r->pool, "%" APR_OFF_T_FMT, clength));
-}
-
 AP_DECLARE(int) ap_set_keepalive(request_rec *r)
 {
     int ka_sent = 0;
@@ -466,30 +191,6 @@ AP_DECLARE(int) ap_set_keepalive(request_rec *r)
     return 0;
 }
 
-/*
- * Return the latest rational time from a request/mtime (modification time)
- * pair.  We return the mtime unless it's in the future, in which case we
- * return the current time.  We use the request time as a reference in order
- * to limit the number of calls to time().  We don't check for futurosity
- * unless the mtime is at least as new as the reference.
- */
-AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
-{
-    apr_time_t now;
-
-    /* For all static responses, it's almost certain that the file was
-     * last modified before the beginning of the request.  So there's
-     * no reason to call time(NULL) again.  But if the response has been
-     * created on demand, then it might be newer than the time the request
-     * started.  In this event we really have to call time(NULL) again
-     * so that we can give the clients the most accurate Last-Modified.  If we
-     * were given a time in the future, we return the current time - the
-     * Last-Modified can't be in the future.
-     */
-    now = (mtime < r->request_time) ? r->request_time : apr_time_now();
-    return (mtime > now) ? now : mtime;
-}
-
 AP_DECLARE(int) ap_meets_conditions(request_rec *r)
 {
     const char *etag = apr_table_get(r->headers_out, "ETag");
@@ -598,93 +299,6 @@ AP_DECLARE(int) ap_meets_conditions(request_rec *r)
     return OK;
 }
 
-/*
- * Construct an entity tag (ETag) from resource information.  If it's a real
- * file, build in some of the file characteristics.  If the modification time
- * is newer than (request-time minus 1 second), mark the ETag as weak - it
- * could be modified again in as short an interval.  We rationalize the
- * modification time we're given to keep it from being in the future.
- */
-AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
-{
-    char *etag;
-    char *weak;
-
-    /*
-     * Make an ETag header out of various pieces of information. We use
-     * the last-modified date and, if we have a real file, the
-     * length and inode number - note that this doesn't have to match
-     * the content-length (i.e. includes), it just has to be unique
-     * for the file.
-     *
-     * If the request was made within a second of the last-modified date,
-     * we send a weak tag instead of a strong one, since it could
-     * be modified again later in the second, and the validation
-     * would be incorrect.
-     */
-    
-    weak = ((r->request_time - r->mtime > APR_USEC_PER_SEC)
-           && !force_weak) ? "" : "W/";
-
-    if (r->finfo.filetype != 0) {
-        etag = apr_psprintf(r->pool,
-                           "%s\"%lx-%lx-%lx\"", weak,
-                           (unsigned long) r->finfo.inode,
-                           (unsigned long) r->finfo.size,
-                           (unsigned long) r->mtime);
-    }
-    else {
-        etag = apr_psprintf(r->pool, "%s\"%lx\"", weak,
-                           (unsigned long) r->mtime);
-    }
-
-    return etag;
-}
-
-AP_DECLARE(void) ap_set_etag(request_rec *r)
-{
-    char *etag;
-    char *variant_etag, *vlv;
-    int vlv_weak;
-
-    if (!r->vlist_validator) {
-        etag = ap_make_etag(r, 0);
-    }
-    else {
-        /* If we have a variant list validator (vlv) due to the
-         * response being negotiated, then we create a structured
-         * entity tag which merges the variant etag with the variant
-         * list validator (vlv).  This merging makes revalidation
-         * somewhat safer, ensures that caches which can deal with
-         * Vary will (eventually) be updated if the set of variants is
-         * changed, and is also a protocol requirement for transparent
-         * content negotiation.
-         */
-
-        /* if the variant list validator is weak, we make the whole
-         * structured etag weak.  If we would not, then clients could
-         * have problems merging range responses if we have different
-         * variants with the same non-globally-unique strong etag.
-         */
-
-        vlv = r->vlist_validator;
-        vlv_weak = (vlv[0] == 'W');
-               
-        variant_etag = ap_make_etag(r, vlv_weak);
-
-        /* merge variant_etag and vlv into a structured etag */
-
-        variant_etag[strlen(variant_etag) - 1] = '\0';
-        if (vlv_weak)
-            vlv += 3;
-        else
-            vlv++;
-        etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
-    }
-
-    apr_table_setn(r->headers_out, "ETag", etag);
-}
-
 /*
  * This function sets the Last-Modified output header field to the value
  * of the mtime field in the request structure - rationalized to keep it from
@@ -904,10 +518,6 @@ typedef struct http_filter_ctx {
 
 apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode)
 {
-#define ASCII_BLANK  '\040'
-#define ASCII_CR     '\015'
-#define ASCII_LF     '\012'
-#define ASCII_TAB    '\011' 
     apr_bucket *e;
     char *buff;
     apr_size_t len;
@@ -942,707 +552,80 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode
                 e = APR_BRIGADE_FIRST(ctx->b);
             }
             if (!e || apr_bucket_read(e, &str, &length, APR_NONBLOCK_READ) != APR_SUCCESS) {
-                return APR_EOF;
-            }
-            else {
-                const char *c = str;
-                while (c - str < length) {
-                    if (*c == ASCII_LF)
-                        c++;
-                    else if (*c == ASCII_CR && *(c + 1) == ASCII_LF)
-                        c += 2;
-                    else return APR_SUCCESS;
-                }
-                apr_bucket_delete(e);
-            }
-        }
-    }
-
-    if (APR_BRIGADE_EMPTY(ctx->b)) {
-        if ((rv = ap_get_brigade(f->next, ctx->b, mode)) != APR_SUCCESS) {
-            return rv;
-        }
-    }
-
-    if (f->c->remain) {
-        while (!APR_BRIGADE_EMPTY(ctx->b)) {
-            const char *ignore;
-
-            e = APR_BRIGADE_FIRST(ctx->b);
-            if ((rv = apr_bucket_read(e, &ignore, &len, mode)) != APR_SUCCESS) {
-                /* probably APR_IS_EAGAIN(rv); socket state isn't correct;
-                 * remove log once we get this squared away */
-                ap_log_error(APLOG_MARK, APLOG_ERR, rv, f->c->base_server, 
-                             "apr_bucket_read");
-                return rv;
-            }
-
-            if (len) {
-                if (f->c->remain < len) {
-                    apr_bucket_split(e, f->c->remain);
-                    f->c->remain = 0;
-                }
-                else {
-                    f->c->remain -= len;
-                }
-                APR_BUCKET_REMOVE(e);
-                APR_BRIGADE_INSERT_TAIL(b, e);
-                break; /* once we've gotten some data, deliver it to caller */
-            }
-            apr_bucket_delete(e);
-        }
-        if (f->c->remain == 0) {
-            apr_bucket *eos = apr_bucket_eos_create();
-                
-            APR_BRIGADE_INSERT_TAIL(b, eos);
-        }
-        return APR_SUCCESS;
-    }
-
-    while (!APR_BRIGADE_EMPTY(ctx->b)) {
-        e = APR_BRIGADE_FIRST(ctx->b);
-        if ((rv = apr_bucket_read(e, (const char **)&buff, &len, mode)) != APR_SUCCESS) {
-            return rv;
-        }
-
-        pos = memchr(buff, ASCII_LF, len);
-        if (pos != NULL) {
-            apr_bucket_split(e, pos - buff + 1);
-            APR_BUCKET_REMOVE(e);
-            APR_BRIGADE_INSERT_TAIL(b, e);
-            return APR_SUCCESS;
-        }
-        APR_BUCKET_REMOVE(e);
-        APR_BRIGADE_INSERT_TAIL(b, e);
-    }
-    return APR_SUCCESS;
-}
-
-/* Get a line of protocol input, including any continuation lines
- * caused by MIME folding (or broken clients) if fold != 0, and place it
- * in the buffer s, of size n bytes, without the ending newline.
- *
- * Returns -1 on error, or the length of s.  
- *
- * Notes: Because the buffer uses 1 char for NUL, the most we can return is 
- *        (n - 1) actual characters.  
- *
- *        If no LF is detected on the last line due to a dropped connection 
- *        or a full buffer, that's considered an error.
- */
-AP_CORE_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold)
-{
-    char *pos = s;
-    char *last_char;
-    char *beyond_buff = s + n;
-    const char *temp;
-    int retval;
-    int total = 0;
-    int looking_ahead = 0;
-    apr_size_t length;
-    conn_rec *c = r->connection;
-    core_request_config *req_cfg;
-    apr_bucket_brigade *b;
-    apr_bucket *e;
-
-    req_cfg = (core_request_config *)
-                ap_get_module_config(r->request_config, &core_module);
-    b = req_cfg->bb;
-    /* make sure it's empty unless we're folding */ 
-    AP_DEBUG_ASSERT(fold || APR_BRIGADE_EMPTY(b));
-
-    while (1) {
-        if (APR_BRIGADE_EMPTY(b)) {
-            if (ap_get_brigade(c->input_filters, b, AP_MODE_BLOCKING) != APR_SUCCESS ||
-                APR_BRIGADE_EMPTY(b)) {
-                return -1;
-            }
-        }
-        e = APR_BRIGADE_FIRST(b); 
-        if (e->length == 0) {
-            apr_bucket_delete(e);
-            continue;
-        }
-        retval = apr_bucket_read(e, &temp, &length, APR_BLOCK_READ);
-
-        if (retval != APR_SUCCESS) {
-            total = ((length < 0) && (total == 0)) ? -1 : total;
-            break;
-        }
-
-        if ((looking_ahead) && (*temp != ASCII_BLANK) && (*temp != ASCII_TAB)) { 
-            /* can't fold because next line isn't indented, 
-             * so return what we have.  lookahead brigade is 
-             * stashed on req_cfg->bb
-             */
-            AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(req_cfg->bb));
-            break;
-        }
-        last_char = pos + length - 1;
-        if (last_char < beyond_buff) {
-            memcpy(pos, temp, length);
-            apr_bucket_delete(e);
-        }
-        else {
-            /* input line was larger than the caller's buffer */
-            apr_brigade_destroy(b); 
-            
-            /* don't need to worry about req_cfg->bb being bogus.
-             * the request is about to die, and ErrorDocument
-             * redirects get a new req_cfg->bb
-             */
-            
-            return -1;
-        }
-        
-        pos = last_char;        /* Point at the last character           */
-
-        if (*pos == ASCII_LF) { /* Did we get a full line of input?      */
-                
-            if (pos > s && *(pos - 1) == ASCII_CR) {
-                --pos;          /* zap optional CR before LF             */
-            }
-                
-            /*
-             * Trim 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, and
-             * saves memory as well.  Terminate string at end of line.
-             */
-            while (pos > (s + 1) && 
-                   (*(pos - 1) == ASCII_BLANK || *(pos - 1) == ASCII_TAB)) {
-                --pos;          /* trim extra trailing spaces or tabs    */
-            }
-            *pos = '\0';        /* zap end of string                     */
-            total = pos - s;    /* update total string length            */
-
-            /* look ahead another line if line folding is desired 
-             * and this line isn't empty
-             */
-            if (fold && total) {
-                looking_ahead = 1;
-            }
-            else {
-                AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb));
-                break;
-            }
-        }
-        else {
-            /* no LF yet...character mode client (telnet)...keep going
-             * bump past last character read,   
-             * and set total in case we bail before finding a LF   
-             */
-            total = ++pos - s;    
-            looking_ahead = 0;  /* only appropriate right after LF       */ 
-        }
-    }
-    ap_xlate_proto_from_ascii(s, total);
-    return total;
-}
-
-/* parse_uri: break apart the uri
- * Side Effects:
- * - sets r->args to rest after '?' (or NULL if no '?')
- * - sets r->uri to request uri (without r->args part)
- * - sets r->hostname (if not set already) from request (scheme://host:port)
- */
-AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri)
-{
-    int status = HTTP_OK;
-
-    r->unparsed_uri = apr_pstrdup(r->pool, uri);
-
-    if (r->method_number == M_CONNECT) {
-       status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri);
-    }
-    else {
-       /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */
-       status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri);
-    }
-
-    if (ap_is_HTTP_SUCCESS(status)) {
-       /* if it has a scheme we may need to do absoluteURI vhost stuff */
-       if (r->parsed_uri.scheme
-           && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) {
-           r->hostname = r->parsed_uri.hostname;
-       }
-       else if (r->method_number == M_CONNECT) {
-           r->hostname = r->parsed_uri.hostname;
-       }
-       r->args = r->parsed_uri.query;
-       r->uri = r->parsed_uri.path ? r->parsed_uri.path
-                                   : apr_pstrdup(r->pool, "/");
-#if defined(OS2) || defined(WIN32)
-       /* Handle path translations for OS/2 and plug security hole.
-        * This will prevent "http://www.wherever.com/..\..\/" from
-        * returning a directory for the root drive.
-        */
-       {
-           char *x;
-
-           for (x = r->uri; (x = strchr(x, '\\')) != NULL; )
-               *x = '/';
-       }
-#endif  /* OS2 || WIN32 */
-    }
-    else {
-       r->args = NULL;
-       r->hostname = NULL;
-       r->status = status;             /* set error status */
-       r->uri = apr_pstrdup(r->pool, uri);
-    }
-}
-
-static int read_request_line(request_rec *r)
-{
-    char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */
-    const char *ll = l;
-    const char *uri;
-#if 0
-    conn_rec *conn = r->connection;
-#endif
-    int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
-    int len;
-
-    /* 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.  But note that we may not
-     * have flushed any previous response completely to the client yet.
-     * We delay the flush as long as possible so that we can improve
-     * performance for clients that are pipelining requests.  If a request
-     * is pipelined then we won't block during the (implicit) read() below.
-     * If the requests aren't pipelined, then the client is still waiting
-     * for the final buffer flush from us, and we will block in the implicit
-     * read().  B_SAFEREAD ensures that the BUFF layer flushes if it will
-     * have to block during a read.
-     */
-
-    while ((len = ap_getline(l, sizeof(l), r, 0)) <= 0) {
-        if (len < 0) {             /* includes EOF */
-           /* this is a hack to make sure that request time is set,
-            * it's not perfect, but it's better than nothing 
-            */
-           r->request_time = apr_time_now();
-            return 0;
-        }
-    }
-    /* we've probably got something to do, ignore graceful restart requests */
-
-    /* XXX - sigwait doesn't work if the signal has been SIG_IGNed (under
-     * linux 2.0 w/ glibc 2.0, anyway), and this step isn't necessary when
-     * we're running a sigwait thread anyway. If/when unthreaded mode is
-     * put back in, we should make sure to ignore this signal iff a sigwait
-     * thread isn't used. - mvsk
-
-#ifdef SIGWINCH
-    apr_signal(SIGWINCH, SIG_IGN);
-#endif
-    */
-
-    r->request_time = apr_time_now();
-    r->the_request = apr_pstrdup(r->pool, l);
-    r->method = ap_getword_white(r->pool, &ll);
-
-#if 0
-/* XXX If we want to keep track of the Method, the protocol module should do
- * it.  That support isn't in the scoreboard yet.  Hopefully next week 
- * sometime.   rbb */
-    ap_update_connection_status(AP_CHILD_THREAD_FROM_ID(conn->id), "Method", r->method); 
-#endif
-    uri = ap_getword_white(r->pool, &ll);
-
-    /* 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;
-    }
-
-    ap_parse_uri(r, uri);
-
-    /* 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.
-     */
-    if (len > 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;
-    }
-
-    r->assbackwards = (ll[0] == '\0');
-    r->protocol = apr_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9");
-/* XXX If we want to keep track of the Method, the protocol module should do
- * it.  That support isn't in the scoreboard yet.  Hopefully next week 
- * sometime.   rbb
-    ap_update_connection_status(conn->id, "Protocol", r->protocol); 
- */
-
-    if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor)
-      && 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);
-
-    return 1;
-}
-
-static void get_mime_headers(request_rec *r)
-{
-    char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */
-    char *value;
-    char *copy;
-    int len;
-    int fields_read = 0;
-    apr_table_t *tmp_headers;
-
-    /* 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 ((len = ap_getline(field, sizeof(field), r, 1)) > 0) {
-
-        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.<P>\n");
-            return;
-        }
-        /* 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 field size.
-         */
-        if (len > 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.<P>\n"
-                                      "<PRE>\n",
-                                      ap_escape_html(r->pool, field),
-                                      "</PRE>\n", NULL));
-            return;
-        }
-        copy = apr_palloc(r->pool, len + 1);
-        memcpy(copy, field, len + 1);
-
-        if (!(value = strchr(copy, ':'))) {     /* Find the colon separator */
-            r->status = HTTP_BAD_REQUEST;       /* or abort the bad request */
-            apr_table_setn(r->notes, "error-notes",
-                          apr_pstrcat(r->pool,
-                                      "Request header field is missing "
-                                      "colon separator.<P>\n"
-                                      "<PRE>\n",
-                                      ap_escape_html(r->pool, copy),
-                                      "</PRE>\n", NULL));
-            return;
-        }
-
-        *value = '\0';
-        ++value;
-        while (*value == ' ' || *value == '\t') {
-            ++value;            /* Skip to start of value   */
-       }
-
-       apr_table_addn(tmp_headers, copy, value);
-    }
-
-    apr_table_overlap(r->headers_in, tmp_headers, APR_OVERLAP_TABLES_MERGE);
-}
-
-request_rec *ap_read_request(conn_rec *conn)
-{
-    request_rec *r;
-    apr_pool_t *p;
-    const char *expect;
-    int access_status;
-    core_request_config *req_cfg;
-
-    apr_pool_create(&p, conn->pool);
-    r = apr_pcalloc(p, sizeof(request_rec));
-    r->pool            = p;
-    r->connection      = conn;
-    r->server          = conn->base_server;
-
-    conn->keptalive    = conn->keepalive == 1;
-    conn->keepalive    = 0;
-
-    r->user            = NULL;
-    r->ap_auth_type    = NULL;
-
-    r->allowed_methods = ap_make_method_list(p, 2);
-
-    r->headers_in      = apr_table_make(r->pool, 50);
-    r->subprocess_env  = apr_table_make(r->pool, 50);
-    r->headers_out     = apr_table_make(r->pool, 12);
-    r->err_headers_out = apr_table_make(r->pool, 5);
-    r->notes           = apr_table_make(r->pool, 5);
-
-    r->request_config  = ap_create_request_config(r->pool);
-    req_cfg = apr_pcalloc(r->pool, sizeof(core_request_config));
-    req_cfg->bb = apr_brigade_create(r->pool);
-    ap_set_module_config(r->request_config, &core_module, req_cfg);
-                    
-    r->per_dir_config  = r->server->lookup_defaults;
-
-    r->sent_bodyct     = 0;                      /* bytect isn't for body */
-
-    r->read_length     = 0;
-    r->read_body       = REQUEST_NO_BODY;
-
-    r->status          = HTTP_REQUEST_TIME_OUT;  /* Until we get a request */
-    r->the_request     = NULL;
-    r->output_filters  = conn->output_filters;
-    r->input_filters   = conn->input_filters;
-
-    apr_setsocketopt(conn->client_socket, APR_SO_TIMEOUT, 
-                     (int)(conn->keptalive
-                     ? r->server->keep_alive_timeout * APR_USEC_PER_SEC
-                     : r->server->timeout * APR_USEC_PER_SEC));
-                     
-    ap_add_output_filter("BYTERANGE", NULL, r, r->connection);
-    ap_add_output_filter("CONTENT_LENGTH", NULL, r, r->connection);
-    ap_add_output_filter("HTTP_HEADER", NULL, r, r->connection);
-
-    /* Get the request... */
-    if (!read_request_line(r)) {
-        if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
-            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                         "request failed: URI too long");
-            ap_send_error_response(r, 0);
-            ap_run_log_transaction(r);
-            return r;
-        }
-        return NULL;
-    }
-    if (r->connection->keptalive) {
-        apr_setsocketopt(r->connection->client_socket, APR_SO_TIMEOUT,
-                         (int)(r->server->timeout * APR_USEC_PER_SEC));
-    }
-    if (!r->assbackwards) {
-        get_mime_headers(r);
-        if (r->status != HTTP_REQUEST_TIME_OUT) {
-            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                         "request failed: error reading the headers");
-            ap_send_error_response(r, 0);
-            ap_run_log_transaction(r);
-            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_NOERRNO|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 r;
-        }
-    }
-
-    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;
-
-    conn->keptalive = 0;        /* We now have a request to play with */
-
-    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_NOERRNO|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;
-    }
-    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_NOERRNO|APLOG_INFO, 0, r,
-                          "client sent an unrecognized expectation value of "
-                          "Expect: %s", expect);
-            ap_send_error_response(r, 0);
-            (void) ap_discard_request_body(r);
-            ap_run_log_transaction(r);
-            return r;
-        }
-    }
-
-    if ((access_status = ap_run_post_read_request(r))) {
-        ap_die(access_status, r);
-        ap_run_log_transaction(r);
-        return NULL;
-    }
-
-    return r;
-}
-
-/*
- * A couple of other functions which initialize some of the fields of
- * a request structure, as appropriate for adjuncts of one kind or another
- * to a request in progress.  Best here, rather than elsewhere, since
- * *someone* has to set the protocol-specific fields...
- */
-
-void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r)
-{
-    rnew->the_request     = r->the_request;  /* Keep original request-line */
-
-    rnew->assbackwards    = 1;   /* Don't send headers from this. */
-    rnew->no_local_copy   = 1;   /* Don't try to send HTTP_NOT_MODIFIED for a
-                                  * fragment. */
-    rnew->method          = "GET";
-    rnew->method_number   = M_GET;
-    rnew->protocol        = "INCLUDED";
-
-    rnew->status          = HTTP_OK;
-
-    rnew->headers_in      = r->headers_in;
-    rnew->subprocess_env  = apr_table_copy(rnew->pool, r->subprocess_env);
-    rnew->headers_out     = apr_table_make(rnew->pool, 5);
-    rnew->err_headers_out = apr_table_make(rnew->pool, 5);
-    rnew->notes           = apr_table_make(rnew->pool, 5);
-
-    rnew->expecting_100   = r->expecting_100;
-    rnew->read_length     = r->read_length;
-    rnew->read_body       = REQUEST_NO_BODY;
-
-    rnew->main = (request_rec *) r;
-}
-
-static void end_output_stream(request_rec *r)
-{
-    apr_bucket_brigade *bb;
-    apr_bucket *b;
-
-    bb = apr_brigade_create(r->pool);
-    b = apr_bucket_eos_create();
-    APR_BRIGADE_INSERT_TAIL(bb, b);
-    ap_pass_brigade(r->output_filters, bb);
-}
-
-void ap_finalize_sub_req_protocol(request_rec *sub)
-{
-    end_output_stream(sub); 
-}
-
-/*
- * Support for the Basic authentication protocol, and a bit for Digest.
- */
-
-AP_DECLARE(void) ap_note_auth_failure(request_rec *r)
-{
-    if (!strcasecmp(ap_auth_type(r), "Basic"))
-        ap_note_basic_auth_failure(r);
-    else if (!strcasecmp(ap_auth_type(r), "Digest"))
-        ap_note_digest_auth_failure(r);
-}
-
-AP_DECLARE(void) ap_note_basic_auth_failure(request_rec *r)
-{
-    if (strcasecmp(ap_auth_type(r), "Basic"))
-        ap_note_auth_failure(r);
-    else
-        apr_table_setn(r->err_headers_out,
-                  r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
-                  apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
-                          NULL));
-}
-
-AP_DECLARE(void) ap_note_digest_auth_failure(request_rec *r)
-{
-    apr_table_setn(r->err_headers_out,
-           r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
-           apr_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%llx\"",
-               ap_auth_name(r), r->request_time));
-}
-
-AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
-{
-    const char *auth_line = apr_table_get(r->headers_in,
-                                      r->proxyreq ? "Proxy-Authorization"
-                                                  : "Authorization");
-    const char *t;
-
-    if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
-        return DECLINED;
-
-    if (!ap_auth_name(r)) {
-        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
-                     0, r, "need AuthName: %s", r->uri);
-        return HTTP_INTERNAL_SERVER_ERROR;
+                return APR_EOF;
+            }
+            else {
+                const char *c = str;
+                while (c - str < length) {
+                    if (*c == APR_ASCII_LF)
+                        c++;
+                    else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF)
+                        c += 2;
+                    else return APR_SUCCESS;
+                }
+                apr_bucket_delete(e);
+            }
+        }
     }
 
-    if (!auth_line) {
-        ap_note_basic_auth_failure(r);
-        return HTTP_UNAUTHORIZED;
+    if (APR_BRIGADE_EMPTY(ctx->b)) {
+        if ((rv = ap_get_brigade(f->next, ctx->b, mode)) != APR_SUCCESS) {
+            return rv;
+        }
     }
 
-    if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
-        /* Client tried to authenticate using wrong auth scheme */
-        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                     "client used wrong authentication scheme: %s", r->uri);
-        ap_note_basic_auth_failure(r);
-        return HTTP_UNAUTHORIZED;
-    }
+    if (f->c->remain) {
+        while (!APR_BRIGADE_EMPTY(ctx->b)) {
+            const char *ignore;
 
-    while (*auth_line== ' ' || *auth_line== '\t') {
-        auth_line++;
-    }
+            e = APR_BRIGADE_FIRST(ctx->b);
+            if ((rv = apr_bucket_read(e, &ignore, &len, mode)) != APR_SUCCESS) {
+                /* probably APR_IS_EAGAIN(rv); socket state isn't correct;
+                 * remove log once we get this squared away */
+                ap_log_error(APLOG_MARK, APLOG_ERR, rv, f->c->base_server, 
+                             "apr_bucket_read");
+                return rv;
+            }
 
-    t = ap_pbase64decode(r->pool, auth_line);
-    /* Note that this allocation has to be made from r->connection->pool
-     * because it has the lifetime of the connection.  The other allocations
-     * are temporary and can be tossed away any time.
-     */
-    r->user = ap_getword_nulls (r->pool, &t, ':');
-    r->ap_auth_type = "Basic";
+            if (len) {
+                if (f->c->remain < len) {
+                    apr_bucket_split(e, f->c->remain);
+                    f->c->remain = 0;
+                }
+                else {
+                    f->c->remain -= len;
+                }
+                APR_BUCKET_REMOVE(e);
+                APR_BRIGADE_INSERT_TAIL(b, e);
+                break; /* once we've gotten some data, deliver it to caller */
+            }
+            apr_bucket_delete(e);
+        }
+        if (f->c->remain == 0) {
+            apr_bucket *eos = apr_bucket_eos_create();
+                
+            APR_BRIGADE_INSERT_TAIL(b, eos);
+        }
+        return APR_SUCCESS;
+    }
 
-    *pw = t;
+    while (!APR_BRIGADE_EMPTY(ctx->b)) {
+        e = APR_BRIGADE_FIRST(ctx->b);
+        if ((rv = apr_bucket_read(e, (const char **)&buff, &len, mode)) != APR_SUCCESS) {
+            return rv;
+        }
 
-    return OK;
+        pos = memchr(buff, APR_ASCII_LF, len);
+        if (pos != NULL) {
+            apr_bucket_split(e, pos - buff + 1);
+            APR_BUCKET_REMOVE(e);
+            APR_BRIGADE_INSERT_TAIL(b, e);
+            return APR_SUCCESS;
+        }
+        APR_BUCKET_REMOVE(e);
+        APR_BRIGADE_INSERT_TAIL(b, e);
+    }
+    return APR_SUCCESS;
 }
 
 /* New Apache routine to map status codes into array indicies
@@ -2222,183 +1205,6 @@ AP_DECLARE(void) ap_send_http_header(request_rec *r)
 {
 }
 
-struct content_length_ctx {
-    apr_bucket_brigade *saved;
-    int compute_len;
-    apr_size_t curr_len;
-};
-
-/* This filter computes the content length, but it also computes the number
- * of bytes sent to the client.  This means that this filter will always run
- * through all of the buckets in all brigades 
- */
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *f,
-                                                              apr_bucket_brigade *b)
-{
-    request_rec *r = f->r;
-    struct content_length_ctx *ctx;
-    apr_status_t rv;
-    apr_bucket *e;
-    int send_it = 0;
-
-    ctx = f->ctx;
-    if (!ctx) { /* first time through */
-        f->ctx = ctx = apr_pcalloc(r->pool, sizeof(struct content_length_ctx));
-    }
-
-    APR_BRIGADE_FOREACH(e, b) {
-        const char *ignored;
-        apr_size_t length;
-
-        if (APR_BUCKET_IS_EOS(e) || APR_BUCKET_IS_FLUSH(e)) {
-            send_it = 1;
-        }
-        if (e->length == -1) { /* if length unknown */
-            rv = apr_bucket_read(e, &ignored, &length, APR_BLOCK_READ);
-            if (rv != APR_SUCCESS) {
-                return rv;
-            }
-        }
-        else {
-            length = e->length;
-        }
-        ctx->curr_len += length;
-        r->bytes_sent += length;
-    }
-
-    if ((ctx->curr_len < AP_MIN_BYTES_TO_WRITE) && !send_it) {
-        return ap_save_brigade(f, &ctx->saved, &b);
-    }
-
-    /* We will compute a content length if:
-     *     We already have all the data
-     *         This is a bit confusing, because we will always buffer up
-     *         to AP_MIN_BYTES_TO_WRITE, so if we get all the data while
-     *         we are buffering that much data, we set the c-l.
-     *  or We are in a 1.1 request and we can't chunk
-     *  or This is a keepalive connection
-     *         We may want to change this later to just close the connection
-     */
-    if ((r->proto_num == HTTP_VERSION(1,1)
-        && !ap_find_last_token(f->r->pool,
-                               apr_table_get(r->headers_out,
-                                             "Transfer-Encoding"),
-                               "chunked"))
-        || (f->r->connection->keepalive)
-        || (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(b)))) {
-        ctx->compute_len = 1;
-    }
-    else {
-        ctx->compute_len = 0;
-    }
-
-    if (ctx->compute_len) {
-        /* save the brigade; we can't pass any data to the next
-         * filter until we have the entire content length
-         */
-        if (!send_it) {
-            ap_save_brigade(f, &ctx->saved, &b);
-            return APR_SUCCESS;
-        }
-        ap_set_content_length(r, r->bytes_sent);
-    }
-    if (ctx->saved) {
-        APR_BRIGADE_CONCAT(ctx->saved, b);
-        apr_brigade_destroy(b);
-        b = ctx->saved;
-        ctx->saved = NULL;
-    }
-
-    ctx->curr_len = 0;
-    return ap_pass_brigade(f->next, b);
-}
-
-static int ap_set_byterange(request_rec *r)
-{
-    const char *range;
-    const char *if_range;
-    const char *match;
-    const char *ct;
-    apr_off_t range_start;
-    apr_off_t range_end;
-    int num_ranges;
-
-    if (r->assbackwards)
-        return 0;
-
-    /* Check for Range request-header (HTTP/1.1) or Request-Range for
-     * backwards-compatibility with second-draft Luotonen/Franks
-     * byte-ranges (e.g. Netscape Navigator 2-3).
-     *
-     * We support this form, with Request-Range, and (farther down) we
-     * send multipart/x-byteranges instead of multipart/byteranges for
-     * Request-Range based requests to work around a bug in Netscape
-     * Navigator 2-3 and MSIE 3.
-     */
-
-    if (!(range = apr_table_get(r->headers_in, "Range")))
-        range = apr_table_get(r->headers_in, "Request-Range");
-
-    if (!range || strncasecmp(range, "bytes=", 6)) {
-        return 0;
-    }
-
-    /* Check the If-Range header for Etag or Date.
-     * Note that this check will return false (as required) if either
-     * of the two etags are weak.
-     */
-    if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
-        if (if_range[0] == '"') {
-            if (!(match = apr_table_get(r->headers_out, "Etag")) ||
-                (strcmp(if_range, match) != 0))
-                return 0;
-        }
-        else if (!(match = apr_table_get(r->headers_out, "Last-Modified")) ||
-                 (strcmp(if_range, match) != 0))
-            return 0;
-    }
-
-    /* would be nice to pick this up from f->ctx */
-    ct = make_content_type(r, r->content_type);
-
-    if (!ap_strchr_c(range, ',')) {
-        int rv;
-        /* A single range */
-
-        /* rvarse_byterange() modifies the contents, so make a copy */
-        if ((rv = parse_byterange(apr_pstrdup(r->pool, range + 6), r->clength,
-                             &range_start, &range_end)) <= 0) {
-            return rv;
-        }
-        apr_table_setn(r->headers_out, "Content-Range",
-                       apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
-                                    range_start, range_end, r->clength));
-       apr_table_setn(r->headers_out, "Content-Type", ct);
-
-        num_ranges = 1;
-    }
-    else {
-        /* a multiple range */
-
-        num_ranges = 2;
-
-        /* ### it would be nice if r->boundary was in f->ctx */
-        r->boundary = apr_psprintf(r->pool, "%qx%lx",
-                                   r->request_time, (long) getpid());
-
-        apr_table_setn(r->headers_out, "Content-Type",
-                      apr_pstrcat(r->pool,
-                                   "multipart", use_range_x(r) ? "/x-" : "/",
-                                  "byteranges; boundary=", r->boundary,
-                                   NULL));
-    }
-
-    r->status = HTTP_PARTIAL_CONTENT;
-    r->range = range + 6;
-
-    return num_ranges;
-}
-
 typedef struct header_filter_cts {
     int headers_sent;
 } header_filter_ctx;
@@ -2478,7 +1284,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_b
 
     }
 
-    apr_table_setn(r->headers_out, "Content-Type", make_content_type(r,
+    apr_table_setn(r->headers_out, "Content-Type", ap_make_content_type(r,
         r->content_type));
 
     if (r->content_encoding) {
@@ -2578,22 +1384,6 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_b
     return ap_pass_brigade(f->next, b);
 }
 
-/* finalize_request_protocol is called at completion of sending the
- * response.  Its sole purpose is to send the terminating protocol
- * information for any wrappers around the response message body
- * (i.e., transfer encodings).  It should have been named finalize_response.
- */
-AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r)
-{
-    while (r->next) {
-        r = r->next;
-    }
-    /* tell the filter chain there is no more content coming */
-    if (!r->eos_sent) {
-        end_output_stream(r);
-    }
-}
-
 /* Here we deal with getting the request message body from the client.
  * Whether or not the request contains a body is signaled by the presence
  * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
@@ -2862,286 +1652,6 @@ AP_DECLARE(int) ap_discard_request_body(request_rec *r)
     return OK;
 }
 
-/*
- * Send the body of a response to the client.
- */
-AP_DECLARE(apr_status_t) ap_send_fd(apr_file_t *fd, request_rec *r, apr_off_t offset, 
-                                    apr_size_t len, apr_size_t *nbytes) 
-{
-    apr_bucket_brigade *bb = NULL;
-    apr_bucket *b;
-    apr_status_t rv;
-
-    bb = apr_brigade_create(r->pool);
-    b = apr_bucket_file_create(fd, offset, len);
-    APR_BRIGADE_INSERT_TAIL(bb, b);
-
-    rv = ap_pass_brigade(r->output_filters, bb);
-    if (rv != APR_SUCCESS) {
-        *nbytes = 0; /* no way to tell how many were actually sent */
-    }
-    else {
-        *nbytes = len;
-    }
-
-    return rv;
-}
-
-#if APR_HAS_MMAP
-/* send data from an in-memory buffer */
-AP_DECLARE(size_t) ap_send_mmap(apr_mmap_t *mm, request_rec *r, size_t offset,
-                             size_t length)
-{
-    apr_bucket_brigade *bb = NULL;
-    apr_bucket *b;
-
-    bb = apr_brigade_create(r->pool);
-    b = apr_bucket_mmap_create(mm, offset, length);
-    APR_BRIGADE_INSERT_TAIL(bb, b);
-    ap_pass_brigade(r->output_filters, bb);
-
-    return mm->size; /* XXX - change API to report apr_status_t? */
-}
-#endif /* APR_HAS_MMAP */
-
-typedef struct {
-    char *buf;
-    char *cur;
-    apr_size_t avail;
-} old_write_filter_ctx;
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(
-    ap_filter_t *f, apr_bucket_brigade *bb)
-{
-    old_write_filter_ctx *ctx = f->ctx;
-
-    AP_DEBUG_ASSERT(ctx);
-
-    if (ctx->buf != NULL) {
-        apr_size_t nbyte = ctx->cur - ctx->buf;
-
-        if (nbyte != 0) {
-            /* whatever is coming down the pipe (we don't care), we
-               can simply insert our buffered data at the front and
-               pass the whole bundle down the chain. */
-            apr_bucket *b = apr_bucket_heap_create(ctx->buf, nbyte, 0, NULL);
-            APR_BRIGADE_INSERT_HEAD(bb, b);
-            ctx->buf = NULL;
-        }
-    }
-
-    return ap_pass_brigade(f->next, bb);
-}
-
-static apr_status_t flush_buffer(request_rec *r, old_write_filter_ctx *ctx,
-                                 const char *extra, apr_size_t extra_len)
-{
-    apr_bucket_brigade *bb = apr_brigade_create(r->pool);
-    apr_size_t nbyte = ctx->cur - ctx->buf;
-    apr_bucket *b = apr_bucket_heap_create(ctx->buf, nbyte, 0, NULL);
-
-    APR_BRIGADE_INSERT_TAIL(bb, b);
-    ctx->buf = NULL;
-
-    /* if there is extra data, then send that, too */
-    if (extra != NULL) {
-        b = apr_bucket_transient_create(extra, extra_len);
-        APR_BRIGADE_INSERT_TAIL(bb, b);
-    }
-
-    return ap_pass_brigade(r->output_filters, bb);
-}
-
-static apr_status_t buffer_output(request_rec *r,
-                                  const char *str, apr_size_t len)
-{
-    ap_filter_t *f;
-    old_write_filter_ctx *ctx;
-    apr_size_t amt;
-    apr_status_t status;
-
-    if (len == 0)
-        return APR_SUCCESS;
-
-    /* ### future optimization: record some flags in the request_rec to
-       ### say whether we've added our filter, and whether it is first. */
-
-    /* this will typically exit on the first test */
-    for (f = r->output_filters; f != NULL; f = f->next)
-        if (strcmp("OLD_WRITE", f->frec->name) == 0)
-            break;
-    if (f == NULL) {
-        /* our filter hasn't been added yet */
-        ctx = apr_pcalloc(r->pool, sizeof(*ctx));
-        ap_add_output_filter("OLD_WRITE", ctx, r, r->connection);
-    }
-
-    /* if the first filter is not our buffering filter, then we have to
-       deliver the content through the normal filter chain */
-    if (strcmp("OLD_WRITE", r->output_filters->frec->name) != 0) {
-        apr_bucket_brigade *bb = apr_brigade_create(r->pool);
-        apr_bucket *b = apr_bucket_transient_create(str, len);
-        APR_BRIGADE_INSERT_TAIL(bb, b);
-
-        return ap_pass_brigade(r->output_filters, bb);
-    }
-
-    /* grab the context from our filter */
-    ctx = r->output_filters->ctx;
-
-    /* if there isn't a buffer in the context yet, put one there. */
-    if (ctx->buf == NULL) {
-        /* use the heap so it will get free'd after being flushed */
-        ctx->avail = AP_MIN_BYTES_TO_WRITE;
-        ctx->buf = ctx->cur = malloc(ctx->avail);
-    }
-
-    /* squeeze the data into the existing buffer */
-    if (len <= ctx->avail) {
-        memcpy(ctx->cur, str, len);
-        ctx->cur += len;
-        if ((ctx->avail -= len) == 0)
-            return flush_buffer(r, ctx, NULL, 0);
-        return APR_SUCCESS;
-    }
-
-    /* the new content can't fit in the existing buffer */
-
-    if (len >= AP_MIN_BYTES_TO_WRITE) {
-        /* it is really big. send what we have, and the new stuff. */
-        return flush_buffer(r, ctx, str, len);
-    }
-
-    /* the new data is small. put some into the current buffer, flush it,
-       and then drop the remaining into a new buffer. */
-    amt = ctx->avail;
-    memcpy(ctx->cur, str, amt);
-    ctx->cur += amt;
-    ctx->avail = 0;
-    if ((status = flush_buffer(r, ctx, NULL, 0)) != APR_SUCCESS)
-        return status;
-
-    ctx->buf = malloc(AP_MIN_BYTES_TO_WRITE);
-    memcpy(ctx->buf, str + amt, len - amt);
-    ctx->cur = ctx->buf + (len - amt);
-    ctx->avail = AP_MIN_BYTES_TO_WRITE - (len - amt);
-
-    return APR_SUCCESS;
-}
-
-AP_DECLARE(int) ap_rputc(int c, request_rec *r)
-{
-    char c2 = (char)c;
-
-    if (r->connection->aborted) {
-       return -1;
-    }
-
-    if (buffer_output(r, &c2, 1) != APR_SUCCESS)
-        return -1;
-
-    return c;
-}
-
-AP_DECLARE(int) ap_rputs(const char *str, request_rec *r)
-{
-    apr_size_t len;
-
-    if (r->connection->aborted)
-        return -1;
-
-    if (buffer_output(r, str, len = strlen(str)) != APR_SUCCESS)
-        return -1;
-
-    return len;
-}
-
-AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
-{
-    if (r->connection->aborted)
-        return -1;
-
-    if (buffer_output(r, buf, nbyte) != APR_SUCCESS)
-        return -1;
-
-    return nbyte;
-}
-
-AP_DECLARE(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
-{
-    char buf[4096];
-    apr_size_t written;
-
-    if (r->connection->aborted)
-        return -1;
-
-    /* ### fix this mechanism to allow more than 4K of output */
-    written = apr_vsnprintf(buf, sizeof(buf), fmt, va);
-    if (buffer_output(r, buf, written) != APR_SUCCESS)
-        return -1;
-
-    return written;
-}
-
-AP_DECLARE_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...)
-{
-    va_list va;
-    int n;
-
-    if (r->connection->aborted)
-        return -1;
-
-    va_start(va, fmt);
-    n = ap_vrprintf(r, fmt, va);
-    va_end(va);
-
-    return n;
-}
-
-AP_DECLARE_NONSTD(int) ap_rvputs(request_rec *r, ...)
-{
-    va_list va;
-    const char *s;
-    apr_size_t len;
-    apr_size_t written = 0;
-
-    if (r->connection->aborted)
-        return -1;
-
-    /* ### TODO: if the total output is large, put all the strings
-       ### into a single brigade, rather than flushing each time we
-       ### fill the buffer */
-    va_start(va, r);
-    while (1) {
-        s = va_arg(va, const char *);
-        if (s == NULL)
-            break;
-
-        len = strlen(s);
-        if (buffer_output(r, s, len) != APR_SUCCESS) {
-            return -1;
-        }
-
-        written += len;
-    }
-    va_end(va);
-
-    return written;
-}
-
-AP_DECLARE(int) ap_rflush(request_rec *r)
-{
-    apr_bucket_brigade *bb;
-    apr_bucket *b;
-
-    bb = apr_brigade_create(r->pool);
-    b = apr_bucket_flush_create();
-    APR_BRIGADE_INSERT_TAIL(bb, b);
-    if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS)
-        return -1;
-    return 0;
-}
-
 static const char *add_optional_notes(request_rec *r, 
                                       const char *prefix,
                                       const char *key, 
@@ -3553,11 +2063,3 @@ AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
     ap_finalize_request_protocol(r);
 }
 
-AP_IMPLEMENT_HOOK_RUN_ALL(int,post_read_request,
-                          (request_rec *r),(r),OK,DECLINED)
-AP_IMPLEMENT_HOOK_RUN_ALL(int,log_transaction,
-                          (request_rec *r),(r),OK,DECLINED)
-AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,http_method,
-                            (const request_rec *r),(r),NULL)
-AP_IMPLEMENT_HOOK_RUN_FIRST(unsigned short,default_port,
-                            (const request_rec *r),(r),0)
index eb3e3edb8a21c35abd76a915ca69a66859014220..63e9fad1634eaa80a309751c24c07dff7f690cab 100644 (file)
@@ -70,37 +70,35 @@ extern "C" {
  * @package mod_core private header file
  */
 
-/*
- * These (output) filters are internal to the mod_core operation.
- */
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, apr_bucket_brigade *b);
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_bucket_brigade *b);
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *, 
-                                                              apr_bucket_brigade *);
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(ap_filter_t *f, apr_bucket_brigade *b);
-
 /*
  * These (input) filters are internal to the mod_core operation.
  */
 apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode);
 apr_status_t ap_dechunk_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode);
 
+char *ap_response_code_string(request_rec *r, int error_index);
 
-/*
- * Setting up the protocol fields for subsidiary requests...
- * Also, a wrapup function to keep the internal accounting straight.
+/**
+ * Send the minimal part of an HTTP response header.
+ * @param r The current request
+ * @param bb The brigade to add the header to.
+ * @warning Modules should be very careful about using this, and should
+ *          prefer ap_send_http_header().  Much of the HTTP/1.1 implementation
+ *          correctness depends on code in ap_send_http_header().
+ * @deffunc void ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb)
  */
-void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r);
-void ap_finalize_sub_req_protocol(request_rec *sub_r);
-
+AP_DECLARE(void) ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb);
+/**
+ * Send the Status-Line and header fields for HTTP response
+ * @param r The current request
+ * @deffunc void ap_send_http_header(request_rec *r)
+ */
+AP_DECLARE(void) ap_send_http_header(request_rec *r);
 
-/* Send the response to special method requests */
 AP_DECLARE(int) ap_send_http_trace(request_rec *r);
 int ap_send_http_options(request_rec *r);
 
-
-char *ap_response_code_string(request_rec *r, int error_index);
-
 #ifdef __cplusplus
 }
 #endif
index b6540e5c6a6642da97b80be1010fc4f472f3f2a4..6f200ab4e360272a554c558ea6570c965522bfad 100644 (file)
 #include "http_main.h"
 #include "http_log.h"
 #include "util_script.h"
+#include "mod_core.h"
 
 
 #define IMAP_MAGIC_TYPE "application/x-httpd-imap"
index 9e98d7c5ef911d16059b22c9007853091c354b9c..39e85de10e1411266037047edee6ef4ed7b23f2c 100644 (file)
@@ -13,7 +13,7 @@ LTLIBRARY_SOURCES = \
        rfc1413.c connection.c listen.c \
         mpm_common.c util_charset.c util_debug.c util_xml.c \
        util_filter.c exports.c buildmark.c scoreboard.c \
-       error_bucket.c
+       error_bucket.c protocol.c
 
 targets = delete-exports $(LTLIBRARY_NAME)
 
diff --git a/server/protocol.c b/server/protocol.c
new file mode 100644 (file)
index 0000000..f3b1e7e
--- /dev/null
@@ -0,0 +1,1594 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/*
+ * http_protocol.c --- routines which directly communicate with the client.
+ *
+ * Code originally by Rob McCool; much redone by Robert S. Thau
+ * and the Apache Software Foundation.
+ */
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_lib.h"
+#include "apr_signal.h"
+
+#define APR_WANT_STDIO          /* for sscanf */
+#define APR_WANT_STRFUNC
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+
+#define CORE_PRIVATE
+#include "util_filter.h"
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_request.h"
+#include "http_vhost.h"
+#include "http_log.h"           /* For errors detected in basic auth common
+                                 * support code... */
+#include "util_date.h"          /* For parseHTTPdate and BAD_DATE */
+#include "util_charset.h"
+#include "util_ebcdic.h"
+
+#if APR_HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+APR_HOOK_STRUCT(
+           APR_HOOK_LINK(post_read_request)
+           APR_HOOK_LINK(log_transaction)
+           APR_HOOK_LINK(http_method)
+           APR_HOOK_LINK(default_port)
+)
+
+/*
+ * Builds the content-type that should be sent to the client from the
+ * content-type specified.  The following rules are followed:
+ *    - if type is NULL, type is set to ap_default_type(r)
+ *    - if charset adding is disabled, stop processing and return type.
+ *    - then, if there are no parameters on type, add the default charset
+ *    - return type
+ */
+const char *ap_make_content_type(request_rec *r, const char *type)
+{
+    static const char *needcset[] = {
+       "text/plain",
+       "text/html",
+       NULL };
+    const char **pcset;
+    core_dir_config *conf =
+       (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                               &core_module);
+
+    if (!type) {
+       type = ap_default_type(r);
+    }
+    if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) {
+       return type;
+    }
+
+    if (ap_strcasestr(type, "charset=") != NULL) {
+       /* already has parameter, do nothing */
+       /* XXX we don't check the validity */
+       ;
+    }
+    else {
+       /* see if it makes sense to add the charset. At present,
+        * we only add it if the Content-type is one of needcset[]
+        */
+       for (pcset = needcset; *pcset ; pcset++) {
+           if (ap_strcasestr(type, *pcset) != NULL) {
+               type = apr_pstrcat(r->pool, type, "; charset=", 
+                                  conf->add_default_charset_name, NULL);
+               break;
+           }
+       }
+    }
+    return type;
+}
+
+static int parse_byterange(char *range, apr_off_t clength,
+                           apr_off_t *start, apr_off_t *end)
+{
+    char *dash = strchr(range, '-');
+
+    if (!dash)
+        return 0;
+
+    if ((dash == range)) {
+        /* In the form "-5" */
+        *start = clength - atol(dash + 1);
+        *end = clength - 1;
+    }
+    else {
+        *dash = '\0';
+        dash++;
+        *start = atol(range);
+        if (*dash)
+            *end = atol(dash);
+        else                    /* "5-" */
+            *end = clength - 1;
+    }
+
+    if (*start < 0)
+       *start = 0;
+
+    if (*end >= clength)
+        *end = clength - 1;
+
+    if (*start > *end)
+       return -1;
+
+    return (*start > 0 || *end < clength);
+}
+
+static int ap_set_byterange(request_rec *r);
+
+typedef struct byterange_ctx {
+    apr_bucket_brigade *bb;
+    int num_ranges;
+    const char *orig_ct;
+} byterange_ctx;
+
+/*
+ * Here we try to be compatible with clients that want multipart/x-byteranges
+ * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
+ * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
+ * that the browser supports an older protocol. We also check User-Agent
+ * for Microsoft Internet Explorer 3, which needs this as well.
+ */
+static int use_range_x(request_rec *r)
+{
+    const char *ua;
+    return (apr_table_get(r->headers_in, "Request-Range") ||
+            ((ua = apr_table_get(r->headers_in, "User-Agent"))
+             && ap_strstr_c(ua, "MSIE 3")));
+}
+
+#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
+
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(
+    ap_filter_t *f,
+    apr_bucket_brigade *bb)
+{
+#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
+    request_rec *r = f->r;
+    byterange_ctx *ctx = f->ctx;
+    apr_bucket *e;
+    apr_bucket_brigade *bsend;
+    apr_off_t range_start;
+    apr_off_t range_end;
+    char *current;
+    char *bound_head;
+    apr_ssize_t bb_length;
+    apr_off_t clength = 0;
+    apr_status_t rv;
+    int found = 0;
+
+    if (!ctx) {
+        int num_ranges = ap_set_byterange(r);
+        if (num_ranges == -1) {
+            ap_remove_output_filter(f);
+            bsend = apr_brigade_create(r->pool);
+            e = ap_bucket_error_create(HTTP_RANGE_NOT_SATISFIABLE, NULL, r->pool);
+            APR_BRIGADE_INSERT_TAIL(bsend, e);
+            e = apr_bucket_eos_create();
+            APR_BRIGADE_INSERT_TAIL(bsend, e);
+            return ap_pass_brigade(f->next, bsend);
+        }
+        if (num_ranges == 0) {
+            ap_remove_output_filter(f);
+            return ap_pass_brigade(f->next, bb);
+        }
+
+        ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+        ctx->num_ranges = num_ranges;
+
+        if (num_ranges > 1) {
+            ctx->orig_ct = r->content_type;
+            r->content_type = 
+                 apr_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
+                          "byteranges; boundary=", r->boundary, NULL);
+        }
+
+        /* create a brigade in case we never call ap_save_brigade() */
+        ctx->bb = apr_brigade_create(r->pool);
+    }
+
+    /* We can't actually deal with byte-ranges until we have the whole brigade
+     * because the byte-ranges can be in any order, and according to the RFC,
+     * we SHOULD return the data in the same order it was requested. 
+     */
+    if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+        ap_save_brigade(f, &ctx->bb, &bb);
+        return APR_SUCCESS;
+    }
+
+    /* compute this once (it is an invariant) */
+    bound_head = apr_pstrcat(r->pool,
+                             CRLF "--", r->boundary,
+                             CRLF "Content-type: ",
+                             ap_make_content_type(r, ctx->orig_ct),
+                             CRLF "Content-range: bytes ", 
+                             NULL);
+    ap_xlate_proto_to_ascii(bound_head, strlen(bound_head));
+
+    /* If we have a saved brigade from a previous run, concat the passed
+     * brigade with our saved brigade.  Otherwise just continue.  
+     */
+    if (ctx->bb) {
+        APR_BRIGADE_CONCAT(ctx->bb, bb);
+        bb = ctx->bb;
+        ctx->bb = NULL;     /* ### strictly necessary? call brigade_destroy? */
+    }
+
+    /* It is possible that we won't have a content length yet, so we have to
+     * compute the length before we can actually do the byterange work.
+     */
+    (void) apr_brigade_length(bb, 1, &bb_length);
+    clength = (apr_off_t)bb_length;
+
+    /* this brigade holds what we will be sending */
+    bsend = apr_brigade_create(r->pool);
+
+    while ((current = ap_getword(r->pool, &r->range, ',')) &&
+           (rv = parse_byterange(current, clength, &range_start, &range_end))) {
+        apr_bucket *e2;
+        apr_bucket *ec;
+
+        if (rv == -1) {
+            continue;
+        }        
+        else {
+            found = 1;
+        }
+
+        if (ctx->num_ranges > 1) {
+            char *ts;
+
+            e = apr_bucket_pool_create(bound_head,
+                                      strlen(bound_head), r->pool);
+            APR_BRIGADE_INSERT_TAIL(bsend, e);
+
+            ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
+                              range_start, range_end, clength);
+            ap_xlate_proto_to_ascii(ts, strlen(ts));
+            e = apr_bucket_pool_create(ts, strlen(ts), r->pool);
+            APR_BRIGADE_INSERT_TAIL(bsend, e);
+        }
+        
+        e = apr_brigade_partition(bb, range_start);
+        e2 = apr_brigade_partition(bb, range_end + 1);
+        
+        ec = e;
+        do {
+            apr_bucket *foo;
+            const char *str;
+            apr_size_t len;
+
+            if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
+                apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
+                foo = apr_bucket_heap_create(str, len, 0, NULL);
+            }
+            APR_BRIGADE_INSERT_TAIL(bsend, foo);
+            ec = APR_BUCKET_NEXT(ec);
+        } while (ec != e2);
+    }
+
+    if (found == 0) {
+        ap_remove_output_filter(f);
+        r->status = HTTP_OK;
+        return HTTP_RANGE_NOT_SATISFIABLE;
+    }
+
+    if (ctx->num_ranges > 1) {
+        char *end;
+
+        /* add the final boundary */
+        end = apr_pstrcat(r->pool, CRLF "--", r->boundary, "--" CRLF, NULL);
+        ap_xlate_proto_to_ascii(end, strlen(end));
+        e = apr_bucket_pool_create(end, strlen(end), r->pool);
+        APR_BRIGADE_INSERT_TAIL(bsend, e);
+    }
+
+    e = apr_bucket_eos_create();
+    APR_BRIGADE_INSERT_TAIL(bsend, e);
+
+    /* we're done with the original content */
+    apr_brigade_destroy(bb);
+
+    /* send our multipart output */
+    return ap_pass_brigade(f->next, bsend); 
+}    
+
+AP_DECLARE(void) ap_set_content_length(request_rec *r, apr_off_t clength)
+{
+    r->clength = clength;
+    apr_table_setn(r->headers_out, "Content-Length",
+                   apr_psprintf(r->pool, "%" APR_OFF_T_FMT, clength));
+}
+
+/*
+ * Return the latest rational time from a request/mtime (modification time)
+ * pair.  We return the mtime unless it's in the future, in which case we
+ * return the current time.  We use the request time as a reference in order
+ * to limit the number of calls to time().  We don't check for futurosity
+ * unless the mtime is at least as new as the reference.
+ */
+AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
+{
+    apr_time_t now;
+
+    /* For all static responses, it's almost certain that the file was
+     * last modified before the beginning of the request.  So there's
+     * no reason to call time(NULL) again.  But if the response has been
+     * created on demand, then it might be newer than the time the request
+     * started.  In this event we really have to call time(NULL) again
+     * so that we can give the clients the most accurate Last-Modified.  If we
+     * were given a time in the future, we return the current time - the
+     * Last-Modified can't be in the future.
+     */
+    now = (mtime < r->request_time) ? r->request_time : apr_time_now();
+    return (mtime > now) ? now : mtime;
+}
+
+/*
+ * Construct an entity tag (ETag) from resource information.  If it's a real
+ * file, build in some of the file characteristics.  If the modification time
+ * is newer than (request-time minus 1 second), mark the ETag as weak - it
+ * could be modified again in as short an interval.  We rationalize the
+ * modification time we're given to keep it from being in the future.
+ */
+AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
+{
+    char *etag;
+    char *weak;
+
+    /*
+     * Make an ETag header out of various pieces of information. We use
+     * the last-modified date and, if we have a real file, the
+     * length and inode number - note that this doesn't have to match
+     * the content-length (i.e. includes), it just has to be unique
+     * for the file.
+     *
+     * If the request was made within a second of the last-modified date,
+     * we send a weak tag instead of a strong one, since it could
+     * be modified again later in the second, and the validation
+     * would be incorrect.
+     */
+    
+    weak = ((r->request_time - r->mtime > APR_USEC_PER_SEC)
+           && !force_weak) ? "" : "W/";
+
+    if (r->finfo.filetype != 0) {
+        etag = apr_psprintf(r->pool,
+                           "%s\"%lx-%lx-%lx\"", weak,
+                           (unsigned long) r->finfo.inode,
+                           (unsigned long) r->finfo.size,
+                           (unsigned long) r->mtime);
+    }
+    else {
+        etag = apr_psprintf(r->pool, "%s\"%lx\"", weak,
+                           (unsigned long) r->mtime);
+    }
+
+    return etag;
+}
+
+AP_DECLARE(void) ap_set_etag(request_rec *r)
+{
+    char *etag;
+    char *variant_etag, *vlv;
+    int vlv_weak;
+
+    if (!r->vlist_validator) {
+        etag = ap_make_etag(r, 0);
+    }
+    else {
+        /* If we have a variant list validator (vlv) due to the
+         * response being negotiated, then we create a structured
+         * entity tag which merges the variant etag with the variant
+         * list validator (vlv).  This merging makes revalidation
+         * somewhat safer, ensures that caches which can deal with
+         * Vary will (eventually) be updated if the set of variants is
+         * changed, and is also a protocol requirement for transparent
+         * content negotiation.
+         */
+
+        /* if the variant list validator is weak, we make the whole
+         * structured etag weak.  If we would not, then clients could
+         * have problems merging range responses if we have different
+         * variants with the same non-globally-unique strong etag.
+         */
+
+        vlv = r->vlist_validator;
+        vlv_weak = (vlv[0] == 'W');
+               
+        variant_etag = ap_make_etag(r, vlv_weak);
+
+        /* merge variant_etag and vlv into a structured etag */
+
+        variant_etag[strlen(variant_etag) - 1] = '\0';
+        if (vlv_weak)
+            vlv += 3;
+        else
+            vlv++;
+        etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
+    }
+
+    apr_table_setn(r->headers_out, "ETag", etag);
+}
+
+/* Get a line of protocol input, including any continuation lines
+ * caused by MIME folding (or broken clients) if fold != 0, and place it
+ * in the buffer s, of size n bytes, without the ending newline.
+ *
+ * Returns -1 on error, or the length of s.  
+ *
+ * Notes: Because the buffer uses 1 char for NUL, the most we can return is 
+ *        (n - 1) actual characters.  
+ *
+ *        If no LF is detected on the last line due to a dropped connection 
+ *        or a full buffer, that's considered an error.
+ */
+AP_CORE_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold)
+{
+    char *pos = s;
+    char *last_char;
+    char *beyond_buff = s + n;
+    const char *temp;
+    int retval;
+    int total = 0;
+    int looking_ahead = 0;
+    apr_size_t length;
+    conn_rec *c = r->connection;
+    core_request_config *req_cfg;
+    apr_bucket_brigade *b;
+    apr_bucket *e;
+
+    req_cfg = (core_request_config *)
+                ap_get_module_config(r->request_config, &core_module);
+    b = req_cfg->bb;
+    /* make sure it's empty unless we're folding */ 
+    AP_DEBUG_ASSERT(fold || APR_BRIGADE_EMPTY(b));
+
+    while (1) {
+        if (APR_BRIGADE_EMPTY(b)) {
+            if (ap_get_brigade(c->input_filters, b, AP_MODE_BLOCKING) != APR_SUCCESS ||
+                APR_BRIGADE_EMPTY(b)) {
+                return -1;
+            }
+        }
+        e = APR_BRIGADE_FIRST(b); 
+        if (e->length == 0) {
+            apr_bucket_delete(e);
+            continue;
+        }
+        retval = apr_bucket_read(e, &temp, &length, APR_BLOCK_READ);
+
+        if (retval != APR_SUCCESS) {
+            total = ((length < 0) && (total == 0)) ? -1 : total;
+            break;
+        }
+
+        if ((looking_ahead) && (*temp != APR_ASCII_BLANK) && (*temp != APR_ASCII_TAB)) { 
+            /* can't fold because next line isn't indented, 
+             * so return what we have.  lookahead brigade is 
+             * stashed on req_cfg->bb
+             */
+            AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(req_cfg->bb));
+            break;
+        }
+        last_char = pos + length - 1;
+        if (last_char < beyond_buff) {
+            memcpy(pos, temp, length);
+            apr_bucket_delete(e);
+        }
+        else {
+            /* input line was larger than the caller's buffer */
+            apr_brigade_destroy(b); 
+            
+            /* don't need to worry about req_cfg->bb being bogus.
+             * the request is about to die, and ErrorDocument
+             * redirects get a new req_cfg->bb
+             */
+            
+            return -1;
+        }
+        
+        pos = last_char;        /* Point at the last character           */
+
+        if (*pos == APR_ASCII_LF) { /* Did we get a full line of input?      */
+                
+            if (pos > s && *(pos - 1) == APR_ASCII_CR) {
+                --pos;          /* zap optional CR before LF             */
+            }
+                
+            /*
+             * Trim 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, and
+             * saves memory as well.  Terminate string at end of line.
+             */
+            while (pos > (s + 1) && 
+                   (*(pos - 1) == APR_ASCII_BLANK || *(pos - 1) == APR_ASCII_TAB)) {
+                --pos;          /* trim extra trailing spaces or tabs    */
+            }
+            *pos = '\0';        /* zap end of string                     */
+            total = pos - s;    /* update total string length            */
+
+            /* look ahead another line if line folding is desired 
+             * and this line isn't empty
+             */
+            if (fold && total) {
+                looking_ahead = 1;
+            }
+            else {
+                AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb));
+                break;
+            }
+        }
+        else {
+            /* no LF yet...character mode client (telnet)...keep going
+             * bump past last character read,   
+             * and set total in case we bail before finding a LF   
+             */
+            total = ++pos - s;    
+            looking_ahead = 0;  /* only appropriate right after LF       */ 
+        }
+    }
+    ap_xlate_proto_from_ascii(s, total);
+    return total;
+}
+
+/* parse_uri: break apart the uri
+ * Side Effects:
+ * - sets r->args to rest after '?' (or NULL if no '?')
+ * - sets r->uri to request uri (without r->args part)
+ * - sets r->hostname (if not set already) from request (scheme://host:port)
+ */
+AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri)
+{
+    int status = HTTP_OK;
+
+    r->unparsed_uri = apr_pstrdup(r->pool, uri);
+
+    if (r->method_number == M_CONNECT) {
+       status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri);
+    }
+    else {
+       /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */
+       status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri);
+    }
+
+    if (ap_is_HTTP_SUCCESS(status)) {
+       /* if it has a scheme we may need to do absoluteURI vhost stuff */
+       if (r->parsed_uri.scheme
+           && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) {
+           r->hostname = r->parsed_uri.hostname;
+       }
+       else if (r->method_number == M_CONNECT) {
+           r->hostname = r->parsed_uri.hostname;
+       }
+       r->args = r->parsed_uri.query;
+       r->uri = r->parsed_uri.path ? r->parsed_uri.path
+                                   : apr_pstrdup(r->pool, "/");
+#if defined(OS2) || defined(WIN32)
+       /* Handle path translations for OS/2 and plug security hole.
+        * This will prevent "http://www.wherever.com/..\..\/" from
+        * returning a directory for the root drive.
+        */
+       {
+           char *x;
+
+           for (x = r->uri; (x = strchr(x, '\\')) != NULL; )
+               *x = '/';
+       }
+#endif  /* OS2 || WIN32 */
+    }
+    else {
+       r->args = NULL;
+       r->hostname = NULL;
+       r->status = status;             /* set error status */
+       r->uri = apr_pstrdup(r->pool, uri);
+    }
+}
+
+static int read_request_line(request_rec *r)
+{
+    char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */
+    const char *ll = l;
+    const char *uri;
+#if 0
+    conn_rec *conn = r->connection;
+#endif
+    int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
+    int len;
+
+    /* 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.  But note that we may not
+     * have flushed any previous response completely to the client yet.
+     * We delay the flush as long as possible so that we can improve
+     * performance for clients that are pipelining requests.  If a request
+     * is pipelined then we won't block during the (implicit) read() below.
+     * If the requests aren't pipelined, then the client is still waiting
+     * for the final buffer flush from us, and we will block in the implicit
+     * read().  B_SAFEREAD ensures that the BUFF layer flushes if it will
+     * have to block during a read.
+     */
+
+    while ((len = ap_getline(l, sizeof(l), r, 0)) <= 0) {
+        if (len < 0) {             /* includes EOF */
+           /* this is a hack to make sure that request time is set,
+            * it's not perfect, but it's better than nothing 
+            */
+           r->request_time = apr_time_now();
+            return 0;
+        }
+    }
+    /* we've probably got something to do, ignore graceful restart requests */
+
+    /* XXX - sigwait doesn't work if the signal has been SIG_IGNed (under
+     * linux 2.0 w/ glibc 2.0, anyway), and this step isn't necessary when
+     * we're running a sigwait thread anyway. If/when unthreaded mode is
+     * put back in, we should make sure to ignore this signal iff a sigwait
+     * thread isn't used. - mvsk
+
+#ifdef SIGWINCH
+    apr_signal(SIGWINCH, SIG_IGN);
+#endif
+    */
+
+    r->request_time = apr_time_now();
+    r->the_request = apr_pstrdup(r->pool, l);
+    r->method = ap_getword_white(r->pool, &ll);
+
+#if 0
+/* XXX If we want to keep track of the Method, the protocol module should do
+ * it.  That support isn't in the scoreboard yet.  Hopefully next week 
+ * sometime.   rbb */
+    ap_update_connection_status(AP_CHILD_THREAD_FROM_ID(conn->id), "Method", r->method); 
+#endif
+    uri = ap_getword_white(r->pool, &ll);
+
+    /* 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;
+    }
+
+    ap_parse_uri(r, uri);
+
+    /* 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.
+     */
+    if (len > 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;
+    }
+
+    r->assbackwards = (ll[0] == '\0');
+    r->protocol = apr_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9");
+/* XXX If we want to keep track of the Method, the protocol module should do
+ * it.  That support isn't in the scoreboard yet.  Hopefully next week 
+ * sometime.   rbb
+    ap_update_connection_status(conn->id, "Protocol", r->protocol); 
+ */
+
+    if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor)
+      && 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);
+
+    return 1;
+}
+
+static void get_mime_headers(request_rec *r)
+{
+    char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */
+    char *value;
+    char *copy;
+    int len;
+    int fields_read = 0;
+    apr_table_t *tmp_headers;
+
+    /* 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 ((len = ap_getline(field, sizeof(field), r, 1)) > 0) {
+
+        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.<P>\n");
+            return;
+        }
+        /* 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 field size.
+         */
+        if (len > 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.<P>\n"
+                                      "<PRE>\n",
+                                      ap_escape_html(r->pool, field),
+                                      "</PRE>\n", NULL));
+            return;
+        }
+        copy = apr_palloc(r->pool, len + 1);
+        memcpy(copy, field, len + 1);
+
+        if (!(value = strchr(copy, ':'))) {     /* Find the colon separator */
+            r->status = HTTP_BAD_REQUEST;       /* or abort the bad request */
+            apr_table_setn(r->notes, "error-notes",
+                          apr_pstrcat(r->pool,
+                                      "Request header field is missing "
+                                      "colon separator.<P>\n"
+                                      "<PRE>\n",
+                                      ap_escape_html(r->pool, copy),
+                                      "</PRE>\n", NULL));
+            return;
+        }
+
+        *value = '\0';
+        ++value;
+        while (*value == ' ' || *value == '\t') {
+            ++value;            /* Skip to start of value   */
+       }
+
+       apr_table_addn(tmp_headers, copy, value);
+    }
+
+    apr_table_overlap(r->headers_in, tmp_headers, APR_OVERLAP_TABLES_MERGE);
+}
+
+request_rec *ap_read_request(conn_rec *conn)
+{
+    request_rec *r;
+    apr_pool_t *p;
+    const char *expect;
+    int access_status;
+    core_request_config *req_cfg;
+
+    apr_pool_create(&p, conn->pool);
+    r = apr_pcalloc(p, sizeof(request_rec));
+    r->pool            = p;
+    r->connection      = conn;
+    r->server          = conn->base_server;
+
+    conn->keptalive    = conn->keepalive == 1;
+    conn->keepalive    = 0;
+
+    r->user            = NULL;
+    r->ap_auth_type    = NULL;
+
+    r->allowed_methods = ap_make_method_list(p, 2);
+
+    r->headers_in      = apr_table_make(r->pool, 50);
+    r->subprocess_env  = apr_table_make(r->pool, 50);
+    r->headers_out     = apr_table_make(r->pool, 12);
+    r->err_headers_out = apr_table_make(r->pool, 5);
+    r->notes           = apr_table_make(r->pool, 5);
+
+    r->request_config  = ap_create_request_config(r->pool);
+    req_cfg = apr_pcalloc(r->pool, sizeof(core_request_config));
+    req_cfg->bb = apr_brigade_create(r->pool);
+    ap_set_module_config(r->request_config, &core_module, req_cfg);
+                    
+    r->per_dir_config  = r->server->lookup_defaults;
+
+    r->sent_bodyct     = 0;                      /* bytect isn't for body */
+
+    r->read_length     = 0;
+    r->read_body       = REQUEST_NO_BODY;
+
+    r->status          = HTTP_REQUEST_TIME_OUT;  /* Until we get a request */
+    r->the_request     = NULL;
+    r->output_filters  = conn->output_filters;
+    r->input_filters   = conn->input_filters;
+
+    apr_setsocketopt(conn->client_socket, APR_SO_TIMEOUT, 
+                     (int)(conn->keptalive
+                     ? r->server->keep_alive_timeout * APR_USEC_PER_SEC
+                     : r->server->timeout * APR_USEC_PER_SEC));
+                     
+    ap_add_output_filter("BYTERANGE", NULL, r, r->connection);
+    ap_add_output_filter("CONTENT_LENGTH", NULL, r, r->connection);
+    ap_add_output_filter("HTTP_HEADER", NULL, r, r->connection);
+
+    /* Get the request... */
+    if (!read_request_line(r)) {
+        if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                         "request failed: URI too long");
+            ap_send_error_response(r, 0);
+            ap_run_log_transaction(r);
+            return r;
+        }
+        return NULL;
+    }
+    if (r->connection->keptalive) {
+        apr_setsocketopt(r->connection->client_socket, APR_SO_TIMEOUT,
+                         (int)(r->server->timeout * APR_USEC_PER_SEC));
+    }
+    if (!r->assbackwards) {
+        get_mime_headers(r);
+        if (r->status != HTTP_REQUEST_TIME_OUT) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                         "request failed: error reading the headers");
+            ap_send_error_response(r, 0);
+            ap_run_log_transaction(r);
+            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_NOERRNO|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 r;
+        }
+    }
+
+    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;
+
+    conn->keptalive = 0;        /* We now have a request to play with */
+
+    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_NOERRNO|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;
+    }
+    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_NOERRNO|APLOG_INFO, 0, r,
+                          "client sent an unrecognized expectation value of "
+                          "Expect: %s", expect);
+            ap_send_error_response(r, 0);
+            (void) ap_discard_request_body(r);
+            ap_run_log_transaction(r);
+            return r;
+        }
+    }
+
+    if ((access_status = ap_run_post_read_request(r))) {
+        ap_die(access_status, r);
+        ap_run_log_transaction(r);
+        return NULL;
+    }
+
+    return r;
+}
+
+/*
+ * A couple of other functions which initialize some of the fields of
+ * a request structure, as appropriate for adjuncts of one kind or another
+ * to a request in progress.  Best here, rather than elsewhere, since
+ * *someone* has to set the protocol-specific fields...
+ */
+
+void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r)
+{
+    rnew->the_request     = r->the_request;  /* Keep original request-line */
+
+    rnew->assbackwards    = 1;   /* Don't send headers from this. */
+    rnew->no_local_copy   = 1;   /* Don't try to send HTTP_NOT_MODIFIED for a
+                                  * fragment. */
+    rnew->method          = "GET";
+    rnew->method_number   = M_GET;
+    rnew->protocol        = "INCLUDED";
+
+    rnew->status          = HTTP_OK;
+
+    rnew->headers_in      = r->headers_in;
+    rnew->subprocess_env  = apr_table_copy(rnew->pool, r->subprocess_env);
+    rnew->headers_out     = apr_table_make(rnew->pool, 5);
+    rnew->err_headers_out = apr_table_make(rnew->pool, 5);
+    rnew->notes           = apr_table_make(rnew->pool, 5);
+
+    rnew->expecting_100   = r->expecting_100;
+    rnew->read_length     = r->read_length;
+    rnew->read_body       = REQUEST_NO_BODY;
+
+    rnew->main = (request_rec *) r;
+}
+
+static void end_output_stream(request_rec *r)
+{
+    apr_bucket_brigade *bb;
+    apr_bucket *b;
+
+    bb = apr_brigade_create(r->pool);
+    b = apr_bucket_eos_create();
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+    ap_pass_brigade(r->output_filters, bb);
+}
+
+void ap_finalize_sub_req_protocol(request_rec *sub)
+{
+    end_output_stream(sub); 
+}
+
+/* finalize_request_protocol is called at completion of sending the
+ * response.  Its sole purpose is to send the terminating protocol
+ * information for any wrappers around the response message body
+ * (i.e., transfer encodings).  It should have been named finalize_response.
+ */
+AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r)
+{
+    while (r->next) {
+        r = r->next;
+    }
+    /* tell the filter chain there is no more content coming */
+    if (!r->eos_sent) {
+        end_output_stream(r);
+    }
+} 
+
+/*
+ * Support for the Basic authentication protocol, and a bit for Digest.
+ */
+
+AP_DECLARE(void) ap_note_auth_failure(request_rec *r)
+{
+    if (!strcasecmp(ap_auth_type(r), "Basic"))
+        ap_note_basic_auth_failure(r);
+    else if (!strcasecmp(ap_auth_type(r), "Digest"))
+        ap_note_digest_auth_failure(r);
+}
+
+AP_DECLARE(void) ap_note_basic_auth_failure(request_rec *r)
+{
+    if (strcasecmp(ap_auth_type(r), "Basic"))
+        ap_note_auth_failure(r);
+    else
+        apr_table_setn(r->err_headers_out,
+                  r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+                  apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
+                          NULL));
+}
+
+AP_DECLARE(void) ap_note_digest_auth_failure(request_rec *r)
+{
+    apr_table_setn(r->err_headers_out,
+           r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+           apr_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%llx\"",
+               ap_auth_name(r), r->request_time));
+}
+
+AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
+{
+    const char *auth_line = apr_table_get(r->headers_in,
+                                      r->proxyreq ? "Proxy-Authorization"
+                                                  : "Authorization");
+    const char *t;
+
+    if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
+        return DECLINED;
+
+    if (!ap_auth_name(r)) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
+                     0, r, "need AuthName: %s", r->uri);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (!auth_line) {
+        ap_note_basic_auth_failure(r);
+        return HTTP_UNAUTHORIZED;
+    }
+
+    if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
+        /* Client tried to authenticate using wrong auth scheme */
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                     "client used wrong authentication scheme: %s", r->uri);
+        ap_note_basic_auth_failure(r);
+        return HTTP_UNAUTHORIZED;
+    }
+
+    while (*auth_line== ' ' || *auth_line== '\t') {
+        auth_line++;
+    }
+
+    t = ap_pbase64decode(r->pool, auth_line);
+    /* Note that this allocation has to be made from r->connection->pool
+     * because it has the lifetime of the connection.  The other allocations
+     * are temporary and can be tossed away any time.
+     */
+    r->user = ap_getword_nulls (r->pool, &t, ':');
+    r->ap_auth_type = "Basic";
+
+    *pw = t;
+
+    return OK;
+}
+
+struct content_length_ctx {
+    apr_bucket_brigade *saved;
+    int compute_len;
+    apr_size_t curr_len;
+};
+
+/* This filter computes the content length, but it also computes the number
+ * of bytes sent to the client.  This means that this filter will always run
+ * through all of the buckets in all brigades 
+ */
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *f,
+                                                              apr_bucket_brigade *b)
+{
+    request_rec *r = f->r;
+    struct content_length_ctx *ctx;
+    apr_status_t rv;
+    apr_bucket *e;
+    int send_it = 0;
+
+    ctx = f->ctx;
+    if (!ctx) { /* first time through */
+        f->ctx = ctx = apr_pcalloc(r->pool, sizeof(struct content_length_ctx));
+    }
+
+    APR_BRIGADE_FOREACH(e, b) {
+        const char *ignored;
+        apr_size_t length;
+
+        if (APR_BUCKET_IS_EOS(e) || APR_BUCKET_IS_FLUSH(e)) {
+            send_it = 1;
+        }
+        if (e->length == -1) { /* if length unknown */
+            rv = apr_bucket_read(e, &ignored, &length, APR_BLOCK_READ);
+            if (rv != APR_SUCCESS) {
+                return rv;
+            }
+        }
+        else {
+            length = e->length;
+        }
+        ctx->curr_len += length;
+        r->bytes_sent += length;
+    }
+
+    if ((ctx->curr_len < AP_MIN_BYTES_TO_WRITE) && !send_it) {
+        return ap_save_brigade(f, &ctx->saved, &b);
+    }
+
+    /* We will compute a content length if:
+     *     We already have all the data
+     *         This is a bit confusing, because we will always buffer up
+     *         to AP_MIN_BYTES_TO_WRITE, so if we get all the data while
+     *         we are buffering that much data, we set the c-l.
+     *  or We are in a 1.1 request and we can't chunk
+     *  or This is a keepalive connection
+     *         We may want to change this later to just close the connection
+     */
+    if ((r->proto_num == HTTP_VERSION(1,1)
+        && !ap_find_last_token(f->r->pool,
+                               apr_table_get(r->headers_out,
+                                             "Transfer-Encoding"),
+                               "chunked"))
+        || (f->r->connection->keepalive)
+        || (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(b)))) {
+        ctx->compute_len = 1;
+    }
+    else {
+        ctx->compute_len = 0;
+    }
+
+    if (ctx->compute_len) {
+        /* save the brigade; we can't pass any data to the next
+         * filter until we have the entire content length
+         */
+        if (!send_it) {
+            ap_save_brigade(f, &ctx->saved, &b);
+            return APR_SUCCESS;
+        }
+        ap_set_content_length(r, r->bytes_sent);
+    }
+    if (ctx->saved) {
+        APR_BRIGADE_CONCAT(ctx->saved, b);
+        apr_brigade_destroy(b);
+        b = ctx->saved;
+        ctx->saved = NULL;
+    }
+
+    ctx->curr_len = 0;
+    return ap_pass_brigade(f->next, b);
+}
+
+static int ap_set_byterange(request_rec *r)
+{
+    const char *range;
+    const char *if_range;
+    const char *match;
+    const char *ct;
+    apr_off_t range_start;
+    apr_off_t range_end;
+    int num_ranges;
+
+    if (r->assbackwards)
+        return 0;
+
+    /* Check for Range request-header (HTTP/1.1) or Request-Range for
+     * backwards-compatibility with second-draft Luotonen/Franks
+     * byte-ranges (e.g. Netscape Navigator 2-3).
+     *
+     * We support this form, with Request-Range, and (farther down) we
+     * send multipart/x-byteranges instead of multipart/byteranges for
+     * Request-Range based requests to work around a bug in Netscape
+     * Navigator 2-3 and MSIE 3.
+     */
+
+    if (!(range = apr_table_get(r->headers_in, "Range")))
+        range = apr_table_get(r->headers_in, "Request-Range");
+
+    if (!range || strncasecmp(range, "bytes=", 6)) {
+        return 0;
+    }
+
+    /* Check the If-Range header for Etag or Date.
+     * Note that this check will return false (as required) if either
+     * of the two etags are weak.
+     */
+    if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
+        if (if_range[0] == '"') {
+            if (!(match = apr_table_get(r->headers_out, "Etag")) ||
+                (strcmp(if_range, match) != 0))
+                return 0;
+        }
+        else if (!(match = apr_table_get(r->headers_out, "Last-Modified")) ||
+                 (strcmp(if_range, match) != 0))
+            return 0;
+    }
+
+    /* would be nice to pick this up from f->ctx */
+    ct = ap_make_content_type(r, r->content_type);
+
+    if (!ap_strchr_c(range, ',')) {
+        int rv;
+        /* A single range */
+
+        /* rvarse_byterange() modifies the contents, so make a copy */
+        if ((rv = parse_byterange(apr_pstrdup(r->pool, range + 6), r->clength,
+                             &range_start, &range_end)) <= 0) {
+            return rv;
+        }
+        apr_table_setn(r->headers_out, "Content-Range",
+                       apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
+                                    range_start, range_end, r->clength));
+       apr_table_setn(r->headers_out, "Content-Type", ct);
+
+        num_ranges = 1;
+    }
+    else {
+        /* a multiple range */
+
+        num_ranges = 2;
+
+        /* ### it would be nice if r->boundary was in f->ctx */
+        r->boundary = apr_psprintf(r->pool, "%qx%lx",
+                                   r->request_time, (long) getpid());
+
+        apr_table_setn(r->headers_out, "Content-Type",
+                      apr_pstrcat(r->pool,
+                                   "multipart", use_range_x(r) ? "/x-" : "/",
+                                  "byteranges; boundary=", r->boundary,
+                                   NULL));
+    }
+
+    r->status = HTTP_PARTIAL_CONTENT;
+    r->range = range + 6;
+
+    return num_ranges;
+}
+
+/*
+ * Send the body of a response to the client.
+ */
+AP_DECLARE(apr_status_t) ap_send_fd(apr_file_t *fd, request_rec *r, apr_off_t offset, 
+                                    apr_size_t len, apr_size_t *nbytes) 
+{
+    apr_bucket_brigade *bb = NULL;
+    apr_bucket *b;
+    apr_status_t rv;
+
+    bb = apr_brigade_create(r->pool);
+    b = apr_bucket_file_create(fd, offset, len);
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+
+    rv = ap_pass_brigade(r->output_filters, bb);
+    if (rv != APR_SUCCESS) {
+        *nbytes = 0; /* no way to tell how many were actually sent */
+    }
+    else {
+        *nbytes = len;
+    }
+
+    return rv;
+}
+
+#if APR_HAS_MMAP
+/* send data from an in-memory buffer */
+AP_DECLARE(size_t) ap_send_mmap(apr_mmap_t *mm, request_rec *r, size_t offset,
+                             size_t length)
+{
+    apr_bucket_brigade *bb = NULL;
+    apr_bucket *b;
+
+    bb = apr_brigade_create(r->pool);
+    b = apr_bucket_mmap_create(mm, offset, length);
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+    ap_pass_brigade(r->output_filters, bb);
+
+    return mm->size; /* XXX - change API to report apr_status_t? */
+}
+#endif /* APR_HAS_MMAP */
+
+typedef struct {
+    char *buf;
+    char *cur;
+    apr_size_t avail;
+} old_write_filter_ctx;
+
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(
+    ap_filter_t *f, apr_bucket_brigade *bb)
+{
+    old_write_filter_ctx *ctx = f->ctx;
+
+    AP_DEBUG_ASSERT(ctx);
+
+    if (ctx->buf != NULL) {
+        apr_size_t nbyte = ctx->cur - ctx->buf;
+
+        if (nbyte != 0) {
+            /* whatever is coming down the pipe (we don't care), we
+               can simply insert our buffered data at the front and
+               pass the whole bundle down the chain. */
+            apr_bucket *b = apr_bucket_heap_create(ctx->buf, nbyte, 0, NULL);
+            APR_BRIGADE_INSERT_HEAD(bb, b);
+            ctx->buf = NULL;
+        }
+    }
+
+    return ap_pass_brigade(f->next, bb);
+}
+
+static apr_status_t flush_buffer(request_rec *r, old_write_filter_ctx *ctx,
+                                 const char *extra, apr_size_t extra_len)
+{
+    apr_bucket_brigade *bb = apr_brigade_create(r->pool);
+    apr_size_t nbyte = ctx->cur - ctx->buf;
+    apr_bucket *b = apr_bucket_heap_create(ctx->buf, nbyte, 0, NULL);
+
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+    ctx->buf = NULL;
+
+    /* if there is extra data, then send that, too */
+    if (extra != NULL) {
+        b = apr_bucket_transient_create(extra, extra_len);
+        APR_BRIGADE_INSERT_TAIL(bb, b);
+    }
+
+    return ap_pass_brigade(r->output_filters, bb);
+}
+
+static apr_status_t buffer_output(request_rec *r,
+                                  const char *str, apr_size_t len)
+{
+    ap_filter_t *f;
+    old_write_filter_ctx *ctx;
+    apr_size_t amt;
+    apr_status_t status;
+
+    if (len == 0)
+        return APR_SUCCESS;
+
+    /* ### future optimization: record some flags in the request_rec to
+       ### say whether we've added our filter, and whether it is first. */
+
+    /* this will typically exit on the first test */
+    for (f = r->output_filters; f != NULL; f = f->next)
+        if (strcmp("OLD_WRITE", f->frec->name) == 0)
+            break;
+    if (f == NULL) {
+        /* our filter hasn't been added yet */
+        ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+        ap_add_output_filter("OLD_WRITE", ctx, r, r->connection);
+    }
+
+    /* if the first filter is not our buffering filter, then we have to
+       deliver the content through the normal filter chain */
+    if (strcmp("OLD_WRITE", r->output_filters->frec->name) != 0) {
+        apr_bucket_brigade *bb = apr_brigade_create(r->pool);
+        apr_bucket *b = apr_bucket_transient_create(str, len);
+        APR_BRIGADE_INSERT_TAIL(bb, b);
+
+        return ap_pass_brigade(r->output_filters, bb);
+    }
+
+    /* grab the context from our filter */
+    ctx = r->output_filters->ctx;
+
+    /* if there isn't a buffer in the context yet, put one there. */
+    if (ctx->buf == NULL) {
+        /* use the heap so it will get free'd after being flushed */
+        ctx->avail = AP_MIN_BYTES_TO_WRITE;
+        ctx->buf = ctx->cur = malloc(ctx->avail);
+    }
+
+    /* squeeze the data into the existing buffer */
+    if (len <= ctx->avail) {
+        memcpy(ctx->cur, str, len);
+        ctx->cur += len;
+        if ((ctx->avail -= len) == 0)
+            return flush_buffer(r, ctx, NULL, 0);
+        return APR_SUCCESS;
+    }
+
+    /* the new content can't fit in the existing buffer */
+
+    if (len >= AP_MIN_BYTES_TO_WRITE) {
+        /* it is really big. send what we have, and the new stuff. */
+        return flush_buffer(r, ctx, str, len);
+    }
+
+    /* the new data is small. put some into the current buffer, flush it,
+       and then drop the remaining into a new buffer. */
+    amt = ctx->avail;
+    memcpy(ctx->cur, str, amt);
+    ctx->cur += amt;
+    ctx->avail = 0;
+    if ((status = flush_buffer(r, ctx, NULL, 0)) != APR_SUCCESS)
+        return status;
+
+    ctx->buf = malloc(AP_MIN_BYTES_TO_WRITE);
+    memcpy(ctx->buf, str + amt, len - amt);
+    ctx->cur = ctx->buf + (len - amt);
+    ctx->avail = AP_MIN_BYTES_TO_WRITE - (len - amt);
+
+    return APR_SUCCESS;
+}
+
+AP_DECLARE(int) ap_rputc(int c, request_rec *r)
+{
+    char c2 = (char)c;
+
+    if (r->connection->aborted) {
+       return -1;
+    }
+
+    if (buffer_output(r, &c2, 1) != APR_SUCCESS)
+        return -1;
+
+    return c;
+}
+
+AP_DECLARE(int) ap_rputs(const char *str, request_rec *r)
+{
+    apr_size_t len;
+
+    if (r->connection->aborted)
+        return -1;
+
+    if (buffer_output(r, str, len = strlen(str)) != APR_SUCCESS)
+        return -1;
+
+    return len;
+}
+
+AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
+{
+    if (r->connection->aborted)
+        return -1;
+
+    if (buffer_output(r, buf, nbyte) != APR_SUCCESS)
+        return -1;
+
+    return nbyte;
+}
+
+AP_DECLARE(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
+{
+    char buf[4096];
+    apr_size_t written;
+
+    if (r->connection->aborted)
+        return -1;
+
+    /* ### fix this mechanism to allow more than 4K of output */
+    written = apr_vsnprintf(buf, sizeof(buf), fmt, va);
+    if (buffer_output(r, buf, written) != APR_SUCCESS)
+        return -1;
+
+    return written;
+}
+
+AP_DECLARE_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...)
+{
+    va_list va;
+    int n;
+
+    if (r->connection->aborted)
+        return -1;
+
+    va_start(va, fmt);
+    n = ap_vrprintf(r, fmt, va);
+    va_end(va);
+
+    return n;
+}
+
+AP_DECLARE_NONSTD(int) ap_rvputs(request_rec *r, ...)
+{
+    va_list va;
+    const char *s;
+    apr_size_t len;
+    apr_size_t written = 0;
+
+    if (r->connection->aborted)
+        return -1;
+
+    /* ### TODO: if the total output is large, put all the strings
+       ### into a single brigade, rather than flushing each time we
+       ### fill the buffer */
+    va_start(va, r);
+    while (1) {
+        s = va_arg(va, const char *);
+        if (s == NULL)
+            break;
+
+        len = strlen(s);
+        if (buffer_output(r, s, len) != APR_SUCCESS) {
+            return -1;
+        }
+
+        written += len;
+    }
+    va_end(va);
+
+    return written;
+}
+
+AP_DECLARE(int) ap_rflush(request_rec *r)
+{
+    apr_bucket_brigade *bb;
+    apr_bucket *b;
+
+    bb = apr_brigade_create(r->pool);
+    b = apr_bucket_flush_create();
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+    if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS)
+        return -1;
+    return 0;
+}
+
+AP_IMPLEMENT_HOOK_RUN_ALL(int,post_read_request,
+                          (request_rec *r),(r),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int,log_transaction,
+                          (request_rec *r),(r),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,http_method,
+                            (const request_rec *r),(r),NULL)
+AP_IMPLEMENT_HOOK_RUN_FIRST(unsigned short,default_port,
+                            (const request_rec *r),(r),0)