From: Jeff Trawick Date: Tue, 31 Oct 2000 12:30:22 +0000 (+0000) Subject: Compute the content length (and add appropriate header field) for X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=73c3e4037ac19af58eba8d38512f81e8fb1804b6;p=apache 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 --- diff --git a/include/http_protocol.h b/include/http_protocol.h index 8037a0621b..b1f04b5a75 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 4e84eb4513..ddc316fcf1 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 d01b3e7599..65b7d38457 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;