]> granicus.if.org Git - apache/blobdiff - server/core.c
switch to APR 1.0 API (which is still in flux)
[apache] / server / core.c
index e607a75b8fe21c64b672529375a328916f47af37..e5f5ef0da71d35a41adfe46d7b753d775f9d4ba2 100644 (file)
@@ -1,7 +1,7 @@
 /* ====================================================================
  * The Apache Software License, Version 1.1
  *
- * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
+ * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
  * reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -79,7 +79,6 @@
 #include "http_vhost.h"
 #include "http_main.h"     /* For the default_handler below... */
 #include "http_log.h"
-#include "rfc1413.h"
 #include "util_md5.h"
 #include "http_connection.h"
 #include "apr_buckets.h"
@@ -144,7 +143,6 @@ static void *create_core_dir_config(apr_pool_t *a, char *dir)
     conf->use_canonical_name = USE_CANONICAL_NAME_UNSET;
 
     conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET;
-    conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */
     conf->satisfy = SATISFY_NOSPEC;
 
 #ifdef RLIMIT_CPU
@@ -182,6 +180,7 @@ static void *create_core_dir_config(apr_pool_t *a, char *dir)
 
     conf->enable_mmap = ENABLE_MMAP_UNSET;
     conf->enable_sendfile = ENABLE_SENDFILE_UNSET;
+    conf->allow_encoded_slashes = 0;
 
     return (void *)conf;
 }
@@ -323,10 +322,6 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
         conf->hostname_lookups = new->hostname_lookups;
     }
 
-    if ((new->do_rfc1413 & 2) == 0) {
-        conf->do_rfc1413 = new->do_rfc1413;
-    }
-
     if ((new->content_md5 & 2) == 0) {
         conf->content_md5 = new->content_md5;
     }
@@ -452,6 +447,8 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
         conf->enable_sendfile = new->enable_sendfile;
     }
 
+    conf->allow_encoded_slashes = new->allow_encoded_slashes;
+    
     return (void*)conf;
 }
 
@@ -471,6 +468,10 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
     conf->sec_dir = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
     conf->sec_url = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
 
+    /* recursion stopper */
+    conf->redirect_limit = 0; /* 0 == unset */
+    conf->subreq_limit = 0;
+
     return (void *)conf;
 }
 
@@ -494,6 +495,14 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
     conf->sec_dir = apr_array_append(p, base->sec_dir, virt->sec_dir);
     conf->sec_url = apr_array_append(p, base->sec_url, virt->sec_url);
 
+    conf->redirect_limit = virt->redirect_limit
+                           ? virt->redirect_limit
+                           : base->redirect_limit;
+
+    conf->subreq_limit = virt->subreq_limit
+                         ? virt->subreq_limit
+                         : base->subreq_limit;
+
     return conf;
 }
 
@@ -827,24 +836,22 @@ AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
     }
 }
 
+/*
+ * Optional function coming from mod_ident, used for looking up ident user
+ */
+static APR_OPTIONAL_FN_TYPE(ap_ident_lookup) *ident_lookup;
+
 AP_DECLARE(const char *) ap_get_remote_logname(request_rec *r)
 {
-    core_dir_config *dir_conf;
-
     if (r->connection->remote_logname != NULL) {
         return r->connection->remote_logname;
     }
 
-    /* If we haven't checked the identity, and we want to */
-    dir_conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
-                                                       &core_module);
-
-    if (dir_conf->do_rfc1413 & 1) {
-        return ap_rfc1413(r->connection, r->server);
-    }
-    else {
-        return NULL;
+    if (ident_lookup) {
+        return ident_lookup(r);
     }
+
+    return NULL;
 }
 
 /* There are two options regarding what the "name" of a server is.  The
@@ -890,6 +897,23 @@ AP_DECLARE(const char *) ap_get_server_name(request_rec *r)
     return r->server->server_hostname;
 }
 
+/*
+ * Get the current server name from the request for the purposes
+ * of using in a URL.  If the server name is an IPv6 literal
+ * address, it will be returned in URL format (e.g., "[fe80::1]").
+ */
+static const char *get_server_name_for_url(request_rec *r)
+{
+    const char *plain_server_name = ap_get_server_name(r);
+
+#if APR_HAVE_IPV6
+    if (ap_strchr_c(plain_server_name, ':')) { /* IPv6 literal? */
+        return apr_psprintf(r->pool, "[%s]", plain_server_name);
+    }
+#endif
+    return plain_server_name;
+}
+
 AP_DECLARE(apr_port_t) ap_get_server_port(const request_rec *r)
 {
     apr_port_t port;
@@ -930,7 +954,7 @@ AP_DECLARE(char *) ap_construct_url(apr_pool_t *p, const char *uri,
                                     request_rec *r)
 {
     unsigned port = ap_get_server_port(r);
-    const char *host = ap_get_server_name(r);
+    const char *host = get_server_name_for_url(r);
 
     if (ap_is_default_port(port, r)) {
         return apr_pstrcat(p, ap_http_method(r), "://", host, uri, NULL);
@@ -1089,8 +1113,10 @@ static const char *set_document_root(cmd_parms *cmd, void *dummy,
         return err;
     }
 
+    /* Make it absolute, relative to ServerRoot */
+    arg = ap_server_root_relative(cmd->pool, arg);
+
     /* TODO: ap_configtestonly && ap_docrootcheck && */
-    /* XXX Shouldn't this be relative to ServerRoot ??? */
     if (apr_filepath_merge((char**)&conf->ap_document_root, NULL, arg,
                            APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS
         || !ap_is_directory(cmd->pool, arg)) {
@@ -1201,6 +1227,13 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l)
         return err;
     }
 
+    /* Throw a warning if we're in <Location> or <Files> */
+    if (ap_check_cmd_context(cmd, NOT_IN_LOCATION | NOT_IN_FILES)) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+                     "Useless use of AllowOverride in line %d.",
+                     cmd->directive->line_num);
+    }
+
     d->override = OR_NONE;
     while (l[0]) {
         w = ap_getword_conf(cmd->pool, &l);
@@ -1635,8 +1668,9 @@ static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
         /*
          * Ensure that the pathname is canonical, and append the trailing /
          */
-        if (apr_filepath_merge(&newpath, NULL, cmd->path,
-                               APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS) {
+        apr_status_t rv = apr_filepath_merge(&newpath, NULL, cmd->path,
+                                             APR_FILEPATH_TRUENAME, cmd->pool);
+        if (rv != APR_SUCCESS && rv != APR_EPATHWILD) {
             return apr_pstrcat(cmd->pool, "<Directory \"", cmd->path,
                                "\"> path is invalid.", NULL);
         }
@@ -2073,7 +2107,7 @@ static const char *set_timeout(cmd_parms *cmd, void *dummy, const char *arg)
     return NULL;
 }
 
-static const char *set_idcheck(cmd_parms *cmd, void *d_, int arg)
+static const char *set_allow2f(cmd_parms *cmd, void *d_, int arg)
 {
     core_dir_config *d = d_;
     const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
@@ -2082,7 +2116,7 @@ static const char *set_idcheck(cmd_parms *cmd, void *d_, int arg)
         return err;
     }
 
-    d->do_rfc1413 = arg != 0;
+    d->allow_encoded_slashes = arg != 0;
     return NULL;
 }
 
@@ -2191,7 +2225,7 @@ static const char *include_config (cmd_parms *cmd, void *dummy,
 {
     ap_directive_t *conftree = NULL;
     const char* conffile = ap_server_root_relative(cmd->pool, name);
-    
+
     if (!conffile) {
         return apr_pstrcat(cmd->pool, "Invalid Include path ", 
                            name, NULL);
@@ -2265,7 +2299,8 @@ AP_DECLARE(const char *) ap_psignature(const char *prefix, request_rec *r)
     apr_snprintf(sport, sizeof sport, "%u", (unsigned) ap_get_server_port(r));
 
     if (conf->server_signature == srv_sig_withmail) {
-        return apr_pstrcat(r->pool, prefix, "<address>" AP_SERVER_BASEVERSION
+        return apr_pstrcat(r->pool, prefix, "<address>", 
+                           ap_get_server_version(),
                            " Server at <a href=\"mailto:",
                            r->server->server_admin, "\">",
                            ap_escape_html(r->pool, ap_get_server_name(r)),
@@ -2273,7 +2308,7 @@ AP_DECLARE(const char *) ap_psignature(const char *prefix, request_rec *r)
                            "</address>\n", NULL);
     }
 
-    return apr_pstrcat(r->pool, prefix, "<address>" AP_SERVER_BASEVERSION
+    return apr_pstrcat(r->pool, prefix, "<address>", ap_get_server_version(),
                        " Server at ",
                        ap_escape_html(r->pool, ap_get_server_name(r)),
                        " Port ", sport,
@@ -2293,18 +2328,6 @@ static const char *set_authname(cmd_parms *cmd, void *mconfig,
     return NULL;
 }
 
-#ifdef _OSD_POSIX /* BS2000 Logon Passwd file */
-static const char *set_bs2000_account(cmd_parms *cmd, void *dummy, char *name)
-{
-    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
-    if (err != NULL) {
-        return err;
-    }
-
-    return os_set_account(cmd->pool, name);
-}
-#endif /*_OSD_POSIX*/
-
 /*
  * Handle a request to include the server's OS platform in the Server
  * response header field (the ServerTokens directive).  Unfortunately
@@ -2604,6 +2627,129 @@ static const char *set_limit_nproc(cmd_parms *cmd, void *conf_,
 }
 #endif
 
+static const char *set_recursion_limit(cmd_parms *cmd, void *dummy,
+                                       const char *arg1, const char *arg2)
+{
+    core_server_config *conf = ap_get_module_config(cmd->server->module_config,
+                                                    &core_module);
+    int limit = atoi(arg1);
+
+    if (limit <= 0) {
+        return "The recursion limit must be greater than zero.";
+    }
+    if (limit < 4) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+                     "Limiting internal redirects to very low numbers may "
+                     "cause normal requests to fail.");
+    }
+
+    conf->redirect_limit = limit;
+
+    if (arg2) {
+        limit = atoi(arg2);
+
+        if (limit <= 0) {
+            return "The recursion limit must be greater than zero.";
+        }
+        if (limit < 4) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+                         "Limiting the subrequest depth to a very low level may"
+                         " cause normal requests to fail.");
+        }
+    }
+
+    conf->subreq_limit = limit;
+
+    return NULL;
+}
+
+static void log_backtrace(const request_rec *r)
+{
+    const request_rec *top = r;
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                  "r->uri = %s", r->uri ? r->uri : "(unexpectedly NULL)");
+
+    while (top && (top->prev || top->main)) {
+        if (top->prev) {
+            top = top->prev;
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                          "redirected from r->uri = %s",
+                          top->uri ? top->uri : "(unexpectedly NULL)");
+        }
+
+        if (!top->prev && top->main) {
+            top = top->main;
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                          "subrequested from r->uri = %s",
+                          top->uri ? top->uri : "(unexpectedly NULL)");
+        }
+    }
+}
+
+/*
+ * check whether redirect limit is reached
+ */
+AP_DECLARE(int) ap_is_recursion_limit_exceeded(const request_rec *r)
+{
+    core_server_config *conf = ap_get_module_config(r->server->module_config,
+                                                    &core_module);
+    const request_rec *top = r;
+    int redirects = 0, subreqs = 0;
+    int rlimit = conf->redirect_limit
+                 ? conf->redirect_limit
+                 : AP_DEFAULT_MAX_INTERNAL_REDIRECTS;
+    int slimit = conf->subreq_limit
+                 ? conf->subreq_limit
+                 : AP_DEFAULT_MAX_SUBREQ_DEPTH;
+
+
+    while (top->prev || top->main) {
+        if (top->prev) {
+            if (++redirects >= rlimit) {
+                /* uuh, too much. */
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                              "Request exceeded the limit of %d internal "
+                              "redirects due to probable configuration error. "
+                              "Use 'LimitInternalRecursion' to increase the "
+                              "limit if necessary. Use 'LogLevel debug' to get "
+                              "a backtrace.", rlimit);
+
+                /* post backtrace */
+                log_backtrace(r);
+
+                /* return failure */
+                return 1;
+            }
+
+            top = top->prev;
+        }
+
+        if (!top->prev && top->main) {
+            if (++subreqs >= slimit) {
+                /* uuh, too much. */
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                              "Request exceeded the limit of %d subrequest "
+                              "nesting levels due to probable confguration "
+                              "error. Use 'LimitInternalRecursion' to increase "
+                              "the limit if necessary. Use 'LogLevel debug' to "
+                              "get a backtrace.", slimit);
+
+                /* post backtrace */
+                log_backtrace(r);
+
+                /* return failure */
+                return 1;
+            }
+
+            top = top->main;
+        }
+    }
+
+    /* recursion state: ok */
+    return 0;
+}
+
 static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
                                          const char *arg, const char *arg2)
 {
@@ -2618,6 +2764,12 @@ static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
     else {
         old = (ap_filter_rec_t*) apr_hash_get(conf->ct_output_filters, arg2,
                                               APR_HASH_KEY_STRING);
+        /* find last entry */
+        if (old) {
+            while (old->next) {
+                old = old->next;
+            }
+        }
     }
 
     while (*arg &&
@@ -2628,7 +2780,11 @@ static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
 
         /* We found something, so let's append it.  */
         if (old) {
-            new->next = old;
+            old->next = new;
+        }
+        else {
+            apr_hash_set(conf->ct_output_filters, arg2,
+                         APR_HASH_KEY_STRING, new);
         }
         old = new;
     }
@@ -2637,8 +2793,6 @@ static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
         return "invalid filter name";
     }
     
-    apr_hash_set(conf->ct_output_filters, arg2, APR_HASH_KEY_STRING, new);
-
     return NULL;
 }
 /* 
@@ -2650,7 +2804,7 @@ static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
 void ap_add_output_filters_by_type(request_rec *r)
 {
     core_dir_config *conf;
-    const char *ctype, *ctypes;
+    const char *ctype;
 
     conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
                                                    &core_module);
@@ -2663,10 +2817,9 @@ void ap_add_output_filters_by_type(request_rec *r)
         return;
     }
 
-    ctypes = r->content_type;
-
-    /* We must be able to handle decorated content-types.  */
-    while (*ctypes && (ctype = ap_getword(r->pool, &ctypes, ';'))) {
+    /* remove c-t decoration */
+    ctype = ap_field_noparam(r->pool, r->content_type);
+    if (ctype) {
         ap_filter_rec_t *ct_filter;
         ct_filter = apr_hash_get(conf->ct_output_filters, ctype,
                                  APR_HASH_KEY_STRING);
@@ -2692,7 +2845,7 @@ static apr_status_t writev_it_all(apr_socket_t *s,
 
     /* XXX handle checking for non-blocking socket */
     while (bytes_written != len) {
-        rv = apr_sendv(s, vec + i, nvec - i, &n);
+        rv = apr_socket_sendv(s, vec + i, nvec - i, &n);
         bytes_written += n;
         if (rv != APR_SUCCESS)
             return rv;
@@ -2700,7 +2853,7 @@ static apr_status_t writev_it_all(apr_socket_t *s,
         *nbytes += n;
 
         /* If the write did not complete, adjust the iovecs and issue
-         * apr_sendv again
+         * apr_socket_sendv again
          */
         if (bytes_written < len) {
             /* Skip over the vectors that have already been written */
@@ -2755,8 +2908,8 @@ static apr_status_t sendfile_it_all(core_net_rec *c,
     do {
         apr_size_t tmplen = file_bytes_left;
 
-        rv = apr_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
-                          flags);
+        rv = apr_socket_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
+                                 flags);
         *bytes_sent += tmplen;
         total_bytes_left -= tmplen;
         if (!total_bytes_left || rv != APR_SUCCESS) {
@@ -2866,7 +3019,7 @@ static apr_status_t emulate_sendfile(core_net_rec *c, apr_file_t *fd,
         rv = apr_file_read(fd, buffer, &sendlen);
         while (rv == APR_SUCCESS && sendlen) {
             bytes_sent = sendlen;
-            rv = apr_send(c->client_socket, &buffer[o], &bytes_sent);
+            rv = apr_socket_send(c->client_socket, &buffer[o], &bytes_sent);
             if (rv == APR_SUCCESS) {
                 sendlen -= bytes_sent; /* sendlen != bytes_sent ==> partial write */
                 o += bytes_sent;       /* o is where we are in the buffer */
@@ -2998,8 +3151,6 @@ AP_INIT_TAKE1("ServerPath", set_serverpath, NULL, RSRC_CONF,
   "The pathname the server can be reached at"),
 AP_INIT_TAKE1("Timeout", set_timeout, NULL, RSRC_CONF,
   "Timeout duration (sec)"),
-AP_INIT_FLAG("IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF,
-  "Enable identd (RFC 1413) user lookups - SLOW"),
 AP_INIT_FLAG("ContentDigest", set_content_md5, NULL, OR_OPTIONS,
   "whether or not to send a Content-MD5 header with each request"),
 AP_INIT_TAKE1("UseCanonicalName", set_use_canonical_name, NULL,
@@ -3014,10 +3165,6 @@ AP_INIT_TAKE1("LogLevel", set_loglevel, NULL, RSRC_CONF,
   "Level of verbosity in error logging"),
 AP_INIT_TAKE1("NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF,
   "A numeric IP address:port, or the name of a host"),
-#ifdef _OSD_POSIX
-AP_INIT_TAKE1("BS2000Account", set_bs2000_account, NULL, RSRC_CONF,
-  "Name of server User's bs2000 logon account name"),
-#endif
 AP_INIT_TAKE1("ServerTokens", set_serv_tokens, NULL, RSRC_CONF,
   "Determine tokens displayed in the Server: header - Min(imal), OS or Full"),
 AP_INIT_TAKE1("LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF,
@@ -3060,6 +3207,10 @@ AP_INIT_TAKE12("RLimitNPROC", no_set_limit, NULL,
    OR_ALL, "soft/hard limits for max number of processes per uid"),
 #endif
 
+/* internal recursion stopper */
+AP_INIT_TAKE12("LimitInternalRecursion", set_recursion_limit, NULL, RSRC_CONF,
+              "maximum recursion depth of internal redirects and subrequests"),
+
 AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower,
        (void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO,
      "a mime type that overrides other configured type"),
@@ -3075,6 +3226,8 @@ AP_INIT_TAKE1("SetInputFilter", ap_set_string_slot,
 AP_INIT_ITERATE2("AddOutputFilterByType", add_ct_output_filters,
        (void *)APR_OFFSETOF(core_dir_config, ct_output_filters), OR_FILEINFO,
      "output filter name followed by one or more content-types"),
+AP_INIT_FLAG("AllowEncodedSlashes", set_allow2f, NULL, RSRC_CONF,
+             "Allow URLs containing '/' encoded as '%2F'"),
 
 /*
  * These are default configuration directives that mpms can/should
@@ -3395,30 +3548,34 @@ static int default_handler(request_rec *r)
     }
 }
 
+typedef struct net_time_filter_ctx {
+    apr_socket_t *csd;
+    int           first_line;
+} net_time_filter_ctx_t;
 static int net_time_filter(ap_filter_t *f, apr_bucket_brigade *b,
                            ap_input_mode_t mode, apr_read_type_e block,
                            apr_off_t readbytes)
 {
+    net_time_filter_ctx_t *ctx = f->ctx;
     int keptalive = f->c->keepalive == AP_CONN_KEEPALIVE;
-    apr_socket_t *csd = ap_get_module_config(f->c->conn_config, &core_module);
-    int *first_line = f->ctx;
 
-    if (!f->ctx) {
-        f->ctx = first_line = apr_palloc(f->r->pool, sizeof(*first_line));
-        *first_line = 1;
+    if (!ctx) {
+        f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx));
+        ctx->first_line = 1;
+        ctx->csd = ap_get_module_config(f->c->conn_config, &core_module);        
     }
 
     if (mode != AP_MODE_INIT && mode != AP_MODE_EATCRLF) {
-        if (*first_line) {
-            apr_socket_timeout_set(csd, 
+        if (ctx->first_line) {
+            apr_socket_timeout_set(ctx->csd, 
                                    keptalive
                                       ? f->c->base_server->keep_alive_timeout
                                       : f->c->base_server->timeout);
-            *first_line = 0;
+            ctx->first_line = 0;
         }
         else {
             if (keptalive) {
-                apr_socket_timeout_set(csd, f->c->base_server->timeout);
+                apr_socket_timeout_set(ctx->csd, f->c->base_server->timeout);
             }
         }
     }
@@ -3494,6 +3651,19 @@ static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
         return APR_EOF;
     }
 
+    if (mode == AP_MODE_GETLINE) {
+        /* we are reading a single LF line, e.g. the HTTP headers */
+        rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN);
+        /* We should treat EAGAIN here the same as we do for EOF (brigade is
+         * empty).  We do this by returning whatever we have read.  This may
+         * or may not be bogus, but is consistent (for now) with EOF logic.
+         */
+        if (APR_STATUS_IS_EAGAIN(rv)) {
+            rv = APR_SUCCESS;
+        }
+        return rv;
+    }
+
     /* ### AP_MODE_PEEK is a horrific name for this mode because we also
      * eat any CRLFs that we see.  That's not the obvious intention of
      * this mode.  Determine whether anyone actually uses this or not. */
@@ -3536,6 +3706,7 @@ static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
             /* FIXME: Is this the right thing to do in the core? */
             apr_bucket_delete(e);
         }
+        return APR_SUCCESS;
     }
 
     /* If mode is EXHAUSTIVE, we want to just read everything until the end
@@ -3628,22 +3799,8 @@ static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
 
         /* Take what was originally there and place it back on ctx->b */
         APR_BRIGADE_CONCAT(ctx->b, newbb);
-
-        return APR_SUCCESS;
-    }
-
-    /* we are reading a single LF line, e.g. the HTTP headers */
-    rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN);
-
-    /* We should treat EAGAIN here the same as we do for EOF (brigade is
-     * empty).  We do this by returning whatever we have read.  This may
-     * or may not be bogus, but is consistent (for now) with EOF logic.
-     */
-    if (APR_STATUS_IS_EAGAIN(rv)) {
-        rv = APR_SUCCESS;
     }
-
-    return rv;
+    return APR_SUCCESS;
 }
 
 /* Default filter.  This filter should almost always be used.  Its only job
@@ -3660,9 +3817,12 @@ static APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *logio_add_bytes_out;
 static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
 {
     apr_status_t rv;
+    apr_bucket_brigade *more;
     conn_rec *c = f->c;
     core_net_rec *net = f->ctx;
     core_output_filter_ctx_t *ctx = net->out_ctx;
+    apr_read_type_e eblock = APR_NONBLOCK_READ;
+    apr_pool_t *input_pool = b->p;
 
     if (ctx == NULL) {
         ctx = apr_pcalloc(c->pool, sizeof(*ctx));
@@ -3683,9 +3843,6 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
         apr_bucket *last_e = NULL; /* initialized for debugging */
         apr_bucket *e;
 
-        /* tail of brigade if we need another pass */
-        apr_bucket_brigade *more = NULL;
-
         /* one group of iovecs per pass over the brigade */
         apr_size_t nvec = 0;
         apr_size_t nvec_trailers = 0;
@@ -3702,6 +3859,9 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
          */
         apr_bucket *last_merged_bucket = NULL;
 
+        /* tail of brigade if we need another pass */
+        more = NULL;
+
         /* Iterate over the brigade: collect iovecs and/or a file */
         APR_BRIGADE_FOREACH(e, b) {
             /* keep track of the last bucket processed */
@@ -3710,7 +3870,9 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
                 break;
             }
             if (APR_BUCKET_IS_FLUSH(e)) {
-                more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
+                if (e != APR_BRIGADE_LAST(b)) {
+                    more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
+                }
                 break;
             }
 
@@ -3738,7 +3900,16 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
                 const char *str;
                 apr_size_t n;
 
-                rv = apr_bucket_read(e, &str, &n, APR_BLOCK_READ);
+                rv = apr_bucket_read(e, &str, &n, eblock);
+                if (APR_STATUS_IS_EAGAIN(rv)) {
+                    /* send what we have so far since we shouldn't expect more
+                     * output for a while...  next time we read, block
+                     */
+                    more = apr_brigade_split(b, e);
+                    eblock = APR_BLOCK_READ;
+                    break;
+                }
+                eblock = APR_NONBLOCK_READ;
                 if (n) {
                     if (!fd) {
                         if (nvec == MAX_IOVEC_TO_WRITE) {
@@ -3850,26 +4021,23 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
         }
 
 
-        /* Completed iterating over the brigades, now determine if we want
+        /* Completed iterating over the brigade, now determine if we want
          * to buffer the brigade or send the brigade out on the network.
          *
-         * Save if:
+         * Save if we haven't accumulated enough bytes to send, and:
          *
          *   1) we didn't see a file, we don't have more passes over the
-         *      brigade to perform, we haven't accumulated enough bytes to
-         *      send, AND we didn't stop at a FLUSH bucket.
-         *      (IOW, we will save away plain old bytes)
+         *      brigade to perform,  AND we didn't stop at a FLUSH bucket.
+         *      (IOW, we will save plain old bytes such as HTTP headers)
          * or
          *   2) we hit the EOS and have a keep-alive connection
          *      (IOW, this response is a bit more complex, but we save it
          *       with the hope of concatenating with another response)
          */
-        if ((!fd && !more
-             && (nbytes + flen < AP_MIN_BYTES_TO_WRITE)
-             && !APR_BUCKET_IS_FLUSH(last_e))
-            || (nbytes + flen < AP_MIN_BYTES_TO_WRITE 
-                && APR_BUCKET_IS_EOS(last_e)
-                && c->keepalive == AP_CONN_KEEPALIVE)) {
+        if (nbytes + flen < AP_MIN_BYTES_TO_WRITE
+            && ((!fd && !more && !APR_BUCKET_IS_FLUSH(last_e))
+                || (APR_BUCKET_IS_EOS(last_e)
+                    && c->keepalive == AP_CONN_KEEPALIVE))) {
 
             /* NEVER save an EOS in here.  If we are saving a brigade with
              * an EOS bucket, then we are doing keepalive connections, and
@@ -3911,7 +4079,10 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
                     }
                 }
             }
-            ap_save_brigade(f, &ctx->b, &b, c->pool);
+            if (!ctx->deferred_write_pool) {
+                apr_pool_create(&ctx->deferred_write_pool, c->pool);
+            }
+            ap_save_brigade(f, &ctx->b, &b, ctx->deferred_write_pool);
 
             return APR_SUCCESS;
         }
@@ -3982,6 +4153,31 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
         }
 
         apr_brigade_destroy(b);
+        
+        /* drive cleanups for resources which were set aside 
+         * this may occur before or after termination of the request which
+         * created the resource
+         */
+        if (ctx->deferred_write_pool) {
+            if (more && more->p == ctx->deferred_write_pool) {
+                /* "more" belongs to the deferred_write_pool,
+                 * which is about to be cleared.
+                 */
+                if (APR_BRIGADE_EMPTY(more)) {
+                    more = NULL;
+                }
+                else {
+                    /* uh oh... change more's lifetime 
+                     * to the input brigade's lifetime 
+                     */
+                    apr_bucket_brigade *tmp_more = more;
+                    more = NULL;
+                    ap_save_brigade(f, &more, &tmp_more, input_pool);
+                }
+            }
+            apr_pool_clear(ctx->deferred_write_pool);  
+        }
+
         if (rv != APR_SUCCESS) {
             ap_log_error(APLOG_MARK, APLOG_INFO, rv, c->base_server,
                          "core_output_filter: writing data to the network");
@@ -4011,6 +4207,7 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
 static int core_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
 {
     logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
+    ident_lookup = APR_RETRIEVE_OPTIONAL_FN(ap_ident_lookup);
 
     ap_set_version(pconf);
     ap_setup_make_content_type(pconf);