diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c index 9828cdf..6bedcac 100644 --- a/modules/http/http_filters.c +++ b/modules/http/http_filters.c @@ -1605,9 +1605,9 @@ AP_DECLARE(int) ap_map_http_request_error(apr_status_t rv, int status) */ AP_DECLARE(int) ap_discard_request_body(request_rec *r) { + int rc = OK; + conn_rec *c = r->connection; apr_bucket_brigade *bb; - int seen_eos; - apr_status_t rv; /* Sometimes we'll get in a state where the input handling has * detected an error where we want to drop the connection, so if @@ -1616,54 +1616,57 @@ AP_DECLARE(int) ap_discard_request_body(request_rec *r) * * This function is also a no-op on a subrequest. */ - if (r->main || r->connection->keepalive == AP_CONN_CLOSE || - ap_status_drops_connection(r->status)) { + if (r->main || c->keepalive == AP_CONN_CLOSE) { + return OK; + } + if (ap_status_drops_connection(r->status)) { + c->keepalive = AP_CONN_CLOSE; return OK; } bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - seen_eos = 0; - do { - apr_bucket *bucket; + for (;;) { + apr_status_t rv; rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); - if (rv != APR_SUCCESS) { - apr_brigade_destroy(bb); - return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); + rc = ap_map_http_request_error(rv, HTTP_BAD_REQUEST); + goto cleanup; } - for (bucket = APR_BRIGADE_FIRST(bb); - bucket != APR_BRIGADE_SENTINEL(bb); - bucket = APR_BUCKET_NEXT(bucket)) - { - const char *data; - apr_size_t len; + while (!APR_BRIGADE_EMPTY(bb)) { + apr_bucket *b = APR_BRIGADE_FIRST(bb); - if (APR_BUCKET_IS_EOS(bucket)) { - seen_eos = 1; - break; + if (APR_BUCKET_IS_EOS(b)) { + goto cleanup; } - /* These are metadata buckets. */ - if (bucket->length == 0) { - continue; - } - - /* We MUST read because in case we have an unknown-length - * bucket or one that morphs, we want to exhaust it. + /* There is no need to read empty or metadata buckets or + * buckets of known length, but we MUST read buckets of + * unknown length in order to exhaust them. */ - rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); + if (b->length == (apr_size_t)-1) { + apr_size_t len; + const char *data; + + rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { - apr_brigade_destroy(bb); - return HTTP_BAD_REQUEST; + rc = HTTP_BAD_REQUEST; + goto cleanup; } } - apr_brigade_cleanup(bb); - } while (!seen_eos); - return OK; + apr_bucket_delete(b); + } + } + +cleanup: + apr_brigade_cleanup(bb); + if (rc != OK) { + c->keepalive = AP_CONN_CLOSE; + } + return rc; } /* Here we deal with getting the request message body from the client. diff --git a/server/protocol.c b/server/protocol.c index a2aa081..a554970 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -1666,23 +1666,29 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew, rnew->main = (request_rec *) r; } -static void end_output_stream(request_rec *r) +static void end_output_stream(request_rec *r, int status) { conn_rec *c = r->connection; apr_bucket_brigade *bb; apr_bucket *b; bb = apr_brigade_create(r->pool, c->bucket_alloc); + if (status != OK) { + b = ap_bucket_error_create(status, NULL, r->pool, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + } b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); + ap_pass_brigade(r->output_filters, bb); + apr_brigade_cleanup(bb); } AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub) { /* tell the filter chain there is no more content coming */ if (!sub->eos_sent) { - end_output_stream(sub); + end_output_stream(sub, OK); } } @@ -1693,11 +1699,11 @@ AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub) */ AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r) { - (void) ap_discard_request_body(r); + int status = ap_discard_request_body(r); /* tell the filter chain there is no more content coming */ if (!r->eos_sent) { - end_output_stream(r); + end_output_stream(r, status); } }