41a6c3
diff --git a/include/http_protocol.h b/include/http_protocol.h
41a6c3
index 415270b..67fa02f 100644
41a6c3
--- a/include/http_protocol.h
41a6c3
+++ b/include/http_protocol.h
41a6c3
@@ -502,6 +502,23 @@ AP_DECLARE(int) ap_should_client_block(request_rec *r);
41a6c3
  */
41a6c3
 AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz);
41a6c3
 
41a6c3
+/*
41a6c3
+ * Map specific APR codes returned by the filter stack to HTTP error
41a6c3
+ * codes, or the default status code provided. Use it as follows:
41a6c3
+ *
41a6c3
+ * return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
41a6c3
+ *
41a6c3
+ * If the filter has already handled the error, AP_FILTER_ERROR will
41a6c3
+ * be returned, which is cleanly passed through.
41a6c3
+ *
41a6c3
+ * These mappings imply that the filter stack is reading from the
41a6c3
+ * downstream client, the proxy will map these codes differently.
41a6c3
+ * @param rv APR status code
41a6c3
+ * @param status Default HTTP code should the APR code not be recognised
41a6c3
+ * @return Mapped HTTP status code
41a6c3
+ */
41a6c3
+AP_DECLARE(int) ap_map_http_request_error(apr_status_t rv, int status);
41a6c3
+
41a6c3
 /**
41a6c3
  * In HTTP/1.1, any method can have a body.  However, most GET handlers
41a6c3
  * wouldn't know what to do with a request body if they received one.
41a6c3
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
41a6c3
index 1dde402..ed8749f 100644
41a6c3
--- a/modules/http/http_filters.c
41a6c3
+++ b/modules/http/http_filters.c
41a6c3
@@ -57,24 +57,29 @@
41a6c3
 
41a6c3
 APLOG_USE_MODULE(http);
41a6c3
 
41a6c3
-#define INVALID_CHAR -2
41a6c3
-
41a6c3
-static long get_chunk_size(char *);
41a6c3
-
41a6c3
-typedef struct http_filter_ctx {
41a6c3
+typedef struct http_filter_ctx
41a6c3
+{
41a6c3
     apr_off_t remaining;
41a6c3
     apr_off_t limit;
41a6c3
     apr_off_t limit_used;
41a6c3
-    enum {
41a6c3
-        BODY_NONE,
41a6c3
-        BODY_LENGTH,
41a6c3
-        BODY_CHUNK,
41a6c3
-        BODY_CHUNK_PART
41a6c3
+    apr_int32_t chunk_used;
41a6c3
+    apr_int32_t chunk_bws;
41a6c3
+    apr_int32_t chunkbits;
41a6c3
+    enum
41a6c3
+    {
41a6c3
+        BODY_NONE, /* streamed data */
41a6c3
+        BODY_LENGTH, /* data constrained by content length */
41a6c3
+        BODY_CHUNK, /* chunk expected */
41a6c3
+        BODY_CHUNK_PART, /* chunk digits */
41a6c3
+        BODY_CHUNK_EXT, /* chunk extension */
41a6c3
+        BODY_CHUNK_CR, /* got space(s) after digits, expect [CR]LF or ext */
41a6c3
+        BODY_CHUNK_LF, /* got CR after digits or ext, expect LF */
41a6c3
+        BODY_CHUNK_DATA, /* data constrained by chunked encoding */
41a6c3
+        BODY_CHUNK_END, /* chunked data terminating CRLF */
41a6c3
+        BODY_CHUNK_END_LF, /* got CR after data, expect LF */
41a6c3
+        BODY_CHUNK_TRAILER /* trailers */
41a6c3
     } state;
41a6c3
-    int eos_sent;
41a6c3
-    char chunk_ln[32];
41a6c3
-    char *pos;
41a6c3
-    apr_off_t linesize;
41a6c3
+    unsigned int eos_sent :1;
41a6c3
     apr_bucket_brigade *bb;
41a6c3
 } http_ctx_t;
41a6c3
 
41a6c3
@@ -87,6 +92,23 @@ static apr_status_t bail_out_on_error(http_ctx_t *ctx,
41a6c3
     apr_bucket_brigade *bb = ctx->bb;
41a6c3
 
41a6c3
     apr_brigade_cleanup(bb);
41a6c3
+
41a6c3
+    if (f->r->proxyreq == PROXYREQ_RESPONSE) {
41a6c3
+        switch (http_error) {
41a6c3
+        case HTTP_REQUEST_ENTITY_TOO_LARGE:
41a6c3
+            return APR_ENOSPC;
41a6c3
+
41a6c3
+        case HTTP_REQUEST_TIME_OUT:
41a6c3
+            return APR_INCOMPLETE;
41a6c3
+
41a6c3
+        case HTTP_NOT_IMPLEMENTED:
41a6c3
+            return APR_ENOTIMPL;
41a6c3
+
41a6c3
+        default:
41a6c3
+            return APR_EGENERAL;
41a6c3
+        }
41a6c3
+    }
41a6c3
+
41a6c3
     e = ap_bucket_error_create(http_error,
41a6c3
                                NULL, f->r->pool,
41a6c3
                                f->c->bucket_alloc);
41a6c3
@@ -102,117 +124,154 @@ static apr_status_t bail_out_on_error(http_ctx_t *ctx,
41a6c3
     return ap_pass_brigade(f->r->output_filters, bb);
41a6c3
 }
41a6c3
 
41a6c3
-static apr_status_t get_remaining_chunk_line(http_ctx_t *ctx,
41a6c3
-                                             apr_bucket_brigade *b,
41a6c3
-                                             int linelimit)
41a6c3
+/**
41a6c3
+ * Parse a chunk line with optional extension, detect overflow.
41a6c3
+ * There are two error cases:
41a6c3
+ *  1) If the conversion would require too many bits, APR_EGENERAL is returned.
41a6c3
+ *  2) If the conversion used the correct number of bits, but an overflow
41a6c3
+ *     caused only the sign bit to flip, then APR_ENOSPC is returned.
41a6c3
+ * In general, any negative number can be considered an overflow error.
41a6c3
+ */
41a6c3
+static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer,
41a6c3
+                                     apr_size_t len, int linelimit)
41a6c3
 {
41a6c3
-    apr_status_t rv;
41a6c3
-    apr_off_t brigade_length;
41a6c3
-    apr_bucket *e;
41a6c3
-    const char *lineend;
41a6c3
-    apr_size_t len = 0;
41a6c3
+    apr_size_t i = 0;
41a6c3
 
41a6c3
-    /*
41a6c3
-     * As the brigade b should have been requested in mode AP_MODE_GETLINE
41a6c3
-     * all buckets in this brigade are already some type of memory
41a6c3
-     * buckets (due to the needed scanning for LF in mode AP_MODE_GETLINE)
41a6c3
-     * or META buckets.
41a6c3
-     */
41a6c3
-    rv = apr_brigade_length(b, 0, &brigade_length);
41a6c3
-    if (rv != APR_SUCCESS) {
41a6c3
-        return rv;
41a6c3
-    }
41a6c3
-    /* Sanity check. Should never happen. See above. */
41a6c3
-    if (brigade_length == -1) {
41a6c3
-        return APR_EGENERAL;
41a6c3
-    }
41a6c3
-    if (!brigade_length) {
41a6c3
-        return APR_EAGAIN;
41a6c3
-    }
41a6c3
-    ctx->linesize += brigade_length;
41a6c3
-    if (ctx->linesize > linelimit) {
41a6c3
-        return APR_ENOSPC;
41a6c3
-    }
41a6c3
-    /*
41a6c3
-     * As all buckets are already some type of memory buckets or META buckets
41a6c3
-     * (see above), we only need to check the last byte in the last data bucket.
41a6c3
-     */
41a6c3
-    for (e = APR_BRIGADE_LAST(b);
41a6c3
-         e != APR_BRIGADE_SENTINEL(b);
41a6c3
-         e = APR_BUCKET_PREV(e)) {
41a6c3
+    while (i < len) {
41a6c3
+        char c = buffer[i];
41a6c3
+
41a6c3
+        ap_xlate_proto_from_ascii(&c, 1);
41a6c3
 
41a6c3
-        if (APR_BUCKET_IS_METADATA(e)) {
41a6c3
+        /* handle CRLF after the chunk */
41a6c3
+        if (ctx->state == BODY_CHUNK_END
41a6c3
+                || ctx->state == BODY_CHUNK_END_LF) {
41a6c3
+            if (c == LF) {
41a6c3
+                ctx->state = BODY_CHUNK;
41a6c3
+            }
41a6c3
+            else if (c == CR && ctx->state == BODY_CHUNK_END) {
41a6c3
+                ctx->state = BODY_CHUNK_END_LF;
41a6c3
+            }
41a6c3
+            else {
41a6c3
+                /*
41a6c3
+                 * LF expected.
41a6c3
+                 */
41a6c3
+                return APR_EINVAL;
41a6c3
+            }
41a6c3
+            i++;
41a6c3
             continue;
41a6c3
         }
41a6c3
-        rv = apr_bucket_read(e, &lineend, &len, APR_BLOCK_READ);
41a6c3
-        if (rv != APR_SUCCESS) {
41a6c3
-            return rv;
41a6c3
+
41a6c3
+        /* handle start of the chunk */
41a6c3
+        if (ctx->state == BODY_CHUNK) {
41a6c3
+            if (!apr_isxdigit(c)) {
41a6c3
+                /*
41a6c3
+                 * Detect invalid character at beginning. This also works for
41a6c3
+                 * empty chunk size lines.
41a6c3
+                 */
41a6c3
+                return APR_EINVAL;
41a6c3
+            }
41a6c3
+            else {
41a6c3
+                ctx->state = BODY_CHUNK_PART;
41a6c3
+            }
41a6c3
+            ctx->remaining = 0;
41a6c3
+            ctx->chunkbits = sizeof(apr_off_t) * 8;
41a6c3
+            ctx->chunk_used = 0;
41a6c3
+            ctx->chunk_bws = 0;
41a6c3
         }
41a6c3
-        if (len > 0) {
41a6c3
-            break;  /* we got the data we want */
41a6c3
+
41a6c3
+        if (c == LF) {
41a6c3
+            if (ctx->remaining) {
41a6c3
+                ctx->state = BODY_CHUNK_DATA;
41a6c3
+            }
41a6c3
+            else {
41a6c3
+                ctx->state = BODY_CHUNK_TRAILER;
41a6c3
+            }
41a6c3
         }
41a6c3
-        /* If we got a zero-length data bucket, we try the next one */
41a6c3
-    }
41a6c3
-    /* We had no data in this brigade */
41a6c3
-    if (!len || e == APR_BRIGADE_SENTINEL(b)) {
41a6c3
-        return APR_EAGAIN;
41a6c3
-    }
41a6c3
-    if (lineend[len - 1] != APR_ASCII_LF) {
41a6c3
-        return APR_EAGAIN;
41a6c3
-    }
41a6c3
-    /* Line is complete. So reset ctx for next round. */
41a6c3
-    ctx->linesize = 0;
41a6c3
-    ctx->pos = ctx->chunk_ln;
41a6c3
-    return APR_SUCCESS;
41a6c3
-}
41a6c3
+        else if (ctx->state == BODY_CHUNK_LF) {
41a6c3
+            /*
41a6c3
+             * LF expected.
41a6c3
+             */
41a6c3
+            return APR_EINVAL;
41a6c3
+        }
41a6c3
+        else if (c == CR) {
41a6c3
+            ctx->state = BODY_CHUNK_LF;
41a6c3
+        }
41a6c3
+        else if (c == ';') {
41a6c3
+            ctx->state = BODY_CHUNK_EXT;
41a6c3
+        }
41a6c3
+        else if (ctx->state == BODY_CHUNK_EXT) {
41a6c3
+            /*
41a6c3
+             * Control chars (but tabs) are invalid.
41a6c3
+             */
41a6c3
+            if (c != '\t' && apr_iscntrl(c)) {
41a6c3
+                return APR_EINVAL;
41a6c3
+            }
41a6c3
+        }
41a6c3
+        else if (c == ' ' || c == '\t') {
41a6c3
+            /* Be lenient up to 10 BWS (term from rfc7230 - 3.2.3).
41a6c3
+             */
41a6c3
+            ctx->state = BODY_CHUNK_CR;
41a6c3
+            if (++ctx->chunk_bws > 10) {
41a6c3
+                return APR_EINVAL;
41a6c3
+            }
41a6c3
+        }
41a6c3
+        else if (ctx->state == BODY_CHUNK_CR) {
41a6c3
+            /*
41a6c3
+             * ';', CR or LF expected.
41a6c3
+             */
41a6c3
+            return APR_EINVAL;
41a6c3
+        }
41a6c3
+        else if (ctx->state == BODY_CHUNK_PART) {
41a6c3
+            int xvalue;
41a6c3
 
41a6c3
-static apr_status_t get_chunk_line(http_ctx_t *ctx, apr_bucket_brigade *b,
41a6c3
-                                   int linelimit)
41a6c3
-{
41a6c3
-    apr_size_t len;
41a6c3
-    int tmp_len;
41a6c3
-    apr_status_t rv;
41a6c3
+            /* ignore leading zeros */
41a6c3
+            if (!ctx->remaining && c == '0') {
41a6c3
+                i++;
41a6c3
+                continue;
41a6c3
+            }
41a6c3
 
41a6c3
-    tmp_len = sizeof(ctx->chunk_ln) - (ctx->pos - ctx->chunk_ln) - 1;
41a6c3
-    /* Saveguard ourselves against underflows */
41a6c3
-    if (tmp_len < 0) {
41a6c3
-        len = 0;
41a6c3
-    }
41a6c3
-    else {
41a6c3
-        len = (apr_size_t) tmp_len;
41a6c3
-    }
41a6c3
-    /*
41a6c3
-     * Check if there is space left in ctx->chunk_ln. If not, then either
41a6c3
-     * the chunk size is insane or we have chunk-extensions. Ignore both
41a6c3
-     * by discarding the remaining part of the line via
41a6c3
-     * get_remaining_chunk_line. Only bail out if the line is too long.
41a6c3
-     */
41a6c3
-    if (len > 0) {
41a6c3
-        rv = apr_brigade_flatten(b, ctx->pos, &len;;
41a6c3
-        if (rv != APR_SUCCESS) {
41a6c3
-            return rv;
41a6c3
+            ctx->chunkbits -= 4;
41a6c3
+            if (ctx->chunkbits < 0) {
41a6c3
+                /* overflow */
41a6c3
+                return APR_ENOSPC;
41a6c3
+            }
41a6c3
+
41a6c3
+            if (c >= '0' && c <= '9') {
41a6c3
+                xvalue = c - '0';
41a6c3
+            }
41a6c3
+            else if (c >= 'A' && c <= 'F') {
41a6c3
+                xvalue = c - 'A' + 0xa;
41a6c3
+            }
41a6c3
+            else if (c >= 'a' && c <= 'f') {
41a6c3
+                xvalue = c - 'a' + 0xa;
41a6c3
+            }
41a6c3
+            else {
41a6c3
+                /* bogus character */
41a6c3
+                return APR_EINVAL;
41a6c3
+            }
41a6c3
+
41a6c3
+            ctx->remaining = (ctx->remaining << 4) | xvalue;
41a6c3
+            if (ctx->remaining < 0) {
41a6c3
+                /* overflow */
41a6c3
+                return APR_ENOSPC;
41a6c3
+            }
41a6c3
         }
41a6c3
-        ctx->pos += len;
41a6c3
-        ctx->linesize += len;
41a6c3
-        *(ctx->pos) = '\0';
41a6c3
-        /*
41a6c3
-         * Check if we really got a full line. If yes the
41a6c3
-         * last char in the just read buffer must be LF.
41a6c3
-         * If not advance the buffer and return APR_EAGAIN.
41a6c3
-         * We do not start processing until we have the
41a6c3
-         * full line.
41a6c3
-         */
41a6c3
-        if (ctx->pos[-1] != APR_ASCII_LF) {
41a6c3
-            /* Check if the remaining data in the brigade has the LF */
41a6c3
-            return get_remaining_chunk_line(ctx, b, linelimit);
41a6c3
+        else {
41a6c3
+            /* Should not happen */
41a6c3
+            return APR_EGENERAL;
41a6c3
         }
41a6c3
-        /* Line is complete. So reset ctx->pos for next round. */
41a6c3
-        ctx->pos = ctx->chunk_ln;
41a6c3
-        return APR_SUCCESS;
41a6c3
+
41a6c3
+        i++;
41a6c3
     }
41a6c3
-    return get_remaining_chunk_line(ctx, b, linelimit);
41a6c3
-}
41a6c3
 
41a6c3
+    /* sanity check */
41a6c3
+    ctx->chunk_used += len;
41a6c3
+    if (ctx->chunk_used < 0 || ctx->chunk_used > linelimit) {
41a6c3
+        return APR_ENOSPC;
41a6c3
+    }
41a6c3
+
41a6c3
+    return APR_SUCCESS;
41a6c3
+}
41a6c3
 
41a6c3
 static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
41a6c3
                                           apr_bucket_brigade *b, int merge)
41a6c3
@@ -226,7 +285,6 @@ static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
41a6c3
     r->status = HTTP_OK;
41a6c3
     r->headers_in = r->trailers_in;
41a6c3
     apr_table_clear(r->headers_in);
41a6c3
-    ctx->state = BODY_NONE;
41a6c3
     ap_get_mime_headers(r);
41a6c3
 
41a6c3
     if(r->status == HTTP_OK) {
41a6c3
@@ -239,7 +297,7 @@ static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
41a6c3
     else {
41a6c3
         const char *error_notes = apr_table_get(r->notes,
41a6c3
                                                 "error-notes");
41a6c3
-        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
41a6c3
+        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02656)
41a6c3
                       "Error while reading HTTP trailer: %i%s%s",
41a6c3
                       r->status, error_notes ? ": " : "",
41a6c3
                       error_notes ? error_notes : "");
41a6c3
@@ -270,9 +328,9 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
41a6c3
     apr_bucket *e;
41a6c3
     http_ctx_t *ctx = f->ctx;
41a6c3
     apr_status_t rv;
41a6c3
-    apr_off_t totalread;
41a6c3
     int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE;
41a6c3
     apr_bucket_brigade *bb;
41a6c3
+    int again;
41a6c3
 
41a6c3
     conf = (core_server_config *)
41a6c3
         ap_get_module_config(f->r->server->module_config, &core_module);
41a6c3
@@ -286,7 +344,6 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
41a6c3
         const char *tenc, *lenp;
41a6c3
         f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
41a6c3
         ctx->state = BODY_NONE;
41a6c3
-        ctx->pos = ctx->chunk_ln;
41a6c3
         ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
41a6c3
         bb = ctx->bb;
41a6c3
 
41a6c3
@@ -306,25 +363,33 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
41a6c3
         lenp = apr_table_get(f->r->headers_in, "Content-Length");
41a6c3
 
41a6c3
         if (tenc) {
41a6c3
-            if (!strcasecmp(tenc, "chunked")) {
41a6c3
+            if (strcasecmp(tenc, "chunked") == 0 /* fast path */
41a6c3
+                    || ap_find_last_token(f->r->pool, tenc, "chunked")) {
41a6c3
                 ctx->state = BODY_CHUNK;
41a6c3
             }
41a6c3
-            /* test lenp, because it gives another case we can handle */
41a6c3
-            else if (!lenp) {
41a6c3
-                /* Something that isn't in HTTP, unless some future
41a6c3
+            else if (f->r->proxyreq == PROXYREQ_RESPONSE) {
41a6c3
+                /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
41a6c3
+                 * Section 3.3.3.3: "If a Transfer-Encoding header field is
41a6c3
+                 * present in a response and the chunked transfer coding is not
41a6c3
+                 * the final encoding, the message body length is determined by
41a6c3
+                 * reading the connection until it is closed by the server."
41a6c3
+                 */
41a6c3
+                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(02555)
41a6c3
+                              "Unknown Transfer-Encoding: %s; "
41a6c3
+                              "using read-until-close", tenc);
41a6c3
+                tenc = NULL;
41a6c3
+            }
41a6c3
+            else {
41a6c3
+                /* Something that isn't a HTTP request, unless some future
41a6c3
                  * edition defines new transfer encodings, is unsupported.
41a6c3
                  */
41a6c3
                 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01585)
41a6c3
                               "Unknown Transfer-Encoding: %s", tenc);
41a6c3
-                return bail_out_on_error(ctx, f, HTTP_NOT_IMPLEMENTED);
41a6c3
-            }
41a6c3
-            else {
41a6c3
-                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(01586)
41a6c3
-                  "Unknown Transfer-Encoding: %s; using Content-Length", tenc);
41a6c3
-                tenc = NULL;
41a6c3
+                return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST);
41a6c3
             }
41a6c3
+            lenp = NULL;
41a6c3
         }
41a6c3
-        if (lenp && !tenc) {
41a6c3
+        if (lenp) {
41a6c3
             char *endstr;
41a6c3
 
41a6c3
             ctx->state = BODY_LENGTH;
41a6c3
@@ -339,7 +404,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
41a6c3
                 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01587)
41a6c3
                               "Invalid Content-Length");
41a6c3
 
41a6c3
-                return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
41a6c3
+                return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST);
41a6c3
             }
41a6c3
 
41a6c3
             /* If we have a limit in effect and we know the C-L ahead of
41a6c3
@@ -381,7 +446,8 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
41a6c3
             if (!ap_is_HTTP_SUCCESS(f->r->status)) {
41a6c3
                 ctx->state = BODY_NONE;
41a6c3
                 ctx->eos_sent = 1;
41a6c3
-            } else {
41a6c3
+            }
41a6c3
+            else {
41a6c3
                 char *tmp;
41a6c3
                 int len;
41a6c3
 
41a6c3
@@ -389,7 +455,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
41a6c3
                  * in a state of expecting one.
41a6c3
                  */
41a6c3
                 f->r->expecting_100 = 0;
41a6c3
-                tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ",
41a6c3
+                tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL " ",
41a6c3
                                   ap_get_status_line(HTTP_CONTINUE), CRLF CRLF,
41a6c3
                                   NULL);
41a6c3
                 len = strlen(tmp);
41a6c3
@@ -401,279 +467,205 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
41a6c3
                 e = apr_bucket_flush_create(f->c->bucket_alloc);
41a6c3
                 APR_BRIGADE_INSERT_TAIL(bb, e);
41a6c3
 
41a6c3
-                ap_pass_brigade(f->c->output_filters, bb);
41a6c3
-            }
41a6c3
-        }
41a6c3
-
41a6c3
-        /* We can't read the chunk until after sending 100 if required. */
41a6c3
-        if (ctx->state == BODY_CHUNK) {
41a6c3
-            apr_brigade_cleanup(bb);
41a6c3
-
41a6c3
-            rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
41a6c3
-                                block, 0);
41a6c3
-
41a6c3
-            /* for timeout */
41a6c3
-            if (block == APR_NONBLOCK_READ &&
41a6c3
-                ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
41a6c3
-                  (APR_STATUS_IS_EAGAIN(rv)) )) {
41a6c3
-                ctx->state = BODY_CHUNK_PART;
41a6c3
-                return APR_EAGAIN;
41a6c3
-            }
41a6c3
-
41a6c3
-            if (rv == APR_SUCCESS) {
41a6c3
-                rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line);
41a6c3
-                if (APR_STATUS_IS_EAGAIN(rv)) {
41a6c3
-                    apr_brigade_cleanup(bb);
41a6c3
-                    ctx->state = BODY_CHUNK_PART;
41a6c3
-                    return rv;
41a6c3
-                }
41a6c3
-                if (rv == APR_SUCCESS) {
41a6c3
-                    ctx->remaining = get_chunk_size(ctx->chunk_ln);
41a6c3
-                    if (ctx->remaining == INVALID_CHAR) {
41a6c3
-                        rv = APR_EGENERAL;
41a6c3
-                        http_error = HTTP_BAD_REQUEST;
41a6c3
-                    }
41a6c3
+                rv = ap_pass_brigade(f->c->output_filters, bb);
41a6c3
+                if (rv != APR_SUCCESS) {
41a6c3
+                    return AP_FILTER_ERROR;
41a6c3
                 }
41a6c3
             }
41a6c3
-            apr_brigade_cleanup(bb);
41a6c3
-
41a6c3
-            /* Detect chunksize error (such as overflow) */
41a6c3
-            if (rv != APR_SUCCESS || ctx->remaining < 0) {
41a6c3
-                ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, APLOGNO(01589) "Error reading first chunk %s ",
41a6c3
-                              (ctx->remaining < 0) ? "(overflow)" : "");
41a6c3
-                ctx->remaining = 0; /* Reset it in case we have to
41a6c3
-                                     * come back here later */
41a6c3
-                if (APR_STATUS_IS_TIMEUP(rv)) {
41a6c3
-                    http_error = HTTP_REQUEST_TIME_OUT;
41a6c3
-                }
41a6c3
-                return bail_out_on_error(ctx, f, http_error);
41a6c3
-            }
41a6c3
-
41a6c3
-            if (!ctx->remaining) {
41a6c3
-                return read_chunked_trailers(ctx, f, b,
41a6c3
-                        conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
41a6c3
-            }
41a6c3
         }
41a6c3
     }
41a6c3
-    else {
41a6c3
-        bb = ctx->bb;
41a6c3
-    }
41a6c3
 
41a6c3
+    /* sanity check in case we're read twice */
41a6c3
     if (ctx->eos_sent) {
41a6c3
         e = apr_bucket_eos_create(f->c->bucket_alloc);
41a6c3
         APR_BRIGADE_INSERT_TAIL(b, e);
41a6c3
         return APR_SUCCESS;
41a6c3
     }
41a6c3
 
41a6c3
-    if (!ctx->remaining) {
41a6c3
+    do {
41a6c3
+        apr_brigade_cleanup(b);
41a6c3
+        again = 0; /* until further notice */
41a6c3
+
41a6c3
+        /* read and handle the brigade */
41a6c3
         switch (ctx->state) {
41a6c3
-        case BODY_NONE:
41a6c3
-            break;
41a6c3
-        case BODY_LENGTH:
41a6c3
-            e = apr_bucket_eos_create(f->c->bucket_alloc);
41a6c3
-            APR_BRIGADE_INSERT_TAIL(b, e);
41a6c3
-            ctx->eos_sent = 1;
41a6c3
-            return APR_SUCCESS;
41a6c3
         case BODY_CHUNK:
41a6c3
         case BODY_CHUNK_PART:
41a6c3
-            {
41a6c3
-                apr_brigade_cleanup(bb);
41a6c3
+        case BODY_CHUNK_EXT:
41a6c3
+        case BODY_CHUNK_CR:
41a6c3
+        case BODY_CHUNK_LF:
41a6c3
+        case BODY_CHUNK_END:
41a6c3
+        case BODY_CHUNK_END_LF: {
41a6c3
 
41a6c3
-                /* We need to read the CRLF after the chunk.  */
41a6c3
-                if (ctx->state == BODY_CHUNK) {
41a6c3
-                    rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
41a6c3
-                                        block, 0);
41a6c3
-                    if (block == APR_NONBLOCK_READ &&
41a6c3
-                        ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
41a6c3
-                          (APR_STATUS_IS_EAGAIN(rv)) )) {
41a6c3
-                        return APR_EAGAIN;
41a6c3
-                    }
41a6c3
-                    /* If we get an error, then leave */
41a6c3
-                    if (rv != APR_SUCCESS) {
41a6c3
-                        return rv;
41a6c3
-                    }
41a6c3
-                    /*
41a6c3
-                     * We really don't care whats on this line. If it is RFC
41a6c3
-                     * compliant it should be only \r\n. If there is more
41a6c3
-                     * before we just ignore it as long as we do not get over
41a6c3
-                     * the limit for request lines.
41a6c3
-                     */
41a6c3
-                    rv = get_remaining_chunk_line(ctx, bb,
41a6c3
-                                                  f->r->server->limit_req_line);
41a6c3
-                    apr_brigade_cleanup(bb);
41a6c3
-                    if (APR_STATUS_IS_EAGAIN(rv)) {
41a6c3
-                        return rv;
41a6c3
-                    }
41a6c3
-                } else {
41a6c3
-                    rv = APR_SUCCESS;
41a6c3
-                }
41a6c3
+            rv = ap_get_brigade(f->next, b, AP_MODE_GETLINE, block, 0);
41a6c3
+
41a6c3
+            /* for timeout */
41a6c3
+            if (block == APR_NONBLOCK_READ
41a6c3
+                    && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
41a6c3
+                            || (APR_STATUS_IS_EAGAIN(rv)))) {
41a6c3
+                return APR_EAGAIN;
41a6c3
+            }
41a6c3
+
41a6c3
+            if (rv == APR_EOF) {
41a6c3
+                return APR_INCOMPLETE;
41a6c3
+            }
41a6c3
+
41a6c3
+            if (rv != APR_SUCCESS) {
41a6c3
+                return rv;
41a6c3
+            }
41a6c3
+
41a6c3
+            e = APR_BRIGADE_FIRST(b);
41a6c3
+            while (e != APR_BRIGADE_SENTINEL(b)) {
41a6c3
+                const char *buffer;
41a6c3
+                apr_size_t len;
41a6c3
+
41a6c3
+                if (!APR_BUCKET_IS_METADATA(e)) {
41a6c3
+                    int parsing = 0;
41a6c3
+
41a6c3
+                    rv = apr_bucket_read(e, &buffer, &len, APR_BLOCK_READ);
41a6c3
 
41a6c3
-                if (rv == APR_SUCCESS) {
41a6c3
-                    /* Read the real chunk line. */
41a6c3
-                    rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
41a6c3
-                                        block, 0);
41a6c3
-                    /* Test timeout */
41a6c3
-                    if (block == APR_NONBLOCK_READ &&
41a6c3
-                        ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
41a6c3
-                          (APR_STATUS_IS_EAGAIN(rv)) )) {
41a6c3
-                        ctx->state = BODY_CHUNK_PART;
41a6c3
-                        return APR_EAGAIN;
41a6c3
-                    }
41a6c3
-                    ctx->state = BODY_CHUNK;
41a6c3
                     if (rv == APR_SUCCESS) {
41a6c3
-                        rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line);
41a6c3
-                        if (APR_STATUS_IS_EAGAIN(rv)) {
41a6c3
-                            ctx->state = BODY_CHUNK_PART;
41a6c3
-                            apr_brigade_cleanup(bb);
41a6c3
-                            return rv;
41a6c3
-                        }
41a6c3
-                        if (rv == APR_SUCCESS) {
41a6c3
-                            ctx->remaining = get_chunk_size(ctx->chunk_ln);
41a6c3
-                            if (ctx->remaining == INVALID_CHAR) {
41a6c3
-                                rv = APR_EGENERAL;
41a6c3
+                        parsing = 1;
41a6c3
+                        rv = parse_chunk_size(ctx, buffer, len,
41a6c3
+                                f->r->server->limit_req_fieldsize);
41a6c3
+                    }
41a6c3
+                    if (rv != APR_SUCCESS) {
41a6c3
+                        ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, APLOGNO(01590)
41a6c3
+                                      "Error reading/parsing chunk %s ",
41a6c3
+                                      (APR_ENOSPC == rv) ? "(overflow)" : "");
41a6c3
+                        if (parsing) {
41a6c3
+                            if (rv != APR_ENOSPC) {
41a6c3
                                 http_error = HTTP_BAD_REQUEST;
41a6c3
                             }
41a6c3
+                            return bail_out_on_error(ctx, f, http_error);
41a6c3
                         }
41a6c3
+                        return rv;
41a6c3
                     }
41a6c3
-                    apr_brigade_cleanup(bb);
41a6c3
                 }
41a6c3
 
41a6c3
-                /* Detect chunksize error (such as overflow) */
41a6c3
-                if (rv != APR_SUCCESS || ctx->remaining < 0) {
41a6c3
-                    ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, APLOGNO(01590) "Error reading chunk %s ",
41a6c3
-                                  (ctx->remaining < 0) ? "(overflow)" : "");
41a6c3
-                    ctx->remaining = 0; /* Reset it in case we have to
41a6c3
-                                         * come back here later */
41a6c3
-                    if (APR_STATUS_IS_TIMEUP(rv)) {
41a6c3
-                        http_error = HTTP_REQUEST_TIME_OUT;
41a6c3
-                    }
41a6c3
-                    return bail_out_on_error(ctx, f, http_error);
41a6c3
-                }
41a6c3
+                apr_bucket_delete(e);
41a6c3
+                e = APR_BRIGADE_FIRST(b);
41a6c3
+            }
41a6c3
+            again = 1; /* come around again */
41a6c3
 
41a6c3
-                if (!ctx->remaining) {
41a6c3
-                    return read_chunked_trailers(ctx, f, b,
41a6c3
+            if (ctx->state == BODY_CHUNK_TRAILER) {
41a6c3
+                /* Treat UNSET as DISABLE - trailers aren't merged by default */
41a6c3
+                return read_chunked_trailers(ctx, f, b,
41a6c3
                             conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
41a6c3
-                }
41a6c3
             }
41a6c3
+
41a6c3
             break;
41a6c3
         }
41a6c3
-    }
41a6c3
+        case BODY_NONE:
41a6c3
+        case BODY_LENGTH:
41a6c3
+        case BODY_CHUNK_DATA: {
41a6c3
 
41a6c3
-    /* Ensure that the caller can not go over our boundary point. */
41a6c3
-    if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) {
41a6c3
-        if (ctx->remaining < readbytes) {
41a6c3
-            readbytes = ctx->remaining;
41a6c3
-        }
41a6c3
-        AP_DEBUG_ASSERT(readbytes > 0);
41a6c3
-    }
41a6c3
+            /* Ensure that the caller can not go over our boundary point. */
41a6c3
+            if (ctx->state != BODY_NONE && ctx->remaining < readbytes) {
41a6c3
+                readbytes = ctx->remaining;
41a6c3
+            }
41a6c3
+            if (readbytes > 0) {
41a6c3
+                apr_off_t totalread;
41a6c3
 
41a6c3
-    rv = ap_get_brigade(f->next, b, mode, block, readbytes);
41a6c3
+                rv = ap_get_brigade(f->next, b, mode, block, readbytes);
41a6c3
 
41a6c3
-    if (rv != APR_SUCCESS) {
41a6c3
-        return rv;
41a6c3
-    }
41a6c3
+                /* for timeout */
41a6c3
+                if (block == APR_NONBLOCK_READ
41a6c3
+                        && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
41a6c3
+                                || (APR_STATUS_IS_EAGAIN(rv)))) {
41a6c3
+                    return APR_EAGAIN;
41a6c3
+                }
41a6c3
 
41a6c3
-    /* How many bytes did we just read? */
41a6c3
-    apr_brigade_length(b, 0, &totalread);
41a6c3
+                if (rv == APR_EOF && ctx->state != BODY_NONE
41a6c3
+                        && ctx->remaining > 0) {
41a6c3
+                    return APR_INCOMPLETE;
41a6c3
+                }
41a6c3
 
41a6c3
-    /* If this happens, we have a bucket of unknown length.  Die because
41a6c3
-     * it means our assumptions have changed. */
41a6c3
-    AP_DEBUG_ASSERT(totalread >= 0);
41a6c3
+                if (rv != APR_SUCCESS) {
41a6c3
+                    return rv;
41a6c3
+                }
41a6c3
 
41a6c3
-    if (ctx->state != BODY_NONE) {
41a6c3
-        ctx->remaining -= totalread;
41a6c3
-        if (ctx->remaining > 0) {
41a6c3
-            e = APR_BRIGADE_LAST(b);
41a6c3
-            if (APR_BUCKET_IS_EOS(e))
41a6c3
-                return APR_EOF;
41a6c3
-        }
41a6c3
-    }
41a6c3
+                /* How many bytes did we just read? */
41a6c3
+                apr_brigade_length(b, 0, &totalread);
41a6c3
 
41a6c3
-    /* If we have no more bytes remaining on a C-L request,
41a6c3
-     * save the callter a roundtrip to discover EOS.
41a6c3
-     */
41a6c3
-    if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
41a6c3
-        e = apr_bucket_eos_create(f->c->bucket_alloc);
41a6c3
-        APR_BRIGADE_INSERT_TAIL(b, e);
41a6c3
-    }
41a6c3
+                /* If this happens, we have a bucket of unknown length.  Die because
41a6c3
+                 * it means our assumptions have changed. */
41a6c3
+                AP_DEBUG_ASSERT(totalread >= 0);
41a6c3
 
41a6c3
-    /* We have a limit in effect. */
41a6c3
-    if (ctx->limit) {
41a6c3
-        /* FIXME: Note that we might get slightly confused on chunked inputs
41a6c3
-         * as we'd need to compensate for the chunk lengths which may not
41a6c3
-         * really count.  This seems to be up for interpretation.  */
41a6c3
-        ctx->limit_used += totalread;
41a6c3
-        if (ctx->limit < ctx->limit_used) {
41a6c3
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01591)
41a6c3
-                          "Read content-length of %" APR_OFF_T_FMT
41a6c3
-                          " is larger than the configured limit"
41a6c3
-                          " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
41a6c3
-            apr_brigade_cleanup(bb);
41a6c3
-            e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
41a6c3
-                                       f->r->pool,
41a6c3
-                                       f->c->bucket_alloc);
41a6c3
-            APR_BRIGADE_INSERT_TAIL(bb, e);
41a6c3
-            e = apr_bucket_eos_create(f->c->bucket_alloc);
41a6c3
-            APR_BRIGADE_INSERT_TAIL(bb, e);
41a6c3
-            ctx->eos_sent = 1;
41a6c3
-            return ap_pass_brigade(f->r->output_filters, bb);
41a6c3
-        }
41a6c3
-    }
41a6c3
+                if (ctx->state != BODY_NONE) {
41a6c3
+                    ctx->remaining -= totalread;
41a6c3
+                    if (ctx->remaining > 0) {
41a6c3
+                        e = APR_BRIGADE_LAST(b);
41a6c3
+                        if (APR_BUCKET_IS_EOS(e)) {
41a6c3
+                            apr_bucket_delete(e);
41a6c3
+                            return APR_INCOMPLETE;
41a6c3
+                        }
41a6c3
+                    }
41a6c3
+                    else if (ctx->state == BODY_CHUNK_DATA) {
41a6c3
+                        /* next chunk please */
41a6c3
+                        ctx->state = BODY_CHUNK_END;
41a6c3
+                        ctx->chunk_used = 0;
41a6c3
+                    }
41a6c3
+                }
41a6c3
 
41a6c3
-    return APR_SUCCESS;
41a6c3
-}
41a6c3
+                /* We have a limit in effect. */
41a6c3
+                if (ctx->limit) {
41a6c3
+                    /* FIXME: Note that we might get slightly confused on
41a6c3
+                     * chunked inputs as we'd need to compensate for the chunk
41a6c3
+                     * lengths which may not really count.  This seems to be up
41a6c3
+                     * for interpretation.
41a6c3
+                     */
41a6c3
+                    ctx->limit_used += totalread;
41a6c3
+                    if (ctx->limit < ctx->limit_used) {
41a6c3
+                        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
41a6c3
+                                      APLOGNO(01591) "Read content length of "
41a6c3
+                                      "%" APR_OFF_T_FMT " is larger than the "
41a6c3
+                                      "configured limit of %" APR_OFF_T_FMT,
41a6c3
+                                      ctx->limit_used, ctx->limit);
41a6c3
+                        return bail_out_on_error(ctx, f,
41a6c3
+                                                 HTTP_REQUEST_ENTITY_TOO_LARGE);
41a6c3
+                    }
41a6c3
+                }
41a6c3
+            }
41a6c3
 
41a6c3
-/**
41a6c3
- * Parse a chunk extension, detect overflow.
41a6c3
- * There are two error cases:
41a6c3
- *  1) If the conversion would require too many bits, a -1 is returned.
41a6c3
- *  2) If the conversion used the correct number of bits, but an overflow
41a6c3
- *     caused only the sign bit to flip, then that negative number is
41a6c3
- *     returned.
41a6c3
- * In general, any negative number can be considered an overflow error.
41a6c3
- */
41a6c3
-static long get_chunk_size(char *b)
41a6c3
-{
41a6c3
-    long chunksize = 0;
41a6c3
-    size_t chunkbits = sizeof(long) * 8;
41a6c3
+            /* If we have no more bytes remaining on a C-L request,
41a6c3
+             * save the caller a round trip to discover EOS.
41a6c3
+             */
41a6c3
+            if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
41a6c3
+                e = apr_bucket_eos_create(f->c->bucket_alloc);
41a6c3
+                APR_BRIGADE_INSERT_TAIL(b, e);
41a6c3
+                ctx->eos_sent = 1;
41a6c3
+            }
41a6c3
 
41a6c3
-    ap_xlate_proto_from_ascii(b, strlen(b));
41a6c3
+            break;
41a6c3
+        }
41a6c3
+        case BODY_CHUNK_TRAILER: {
41a6c3
 
41a6c3
-    if (!apr_isxdigit(*b)) {
41a6c3
-        /*
41a6c3
-         * Detect invalid character at beginning. This also works for empty
41a6c3
-         * chunk size lines.
41a6c3
-         */
41a6c3
-        return INVALID_CHAR;
41a6c3
-    }
41a6c3
-    /* Skip leading zeros */
41a6c3
-    while (*b == '0') {
41a6c3
-        ++b;
41a6c3
-    }
41a6c3
+            rv = ap_get_brigade(f->next, b, mode, block, readbytes);
41a6c3
 
41a6c3
-    while (apr_isxdigit(*b) && (chunkbits > 0)) {
41a6c3
-        int xvalue = 0;
41a6c3
+            /* for timeout */
41a6c3
+            if (block == APR_NONBLOCK_READ
41a6c3
+                    && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
41a6c3
+                            || (APR_STATUS_IS_EAGAIN(rv)))) {
41a6c3
+                return APR_EAGAIN;
41a6c3
+            }
41a6c3
+
41a6c3
+            if (rv != APR_SUCCESS) {
41a6c3
+                return rv;
41a6c3
+            }
41a6c3
 
41a6c3
-        if (*b >= '0' && *b <= '9') {
41a6c3
-            xvalue = *b - '0';
41a6c3
+            break;
41a6c3
         }
41a6c3
-        else if (*b >= 'A' && *b <= 'F') {
41a6c3
-            xvalue = *b - 'A' + 0xa;
41a6c3
+        default: {
41a6c3
+            /* Should not happen */
41a6c3
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(02901)
41a6c3
+                          "Unexpected body state (%i)", (int)ctx->state);
41a6c3
+            return APR_EGENERAL;
41a6c3
         }
41a6c3
-        else if (*b >= 'a' && *b <= 'f') {
41a6c3
-            xvalue = *b - 'a' + 0xa;
41a6c3
         }
41a6c3
 
41a6c3
-        chunksize = (chunksize << 4) | xvalue;
41a6c3
-        chunkbits -= 4;
41a6c3
-        ++b;
41a6c3
-    }
41a6c3
-    if (apr_isxdigit(*b)) {
41a6c3
-        /* overflow */
41a6c3
-        return -1;
41a6c3
-    }
41a6c3
+    } while (again);
41a6c3
 
41a6c3
-    return chunksize;
41a6c3
+    return APR_SUCCESS;
41a6c3
 }
41a6c3
 
41a6c3
 typedef struct header_struct {
41a6c3
@@ -1385,6 +1377,39 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
41a6c3
     return ap_pass_brigade(f->next, b);
41a6c3
 }
41a6c3
 
41a6c3
+/*
41a6c3
+ * Map specific APR codes returned by the filter stack to HTTP error
41a6c3
+ * codes, or the default status code provided. Use it as follows:
41a6c3
+ *
41a6c3
+ * return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
41a6c3
+ *
41a6c3
+ * If the filter has already handled the error, AP_FILTER_ERROR will
41a6c3
+ * be returned, which is cleanly passed through.
41a6c3
+ *
41a6c3
+ * These mappings imply that the filter stack is reading from the
41a6c3
+ * downstream client, the proxy will map these codes differently.
41a6c3
+ */
41a6c3
+AP_DECLARE(int) ap_map_http_request_error(apr_status_t rv, int status)
41a6c3
+{
41a6c3
+    switch (rv) {
41a6c3
+    case AP_FILTER_ERROR: {
41a6c3
+        return AP_FILTER_ERROR;
41a6c3
+    }
41a6c3
+    case APR_ENOSPC: {
41a6c3
+        return HTTP_REQUEST_ENTITY_TOO_LARGE;
41a6c3
+    }
41a6c3
+    case APR_ENOTIMPL: {
41a6c3
+        return HTTP_NOT_IMPLEMENTED;
41a6c3
+    }
41a6c3
+    case APR_ETIMEDOUT: {
41a6c3
+        return HTTP_REQUEST_TIME_OUT;
41a6c3
+    }
41a6c3
+    default: {
41a6c3
+        return status;
41a6c3
+    }
41a6c3
+    }
41a6c3
+}
41a6c3
+
41a6c3
 /* In HTTP/1.1, any method can have a body.  However, most GET handlers
41a6c3
  * wouldn't know what to do with a request body if they received one.
41a6c3
  * This helper routine tests for and reads any message body in the request,
41a6c3
@@ -1402,7 +1427,8 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
41a6c3
 AP_DECLARE(int) ap_discard_request_body(request_rec *r)
41a6c3
 {
41a6c3
     apr_bucket_brigade *bb;
41a6c3
-    int rv, seen_eos;
41a6c3
+    int seen_eos;
41a6c3
+    apr_status_t rv;
41a6c3
 
41a6c3
     /* Sometimes we'll get in a state where the input handling has
41a6c3
      * detected an error where we want to drop the connection, so if
41a6c3
@@ -1425,21 +1451,8 @@ AP_DECLARE(int) ap_discard_request_body(request_rec *r)
41a6c3
                             APR_BLOCK_READ, HUGE_STRING_LEN);
41a6c3
 
41a6c3
         if (rv != APR_SUCCESS) {
41a6c3
-            /* FIXME: If we ever have a mapping from filters (apr_status_t)
41a6c3
-             * to HTTP error codes, this would be a good place for them.
41a6c3
-             *
41a6c3
-             * If we received the special case AP_FILTER_ERROR, it means
41a6c3
-             * that the filters have already handled this error.
41a6c3
-             * Otherwise, we should assume we have a bad request.
41a6c3
-             */
41a6c3
-            if (rv == AP_FILTER_ERROR) {
41a6c3
-                apr_brigade_destroy(bb);
41a6c3
-                return rv;
41a6c3
-            }
41a6c3
-            else {
41a6c3
-                apr_brigade_destroy(bb);
41a6c3
-                return HTTP_BAD_REQUEST;
41a6c3
-            }
41a6c3
+            apr_brigade_destroy(bb);
41a6c3
+            return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
41a6c3
         }
41a6c3
 
41a6c3
         for (bucket = APR_BRIGADE_FIRST(bb);
41a6c3
@@ -1608,6 +1621,13 @@ AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer,
41a6c3
     /* We lose the failure code here.  This is why ap_get_client_block should
41a6c3
      * not be used.
41a6c3
      */
41a6c3
+    if (rv == AP_FILTER_ERROR) {
41a6c3
+        /* AP_FILTER_ERROR means a filter has responded already,
41a6c3
+         * we are DONE.
41a6c3
+         */
41a6c3
+        apr_brigade_destroy(bb);
41a6c3
+        return -1;
41a6c3
+    }
41a6c3
     if (rv != APR_SUCCESS) {
41a6c3
         /* if we actually fail here, we want to just return and
41a6c3
          * stop trying to read data from the client.