httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Yann Ylavic <ylavic....@gmail.com>
Subject Re: stop copying footers to r->headers_in?
Date Mon, 28 Oct 2013 13:19:26 GMT
Regarding the "protocol" part, here is the latest patch.

This one renames the 
ap_rgetline_ex() and ap_get_mime_headers_ex() 
to respectively 
ap_rgetline_from
() and 

ap_get_mime_headers_from
() (more ap-ish name than apr-ish old ones, but it's just a proposition).

It also uses r->trailers_in/out instead of footers_in/out, this is how the
RFC calls them (again, it's just a proposition).

More importantly, ap_get_mime_headers_from() won't modify r->status nor
set r->notes now, while ap_get_mime_headers_core() will (still), thanks to
the new (static) ap_parse_mime_headers() used by both.

Moreover, the new hook :
+AP_IMPLEMENT_HOOK_RUN_ALL(int, trailer_parser,
+                          (request_rec *r), (r), OK, DECLINED)
is introduced, though nothing is hooked there for now.

Hence ap_http_filter() does run these hooks after having parsed the trailer.
Should any hook return anything else than OK/DECLINED, ap_http_filter()
will also return an error (APR_EINVAL?), for the caller to know.
The goal is (maybe) to hook the HTTP protocol checker (really) first here
to stop/forbid the trailers if not compliant...

Thanks for your feedbacks,
regards.

Index: include/http_protocol.h
===================================================================
--- include/http_protocol.h    (revision 1536297)
+++ include/http_protocol.h    (working copy)
@@ -75,6 +75,27 @@ AP_DECLARE(void) ap_get_mime_headers(request_rec *
 AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r,
                                           apr_bucket_brigade *bb);

+typedef struct ap_mime_headers_ctx_t ap_mime_headers_ctx_t;
+/**
+ * Full version of ap_get_mime_headers() that requires a
+ * temporary brigade, a blocking mode, a destination table
+ * and an non-blocking (optional) context to work with
+ * @param r The current request
+ * @param bb temp brigade
+ * @param from The filter to read from (r->proto_input_filters if NULL)
+ * @param block Read blocking mode
+ * @param to The table to put the headers/trailers to
+ * @param ctx If not NULL, allows for reentrant non-blocking call
+ * @return APR_SUCCESS when done, APR_EAGAIN when non-blocking, or
+ *         an unrecoverable APR error.
+ */
+AP_DECLARE(apr_status_t) ap_get_mime_headers_from(request_rec *r,
+                                                  apr_bucket_brigade *bb,
+                                                  ap_filter_t *from,
+                                                  apr_read_type_e block,
+                                                  apr_table_t *to,
+                                                  ap_mime_headers_ctx_t
**ctx);
+
 /* Finish up stuff after a request */

 /**
@@ -632,6 +653,12 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s
                                           request_rec *r, int fold,
                                           apr_bucket_brigade *bb);

+/** @see ap_rgetline */
+AP_DECLARE(apr_status_t) ap_rgetline_from(char **s, apr_size_t n,
+                                          apr_size_t *read, request_rec *r,
+                                          int fold, apr_bucket_brigade *bb,
+                                          ap_filter_t *from,
+                                          apr_read_type_e block);
 /**
  * Get the method number associated with the given string, assumed to
  * contain an HTTP method.  Returns M_INVALID if not recognized.
Index: include/http_config.h
===================================================================
--- include/http_config.h    (revision 1536297)
+++ include/http_config.h    (working copy)
@@ -1233,6 +1233,13 @@ AP_CORE_DECLARE(void *) ap_set_config_vectors(serv
 AP_DECLARE_HOOK(int,header_parser,(request_rec *r))

 /**
+ * Run the trailer parser functions for each module
+ * @param r The current request
+ * @return OK or DECLINED
+ */
+AP_DECLARE_HOOK(int,trailer_parser,(request_rec *r))
+
+/**
  * Run the pre_config function for each module
  * @param pconf The config pool
  * @param plog The logging streams pool
Index: include/httpd.h
===================================================================
--- include/httpd.h    (revision 1536297)
+++ include/httpd.h    (working copy)
@@ -1037,6 +1037,11 @@ struct request_rec {
      */
     apr_sockaddr_t *useragent_addr;
     char *useragent_ip;
+
+    /** MIME trailer environment from the request */
+    apr_table_t *trailers_in;
+    /** MIME trailer environment from the response */
+    apr_table_t *trailers_out;
 };

 /**
Index: server/config.c
===================================================================
--- server/config.c    (revision 1536297)
+++ server/config.c    (working copy)
@@ -71,6 +71,7 @@ AP_DECLARE_DATA ap_directive_t *ap_conftree = NULL

 APR_HOOK_STRUCT(
            APR_HOOK_LINK(header_parser)
+           APR_HOOK_LINK(trailer_parser)
            APR_HOOK_LINK(pre_config)
            APR_HOOK_LINK(check_config)
            APR_HOOK_LINK(post_config)
@@ -85,6 +86,8 @@ APR_HOOK_STRUCT(

 AP_IMPLEMENT_HOOK_RUN_ALL(int, header_parser,
                           (request_rec *r), (r), OK, DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int, trailer_parser,
+                          (request_rec *r), (r), OK, DECLINED)

 AP_IMPLEMENT_HOOK_RUN_ALL(int, pre_config,
                           (apr_pool_t *pconf, apr_pool_t *plog,
Index: server/protocol.c
===================================================================
--- server/protocol.c    (revision 1536297)
+++ server/protocol.c    (working copy)
@@ -193,7 +193,7 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
  * stricter protocol adherence and better input filter behavior during
  * chunked trailer processing (for http).
  *
- * If s is NULL, ap_rgetline_core will allocate necessary memory from
r->pool.
+ * If s is NULL, ap_rgetline_from will allocate necessary memory from
r->pool.
  *
  * Returns APR_SUCCESS if there are no problems and sets *read to be
  * the full length of s.
@@ -210,9 +210,11 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
  *        If no LF is detected on the last line due to a dropped connection
  *        or a full buffer, that's considered an error.
  */
-AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
+AP_DECLARE(apr_status_t) ap_rgetline_from(char **s, apr_size_t n,
                                           apr_size_t *read, request_rec *r,
-                                          int fold, apr_bucket_brigade *bb)
+                                          int fold, apr_bucket_brigade *bb,
+                                          ap_filter_t *from,
+                                          apr_read_type_e block)
 {
     apr_status_t rv;
     apr_bucket *e;
@@ -220,6 +222,10 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
     char *pos, *last_char = *s;
     int do_alloc = (*s == NULL), saw_eos = 0;

+    if (from == NULL) {
+        from = r->proto_input_filters;
+    }
+
     /*
      * Initialize last_char as otherwise a random value will be compared
      * against APR_ASCII_LF at the end of the loop if bb only contains
@@ -230,8 +236,28 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques

     for (;;) {
         apr_brigade_cleanup(bb);
-        rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE,
-                            APR_BLOCK_READ, 0);
+        rv = ap_get_brigade(from, bb, AP_MODE_GETLINE, block, 0);
+        if (block == APR_NONBLOCK_READ
+                && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))
+                        || APR_STATUS_IS_EAGAIN(rv))) {
+            *read = bytes_handled;
+            if (*s) {
+                /* ensure this string is NUL terminated */
+                if (n < bytes_handled + 1) {
+                    if (bytes_handled > 0) {
+                        (*s)[bytes_handled-1] = '\0';
+                    }
+                    else {
+                        (*s)[0] = '\0';
+                    }
+                    return APR_ENOSPC;
+                }
+                (*s)[bytes_handled] = '\0';
+            }
+            if (rv == APR_SUCCESS) {
+                rv = APR_EAGAIN;
+            }
+        }
         if (rv != APR_SUCCESS) {
             return rv;
         }
@@ -287,7 +313,7 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
                 /* We'll assume the common case where one bucket is
enough. */
                 if (!*s) {
                     current_alloc = len;
-                    *s = apr_palloc(r->pool, current_alloc);
+                    *s = apr_palloc(r->pool, current_alloc + 1);
                 }
                 else if (bytes_handled + len > current_alloc) {
                     /* Increase the buffer size */
@@ -298,7 +324,7 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
                         new_size = (bytes_handled + len) * 2;
                     }

-                    new_buffer = apr_palloc(r->pool, new_size);
+                    new_buffer = apr_palloc(r->pool, new_size + 1);

                     /* Copy what we already had. */
                     memcpy(new_buffer, *s, bytes_handled);
@@ -329,6 +355,7 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
     }
     *last_char = '\0';
     bytes_handled = last_char - *s;
+    *read = bytes_handled;

     /* If we're folding, we have more work to do.
      *
@@ -344,8 +371,14 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
             apr_brigade_cleanup(bb);

             /* We only care about the first byte. */
-            rv = ap_get_brigade(r->proto_input_filters, bb,
AP_MODE_SPECULATIVE,
-                                APR_BLOCK_READ, 1);
+            rv = ap_get_brigade(from, bb, AP_MODE_SPECULATIVE, block, 1);
+            if (block == APR_NONBLOCK_READ
+                    && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))
+                            || APR_STATUS_IS_EAGAIN(rv))) {
+                if (rv == APR_SUCCESS) {
+                    rv = APR_EAGAIN;
+                }
+            }
             if (rv != APR_SUCCESS) {
                 return rv;
             }
@@ -402,13 +435,13 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques

                     next_size = n - bytes_handled;

-                    rv = ap_rgetline_core(&tmp, next_size,
-                                          &next_len, r, 0, bb);
-                    if (rv != APR_SUCCESS) {
-                        return rv;
-                    }
+                    rv = ap_rgetline_from(&tmp, next_size, &next_len, r,
0, bb,
+                                          from, block);
+                    if ((rv == APR_SUCCESS || (block == APR_NONBLOCK_READ
&&
+                                               APR_STATUS_IS_EAGAIN(rv)))
+                            && (next_len > 0)) {

-                    if (do_alloc && next_len > 0) {
+                        if (do_alloc) {
                         char *new_buffer;
                         apr_size_t new_size = bytes_handled + next_len + 1;

@@ -419,12 +452,18 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
                         memcpy(new_buffer, *s, bytes_handled);

                         /* copy the new line, including the trailing null
*/
-                        memcpy(new_buffer + bytes_handled, tmp, next_len +
1);
+                        last_char = new_buffer + bytes_handled;
+                        memcpy(last_char, tmp, next_len + 1);
                         *s = new_buffer;
+                        }
+
+                        last_char += next_len;
+                        bytes_handled += next_len;
+                        *read = bytes_handled;
                     }
-
-                    last_char += next_len;
-                    bytes_handled += next_len;
+                    if (rv != APR_SUCCESS) {
+                        return rv;
+                    }
                 }
             }
             else { /* next character is not tab or space */
@@ -432,7 +471,6 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
             }
         }
     }
-    *read = bytes_handled;

     /* PR#43039: We shouldn't accept NULL bytes within the line */
     if (strlen(*s) < bytes_handled) {
@@ -442,6 +480,13 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(reques
     return APR_SUCCESS;
 }

+AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
+                                          apr_size_t *read, request_rec *r,
+                                          int fold, apr_bucket_brigade *bb)
+{
+    return ap_rgetline_from(s, n, read, r, fold, bb, NULL, APR_BLOCK_READ);
+}
+
 #if APR_CHARSET_EBCDIC
 AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
                                      apr_size_t *read, request_rec *r,
@@ -755,18 +800,45 @@ static int field_name_len(const char *field)
     return end - field;
 }

-AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r,
apr_bucket_brigade *bb)
+struct ap_mime_headers_ctx_t {
+    int fields_read;
+    char *last_field;
+    apr_size_t last_len;
+    apr_size_t alloc_len;
+    unsigned int fold :1,
+                 done :1;
+};
+
+static int ap_parse_mime_headers(request_rec *r, apr_bucket_brigade *bb,
+                                 ap_filter_t *from, apr_read_type_e block,
+                                 apr_table_t *to, ap_mime_headers_ctx_t
**ctx,
+                                 int *status, const char **html_notes)
 {
-    char *last_field = NULL;
-    apr_size_t last_len = 0;
-    apr_size_t alloc_len = 0;
     char *field;
     char *value;
     apr_size_t len;
-    int fields_read = 0;
     char *tmp_field;
+    ap_mime_headers_ctx_t thectx, *x;
     core_server_config *conf =
ap_get_core_module_config(r->server->module_config);

+    if (status) {
+        *status = OK;
+    }
+    if (html_notes) {
+        *html_notes = NULL;
+    }
+
+    if (!ctx) {
+        x = &thectx;
+        memset(x, 0, sizeof *x);
+    }
+    else if (!(x = *ctx)) {
+        *ctx = x = apr_pcalloc(r->pool, sizeof(ap_mime_headers_ctx_t));
+    }
+    else if (x->done) {
+        return APR_EOF;
+    }
+
     /*
      * Read header lines until we get the empty separator line, a read
error,
      * the connection closes (EOF), reach the server limit, or we timeout.
@@ -776,18 +848,25 @@ static int field_name_len(const char *field)
         int folded = 0;

         field = NULL;
-        rv = ap_rgetline(&field, r->server->limit_req_fieldsize + 2,
-                         &len, r, 0, bb);
+        rv = ap_rgetline_from(&field, r->server->limit_req_fieldsize + 2,
&len,
+                            r, 0, bb, from, block);

-        if (rv != APR_SUCCESS) {
-            if (APR_STATUS_IS_TIMEUP(rv)) {
-                r->status = HTTP_REQUEST_TIME_OUT;
+        if (block == APR_NONBLOCK_READ && APR_STATUS_IS_EAGAIN(rv)) {
+            if (len == 0) {
+                return rv;
             }
-            else {
-                r->status = HTTP_BAD_REQUEST;
+        }
+        else if (rv != APR_SUCCESS) {
+            if (status) {
+                if (APR_STATUS_IS_TIMEUP(rv)) {
+                    *status = HTTP_REQUEST_TIME_OUT;
+                }
+                else {
+                    *status = HTTP_BAD_REQUEST;
+                }
             }

-            /* ap_rgetline returns APR_ENOSPC if it fills up the buffer
before
+            /* ap_rgetline* returns APR_ENOSPC if it fills up the buffer
before
              * finding the end-of-line.  This is only going to happen if it
              * exceeds the configured limit for a field size.
              */
@@ -802,95 +881,108 @@ static int field_name_len(const char *field)
                     field_escaped = field = "";
                 }

-                apr_table_setn(r->notes, "error-notes",
-                               apr_psprintf(r->pool,
+                if (html_notes) {
+                    *html_notes = apr_psprintf(r->pool,
                                            "Size of a request header field
"
                                            "exceeds server limit.<br />\n"
                                            "<pre>\n%.*s\n</pre>\n",
                                            field_name_len(field_escaped),
-                                           field_escaped));
+                                           field_escaped);
+                }
                 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00561)
                               "Request header exceeds
LimitRequestFieldSize%s"
                               "%.*s",
                               *field ? ": " : "",
                               field_name_len(field), field);
             }
-            return;
+            return rv;
         }

-        if (last_field != NULL) {
-            if ((len > 0) && ((*field == '\t') || *field == ' ')) {
+        if (x->last_field != NULL) {
+            if ((len > 0) && (*field == '\t' || *field == ' ' || x->fold))
{
                 /* This line is a continuation of the preceding line(s),
                  * so append it to the line that we've set aside.
                  * Note: this uses a power-of-two allocator to avoid
                  * doing O(n) allocs and using O(n^2) space for
                  * continuations that span many many lines.
                  */
-                apr_size_t fold_len = last_len + len + 1; /* trailing null
*/
+                apr_size_t fold_len = x->last_len + len + 1; /* trailing
nul */

                 if (fold_len >=
(apr_size_t)(r->server->limit_req_fieldsize)) {
-                    r->status = HTTP_BAD_REQUEST;
+                    if (status) {
+                        *status = HTTP_BAD_REQUEST;
+                    }
                     /* report what we have accumulated so far before the
                      * overflow (last_field) as the field with the problem
                      */
-                    apr_table_setn(r->notes, "error-notes",
-                                   apr_psprintf(r->pool,
+                    if (html_notes) {
+                        *html_notes = apr_psprintf(r->pool,
                                                "Size of a request header
field "
                                                "after folding "
                                                "exceeds server limit.<br
/>\n"
                                                "<pre>\n%.*s\n</pre>\n",
-                                               field_name_len(last_field),
-                                               ap_escape_html(r->pool,
last_field)));
+
field_name_len(x->last_field),
+                                               ap_escape_html(r->pool,
+
x->last_field));
+                    }
                     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
APLOGNO(00562)
                                   "Request header exceeds
LimitRequestFieldSize "
                                   "after folding: %.*s",
-                                  field_name_len(last_field), last_field);
-                    return;
+                                  field_name_len(x->last_field),
+                                  x->last_field);
+                    return APR_ENOSPC;
                 }

-                if (fold_len > alloc_len) {
+                if (fold_len > x->alloc_len) {
                     char *fold_buf;
-                    alloc_len += alloc_len;
-                    if (fold_len > alloc_len) {
-                        alloc_len = fold_len;
+                    x->alloc_len += x->alloc_len;
+                    if (fold_len > x->alloc_len) {
+                        x->alloc_len = fold_len;
                     }
-                    fold_buf = (char *)apr_palloc(r->pool, alloc_len);
-                    memcpy(fold_buf, last_field, last_len);
-                    last_field = fold_buf;
+                    fold_buf = (char *)apr_palloc(r->pool, x->alloc_len);
+                    memcpy(fold_buf, x->last_field, x->last_len);
+                    x->last_field = fold_buf;
                 }
-                memcpy(last_field + last_len, field, len +1); /* +1 for
nul */
-                last_len += len;
+                memcpy(x->last_field + x->last_len, field, len +1); /*
+nul */
+                x->last_len += len;
+                x->fold = 0;
                 folded = 1;
             }
             else /* not a continuation line */ {

                 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.");
+                        && (++x->fields_read >
r->server->limit_req_fields)) {
+                    if (status) {
+                        *status = HTTP_BAD_REQUEST;
+                    }
+                    if (html_notes) {
+                        *html_notes = "The number of request header fields
"
+                                      "exceeds this server's limit.";
+                    }
                     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
APLOGNO(00563)
                                   "Number of request headers exceeds "
                                   "LimitRequestFields");
-                    return;
+                    return APR_ENOSPC;
                 }

-                if (!(value = strchr(last_field, ':'))) { /* Find ':'
or    */
-                    r->status = HTTP_BAD_REQUEST;      /* abort bad
request */
-                    apr_table_setn(r->notes, "error-notes",
-                                   apr_psprintf(r->pool,
+                if (!(value = strchr(x->last_field, ':'))) { /* ':' or */
+                    if (status) {
+                        *status = HTTP_BAD_REQUEST; /* abort bad request */
+                    }
+                    if (html_notes) {
+                        *html_notes = apr_psprintf(r->pool,
                                                "Request header field is "
                                                "missing ':' separator.<br
/>\n"
                                                "<pre>\n%.*s</pre>\n",
                                                (int)LOG_NAME_MAX_LEN,
                                                ap_escape_html(r->pool,
-
last_field)));
+
x->last_field));
+                    }
                     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
APLOGNO(00564)
                                   "Request header field is missing ':' "
                                   "separator: %.*s", (int)LOG_NAME_MAX_LEN,
-                                  last_field);
-                    return;
+                                  x->last_field);
+                    return APR_EINVAL;
                 }

                 tmp_field = value - 1; /* last character of field-name */
@@ -902,13 +994,13 @@ static int field_name_len(const char *field)
                 }

                 /* Strip LWS after field-name: */
-                while (tmp_field > last_field
+                while (tmp_field > x->last_field
                        && (*tmp_field == ' ' || *tmp_field == '\t')) {
                     *tmp_field-- = '\0';
                 }

                 /* Strip LWS after field-value: */
-                tmp_field = last_field + last_len - 1;
+                tmp_field = x->last_field + x->last_len - 1;
                 while (tmp_field > value
                        && (*tmp_field == ' ' || *tmp_field == '\t')) {
                     *tmp_field-- = '\0';
@@ -917,66 +1009,103 @@ static int field_name_len(const char *field)
                 if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
                     int err = 0;

-                    if (*last_field == '\0') {
+                    if (*x->last_field == '\0') {
                         err = HTTP_BAD_REQUEST;
                         ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
APLOGNO(02425)
                                       "Empty request header field name not
allowed");
                     }
-                    else if (ap_has_cntrl(last_field)) {
+                    else if (ap_has_cntrl(x->last_field)) {
                         err = HTTP_BAD_REQUEST;
                         ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
APLOGNO(02426)
                                       "[HTTP strict] Request header field
name contains "
                                       "control character: %.*s",
-                                      (int)LOG_NAME_MAX_LEN, last_field);
+                                      (int)LOG_NAME_MAX_LEN,
x->last_field);
                     }
                     else if (ap_has_cntrl(value)) {
                         err = HTTP_BAD_REQUEST;
                         ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
APLOGNO(02427)
                                       "Request header field '%.*s'
contains"
                                       "control character",
(int)LOG_NAME_MAX_LEN,
-                                      last_field);
+                                      x->last_field);
                     }
                     if (err && !(conf->http_conformance &
AP_HTTP_CONFORMANCE_LOGONLY)) {
-                        r->status = err;
-                        return;
+                        if (status) {
+                            *status = err;
+                        }
+                        return APR_EINVAL;
                     }
                 }
-                apr_table_addn(r->headers_in, last_field, value);
+                apr_table_addn(to, x->last_field, value);

                 /* reset the alloc_len so that we'll allocate a new
                  * buffer if we have to do any more folding: we can't
                  * use the previous buffer because its contents are
-                 * now part of r->headers_in
+                 * now part of *to
                  */
-                alloc_len = 0;
+                x->alloc_len = 0;

             } /* end if current line is not a continuation starting with
tab */
         }

-        /* Found a blank line, stop. */
-        if (len == 0) {
-            break;
-        }
-
         /* Keep track of this line so that we can parse it on
          * the next loop iteration.  (In the folded case, last_field
          * has been updated already.)
          */
         if (!folded) {
-            last_field = field;
-            last_len = len;
+            x->last_field = field;
+            x->last_len = len;
         }
+
+        /* We may still be EAGAIN here */
+        if (rv != APR_SUCCESS) {
+            AP_DEBUG_ASSERT(APR_STATUS_IS_EAGAIN(rv));
+            x->fold = 1;
+            return rv;
+        }
+
+        /* Found a blank line, stop. */
+        if (len == 0) {
+            x->done = 1;
+            break;
+        }
     }

     /* Combine multiple message-header fields with the same
      * field-name, following RFC 2616, 4.2.
      */
-    apr_table_compress(r->headers_in, APR_OVERLAP_TABLES_MERGE);
+    apr_table_compress(to, APR_OVERLAP_TABLES_MERGE);

     /* enforce LimitRequestFieldSize for merged headers */
-    apr_table_do(table_do_fn_check_lengths, r, r->headers_in, NULL);
+    apr_table_do(table_do_fn_check_lengths, r, to, NULL);
+
+    return APR_SUCCESS;
 }

+AP_DECLARE(apr_status_t)
+ap_get_mime_headers_from(request_rec *r, apr_bucket_brigade *bb,
+                         ap_filter_t *from, apr_read_type_e block,
+                         apr_table_t *to, ap_mime_headers_ctx_t **ctx)
+{
+    return ap_parse_mime_headers(r, bb, NULL, APR_BLOCK_READ,
+                                 r->headers_in, ctx, NULL, NULL);
+}
+
+AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r,
+                                          apr_bucket_brigade *bb)
+{
+    int status = OK;
+    const char *notes = NULL;
+
+    (void)ap_parse_mime_headers(r, bb, NULL, APR_BLOCK_READ,
+                                r->headers_in, NULL, &status, &notes);
+    if (status != OK) {
+        r->status = status;
+    }
+    if (notes) {
+        apr_table_setn(r->notes, "error-notes", notes);
+    }
+}
+
 AP_DECLARE(void) ap_get_mime_headers(request_rec *r)
 {
     apr_bucket_brigade *tmp_bb;
@@ -1010,9 +1139,11 @@ request_rec *ap_read_request(conn_rec *conn)
     r->allowed_methods = ap_make_method_list(p, 2);

     r->headers_in      = apr_table_make(r->pool, 25);
+    r->trailers_in     = apr_table_make(r->pool, 5);
     r->subprocess_env  = apr_table_make(r->pool, 25);
     r->headers_out     = apr_table_make(r->pool, 12);
     r->err_headers_out = apr_table_make(r->pool, 5);
+    r->trailers_out    = apr_table_make(r->pool, 5);
     r->notes           = apr_table_make(r->pool, 5);

     r->request_config  = ap_create_request_config(r->pool);
@@ -1280,6 +1411,7 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_r
     rnew->status          = HTTP_OK;

     rnew->headers_in      = apr_table_copy(rnew->pool, r->headers_in);
+    rnew->trailers_in     = apr_table_copy(rnew->pool, r->trailers_in);

     /* did the original request have a body?  (e.g. POST w/SSI tags)
      * if so, make sure the subrequest doesn't inherit body headers
@@ -1291,6 +1423,7 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_r
     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->trailers_out    = apr_table_make(rnew->pool, 5);
     rnew->notes           = apr_table_make(rnew->pool, 5);

     rnew->expecting_100   = r->expecting_100;
Index: modules/http/http_filters.c
===================================================================
--- modules/http/http_filters.c    (revision 1536297)
+++ modules/http/http_filters.c    (working copy)
@@ -66,6 +66,8 @@ typedef struct http_filter_ctx
     apr_off_t limit_used;
     apr_int32_t chunk_used;
     apr_int16_t chunkbits;
+    ap_mime_headers_ctx_t *mime_hdrs_ctx;
+    apr_bucket_brigade *tmp_bb;
     enum
     {
         BODY_NONE, /* streamed data */
@@ -384,17 +386,9 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bu

                 apr_bucket_delete(e);
                 e = APR_BRIGADE_FIRST(b);
-                again = 1; /* come around again */
             }

-            if (ctx->state == BODY_CHUNK_TRAILER) {
-                ap_get_mime_headers(f->r);
-                e = apr_bucket_eos_create(f->c->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(b, e);
-                ctx->eos_sent = 1;
-                return APR_SUCCESS;
-            }
-
+            again = 1; /* come around again */
             break;
         }
         case BODY_NONE:
@@ -471,20 +465,30 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bu
         }
         case BODY_CHUNK_TRAILER: {

-            rv = ap_get_brigade(f->next, b, mode, block, readbytes);
-
-            /* for timeout */
-            if (block == APR_NONBLOCK_READ
-                    && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
-                            || (APR_STATUS_IS_EAGAIN(rv)))) {
-                return APR_EAGAIN;
+            if (ctx->tmp_bb == NULL) {
+                ctx->tmp_bb = apr_brigade_create(f->r->pool,
+                                                 f->c->bucket_alloc);
             }
+            rv = ap_get_mime_headers_from(f->r, ctx->tmp_bb,
+                                          f->next, block,
f->r->trailers_in,
+                                          &ctx->mime_hdrs_ctx);
+            apr_brigade_cleanup(ctx->tmp_bb);

             if (rv != APR_SUCCESS) {
                 return rv;
             }

-            break;
+            rv = ap_run_trailer_parser(f->r);
+            if (rv != OK && rv != DECLINED) {
+                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO()
+                        "Invalid chunked trailer part");
+                return APR_EINVAL;
+            }
+
+            e = apr_bucket_eos_create(f->c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(b, e);
+            ctx->eos_sent = 1;
+            return APR_SUCCESS;
         }
         default: {
             break;
Index: modules/http/http_request.c
===================================================================
--- modules/http/http_request.c    (revision 1536297)
+++ modules/http/http_request.c    (working copy)
@@ -463,6 +463,7 @@ static request_rec *internal_internal_redirect(con
     new->main            = r->main;

     new->headers_in      = r->headers_in;
+    new->trailers_in     = r->trailers_in;
     new->headers_out     = apr_table_make(r->pool, 12);
     if (ap_is_HTTP_REDIRECT(new->status)) {
         const char *location = apr_table_get(r->headers_out, "Location");
@@ -470,6 +471,8 @@ static request_rec *internal_internal_redirect(con
             apr_table_setn(new->headers_out, "Location", location);
     }
     new->err_headers_out = r->err_headers_out;
+    new->trailers_out    = r->trailers_out;
+
     new->subprocess_env  = rename_original_env(r->pool, r->subprocess_env);
     new->notes           = apr_table_make(r->pool, 5);

Index: modules/http/chunk_filter.c
===================================================================
--- modules/http/chunk_filter.c    (revision 1536297)
+++ modules/http/chunk_filter.c    (working copy)
@@ -25,6 +25,7 @@
 #include "apr_want.h"

 #include "httpd.h"
+#include "http_log.h"
 #include "http_config.h"
 #include "http_connection.h"
 #include "http_core.h"
@@ -170,18 +171,56 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f,
          * connection to the backend (mod_proxy) broke in the middle of the
          * response. In order to signal the client that something went
wrong
          * we do not create the last-chunk marker and set c->keepalive to
-         * AP_CONN_CLOSE in the core output filter.
+         * AP_CONN_CLOSE in the ap_http_outerror_filter() output filter.
          *
          * XXX: it would be nice to combine this with the end-of-chunk
          * marker above, but this is a bit more straight-forward for
          * now.
          */
         if (eos && !f->ctx) {
-            /* XXX: (2) trailers ... does not yet exist */
+            if (!apr_is_empty_table(f->r->trailers_out)) {
+                const apr_array_header_t *trailers_elts;
+                const apr_table_entry_t *trailers;
+                int counter;
+
+                /* Prevent HTTP Response Splitting */
+                if (APR_BUCKET_NEXT(eos) != APR_BRIGADE_SENTINEL(b)) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO()
+                            "Sent more bytes of response body than
expected");
+                    return APR_EGENERAL;
+                }
+                APR_BUCKET_REMOVE(eos);
+
+                e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF, 3,
+                                               c->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(b, e);
+
+                /* Put the trailers in the output brigade (code stolen from
+                 * ap_proxy_fill_hdrbrgd).
+                 */
+                trailers_elts = apr_table_elts(f->r->trailers_out);
+                trailers = (const apr_table_entry_t *)trailers_elts->elts;
+                for (counter = 0; counter < trailers_elts->nelts;
counter++) {
+                    char *buf = apr_pstrcat(f->r->pool,
trailers[counter].key,
+                                            ": ", trailers[counter].val,
+                                            ASCII_CRLF, NULL);
+                    ap_xlate_proto_to_ascii(buf, strlen(buf));
+                    e = apr_bucket_pool_create(buf, strlen(buf),
f->r->pool,
+                                               c->bucket_alloc);
+                    APR_BRIGADE_INSERT_TAIL(b, e);
+                }
+
+                e = apr_bucket_immortal_create(ASCII_CRLF, 2,
+                                               c->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(b, e);
+
+                APR_BRIGADE_INSERT_TAIL(b, eos);
+            }
+            else {
             e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
-                                           /* <trailers> */
                                            ASCII_CRLF, 5, c->bucket_alloc);
             APR_BUCKET_INSERT_BEFORE(eos, e);
+            }
         }

         /* pass the brigade to the next filter. */
[EOS]

Mime
View raw message