From ad7545f0b46defaf848de1b09385e41b65e509dd Mon Sep 17 00:00:00 2001 From: Jeff Trawick Date: Tue, 31 Oct 2000 12:30:22 +0000 Subject: [PATCH] Compute the content length (and add appropriate header field) for the response when no content length is available and we can't use chunked encoding. This is going to be painful when the response body is huge, so I suspect we'll have additional criteria in the future. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@86775 13f79535-47bb-0310-9956-ffa450edef68 --- include/http_protocol.h | 3 ++ modules/http/http_core.c | 2 + modules/http/http_protocol.c | 79 ++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/include/http_protocol.h b/include/http_protocol.h index 8037a0621b0..b1f04b5a756 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -113,6 +113,8 @@ AP_DECLARE(void) ap_basic_http_header(request_rec *r); AP_DECLARE(void) ap_send_http_header(request_rec *l); AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, ap_bucket_brigade *b); +AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *, + ap_bucket_brigade *); /* Send the response to special method requests */ @@ -534,6 +536,7 @@ AP_DECLARE(const char *) ap_method_name_of(int methnum); apr_status_t ap_http_filter(ap_filter_t *f, ap_bucket_brigade *b, ap_input_mode_t mode); apr_status_t ap_dechunk_filter(ap_filter_t *f, ap_bucket_brigade *b, ap_input_mode_t mode); + /* Hooks */ /* * post_read_request --- run right after read_request or internal_redirect, diff --git a/modules/http/http_core.c b/modules/http/http_core.c index 4e84eb45133..ddc316fcf1e 100644 --- a/modules/http/http_core.c +++ b/modules/http/http_core.c @@ -3578,6 +3578,8 @@ static void register_hooks(void) ap_register_input_filter("DECHUNK", ap_dechunk_filter, AP_FTYPE_TRANSCODE); ap_register_input_filter("CORE_IN", core_input_filter, AP_FTYPE_NETWORK); ap_register_output_filter("HTTP_HEADER", ap_http_header_filter, AP_FTYPE_HTTP_HEADER); + ap_register_output_filter("CONTENT_LENGTH", ap_content_length_filter, + AP_FTYPE_HTTP_HEADER); ap_register_output_filter("CORE", core_output_filter, AP_FTYPE_NETWORK); ap_register_output_filter("SUBREQ_CORE", ap_sub_req_output_filter, AP_FTYPE_CONTENT); diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index d01b3e75992..65b7d38457b 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -1417,6 +1417,7 @@ request_rec *ap_read_request(conn_rec *conn) ? &r->server->keep_alive_timeout : &r->server->timeout); + ap_add_output_filter("CONTENT_LENGTH", NULL, r, r->connection); ap_add_output_filter("HTTP_HEADER", NULL, r, r->connection); /* Get the request... */ @@ -2229,6 +2230,84 @@ AP_DECLARE(void) ap_send_http_header(request_rec *r) { } +struct content_length_ctx { + ap_bucket_brigade *saved; +}; + +AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *f, + ap_bucket_brigade *b) +{ + request_rec *r = f->r; + struct content_length_ctx *ctx; + apr_status_t rv; + ap_bucket *e; + + ctx = f->ctx; + if (!ctx) { /* first time through */ + /* We won't compute a content length if one of the following is true: + * . subrequest + * . HTTP/0.9 + * . status HTTP_NOT_MODIFIED or HTTP_NO_CONTENT + * . HEAD + * . content length already computed + * . can be chunked + * . body already chunked + * Much of this should correspond to checks in ap_set_keepalive(). + */ + if (r->assbackwards + || r->status == HTTP_NOT_MODIFIED + || r->status == HTTP_NO_CONTENT + || r->header_only + || apr_table_get(r->headers_out, "Content-Length") + || r->proto_num == HTTP_VERSION(1,1) + || ap_find_last_token(f->r->pool, + apr_table_get(r->headers_out, + "Transfer-Encoding"), + "chunked")) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, b); + } + + f->ctx = ctx = apr_pcalloc(r->pool, sizeof(struct content_length_ctx)); + } + + if (AP_BUCKET_IS_EOS(AP_BRIGADE_LAST(b))) { + apr_ssize_t content_length = 0; + + if (ctx->saved) { + AP_BRIGADE_CONCAT(ctx->saved, b); + b = ctx->saved; + } + + AP_BRIGADE_FOREACH(e, b) { + if (!AP_BUCKET_IS_EOS(e)) { + if (e->length >= 0) { + content_length += e->length; + } + else { + const char *ignored; + apr_ssize_t length; + + rv = ap_bucket_read(e, &ignored, &length, 1); + if (rv != APR_SUCCESS) { + return rv; + } + content_length += e->length; + } + } + } + + ap_set_content_length(r, content_length); + return ap_pass_brigade(f->next, b); + } + + /* save the brigade; we can't pass any data to the next + * filter until we have the entire content length + */ + ap_save_brigade(f, &ctx->saved, &b); + return APR_SUCCESS; +} + AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, ap_bucket_brigade *b) { int i; -- 2.47.2