]> granicus.if.org Git - apache/commitdiff
Compute the content length (and add appropriate header field) for
authorJeff Trawick <trawick@apache.org>
Tue, 31 Oct 2000 12:30:22 +0000 (12:30 +0000)
committerJeff Trawick <trawick@apache.org>
Tue, 31 Oct 2000 12:30:22 +0000 (12:30 +0000)
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
modules/http/http_core.c
modules/http/http_protocol.c

index 8037a0621b0152d1aa45ab5b4bf484db78dbf02f..b1f04b5a756299b1700c63c06469415ffe9ea710 100644 (file)
@@ -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,
index 4e84eb451332e8d1e0c70fca0d34330f3430b1fd..ddc316fcf1e4575694695e84e2096cbd91f2582e 100644 (file)
@@ -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);
index d01b3e759927cb677ca0702e22ab897eeb4df29f..65b7d38457bcbe554c675a23bb106817157e19be 100644 (file)
@@ -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;