]> granicus.if.org Git - apache/commitdiff
Changed the content-length filter to allow streaming delivery
authorBrian Pane <brianp@apache.org>
Wed, 28 Aug 2002 18:37:48 +0000 (18:37 +0000)
committerBrian Pane <brianp@apache.org>
Wed, 28 Aug 2002 18:37:48 +0000 (18:37 +0000)
of content to clients

With this new code, the C-L filter will compute a content-length
if there's an EOS in the first brigade passed to it.  For normal
static file requests, the response header will still include a C-L.
If there's no EOS in the first brigade passed to the C-L filter,
the filter will give up on setting the C-L header, in favor of
passing the data on to the next filter in a timely manner.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@96557 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
server/protocol.c

diff --git a/CHANGES b/CHANGES
index 517bb60c26bea5158f29e72f4fdb9ba803935e88..bb3159dc9361bb73e25cb114c6d437d5d32beba0 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,9 @@
 Changes with Apache 2.0.41
+
+  *) The content-length filter no longer tries to buffer up
+     the entire output of a long-running request before sending
+     anything to the client.  [Brian Pane]
+
   *) Win32: Lower the default stack size from 1MB to 256K. This will
      allow around 8000 threads to be started per child process. 
      'EDITBIN /STACK:size apache.exe' can be used to change this 
index cf01a4a2e1a8df686a8028950cb796bcb7f46268..b228e811272a720a93cc63e392f4b0bb3b86c1f1 100644 (file)
@@ -1200,9 +1200,10 @@ AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
 }
 
 struct content_length_ctx {
-    apr_bucket_brigade *saved;
-    int compute_len;
-    apr_size_t curr_len;
+    int data_sent;  /* true if the C-L filter has already sent at
+                     * least one bucket on to the next output filter
+                     * for this request
+                     */
 };
 
 /* This filter computes the content length, but it also computes the number
@@ -1214,140 +1215,88 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *f,
 {
     request_rec *r = f->r;
     struct content_length_ctx *ctx;
-    apr_status_t rv;
     apr_bucket *e;
-    int eos = 0, flush = 0, partial_send_okay = 0;
-    apr_bucket_brigade *more, *split;
+    int eos = 0;
     apr_read_type_e eblock = APR_NONBLOCK_READ;
 
-    ctx = f->ctx;
-    if (!ctx) { /* first time through */
-        f->ctx = ctx = apr_pcalloc(r->pool, sizeof(struct content_length_ctx));
-        ctx->compute_len = 1;   /* Assume we will compute the length */
-        ctx->saved = apr_brigade_create(r->pool, f->c->bucket_alloc);
-    }
+    int can_send_content_length;
 
-    /* Humm, is this check the best it can be?
-     * - protocol >= HTTP/1.1 implies support for chunking
-     * - non-keepalive implies the end of byte stream will be signaled
-     *    by a connection close
-     * In both cases, we can send bytes to the client w/o needing to
-     * compute content-length.
-     * Todo:
-     * We should be able to force connection close from this filter
-     * when we see we are buffering too much.
+    /* We can only set a C-L in the response header if we
+     * haven't already sent any buckets on to the next
+     * output filter for this request.
      */
-    if ((r->proto_num >= HTTP_VERSION(1, 1)) ||
-        (r->connection->keepalive == AP_CONN_CLOSE)) {
-        partial_send_okay = 1;
+    ctx = f->ctx;
+    if (!ctx) {
+        f->ctx = ctx = apr_palloc(r->pool, sizeof(struct content_length_ctx));
+        ctx->data_sent = 0;
+        can_send_content_length = 1;
+    }
+    else {
+        can_send_content_length = (ctx->data_sent == 0);
     }
 
-    more = b;
-    while (more) {
-        b = more;
-        more = NULL;
-        split = NULL;
-        flush = 0;
-
-        e = APR_BRIGADE_FIRST(b);
-        while (e != APR_BRIGADE_SENTINEL(b)) {
-            const char *ignored;
+    /* Loop through this set of buckets to compute their length
+     */
+    e = APR_BRIGADE_FIRST(b);
+    while (e != APR_BRIGADE_SENTINEL(b)) {
+        if (APR_BUCKET_IS_EOS(e)) {
+            eos = 1;
+            break;
+        }
+        if (e->length == -1) {
             apr_size_t len;
-            len = 0;
-            if (APR_BUCKET_IS_EOS(e)) {
-                eos = 1;
-                break;
-            }
-            else if (APR_BUCKET_IS_FLUSH(e)) {
-                if (partial_send_okay) {
-                    split = b;
-                    more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
-                    break;
-                }
+            const char *ignored;
+            apr_status_t rv;
+
+            /* This is probably a pipe bucket.  Send everything
+             * prior to this, and then read the data for this bucket.
+             */
+            rv = apr_bucket_read(e, &ignored, &len, eblock);
+            if (rv == APR_SUCCESS) {
+                /* Attempt a nonblocking read next time through */
+                eblock = APR_NONBLOCK_READ;
+                r->bytes_sent += len;
             }
-            else if ((ctx->curr_len > 4 * AP_MIN_BYTES_TO_WRITE)) {
-                /* If we've accumulated more than 4xAP_MIN_BYTES_TO_WRITE and
-                 * the client supports chunked encoding, send what we have
-                 * and come back for more.
+            else if (APR_STATUS_IS_EAGAIN(rv)) {
+                /* Output everything prior to this bucket, and
+                 * do a blocking read
                  */
-                if (partial_send_okay) {
-                    split = b;
-                    more = apr_brigade_split(b, e);
-                    break;
-                }
-            }
-            if (e->length == -1) { /* if length unknown */
-                rv = apr_bucket_read(e, &ignored, &len, eblock);
-                if (rv == APR_SUCCESS) {
-                    /* Attempt a nonblocking read next time through */
-                    eblock = APR_NONBLOCK_READ;
-                }
-                else if (APR_STATUS_IS_EAGAIN(rv)) {
-                    /* Make the next read blocking.  If the client supports
-                     * chunked encoding, flush the filter stack to the network.
-                     */
-                    eblock = APR_BLOCK_READ;
-                    if (partial_send_okay) {
-                        split = b;
-                        more = apr_brigade_split(b, e);
-                        flush = 1;
-                        break;
+                if (e != APR_BRIGADE_FIRST(b)) {
+                    apr_bucket_brigade *split = apr_brigade_split(b, e);
+                    rv = ap_pass_brigade(f->next, b);
+                    if (rv != APR_SUCCESS) {
+                        apr_brigade_destroy(split);
+                        return rv;
                     }
-                    continue;
-                }
-                else if (rv != APR_EOF) {
-                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
-                                  "ap_content_length_filter: "
-                                  "apr_bucket_read() failed");
-                    return rv;
+                    b = split;
+                    e = APR_BRIGADE_FIRST(b);
+                    can_send_content_length = 0;
+                    ctx->data_sent = 1;
                 }
+                eblock = APR_BLOCK_READ;
+                continue;
             }
             else {
-                len = e->length;
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                              "ap_content_length_filter: "
+                              "apr_bucket_read() failed");
+                return rv;
             }
-
-            ctx->curr_len += len;
-            r->bytes_sent += len;
-            e = APR_BUCKET_NEXT(e);
         }
-
-        if (split) {
-            ctx->compute_len = 0;  /* Ooops, can't compute the length now */
-            ctx->curr_len = 0;
-
-            APR_BRIGADE_PREPEND(split, ctx->saved);
-
-            if (flush) {
-                rv = ap_fflush(f->next, split);
-            }
-            else {
-                rv = ap_pass_brigade(f->next, split);
-            }
-
-            if (rv != APR_SUCCESS)
-                return rv;
+        else {
+            r->bytes_sent += e->length;
         }
+        e = APR_BUCKET_NEXT(e);
     }
 
-    if ((ctx->curr_len < AP_MIN_BYTES_TO_WRITE) && !eos) {
-        return ap_save_brigade(f, &ctx->saved, &b,
-                               (r->main) ? r->main->pool : r->pool);
-    }
-
-    if (ctx->compute_len) {
-        /* save the brigade; we can't pass any data to the next
-         * filter until we have the entire content length
+    /* If we've now seen the entire response and it's otherwise
+     * okay to set the C-L in the response header, do so now:
          */
-        if (!eos) {
-            return ap_save_brigade(f, &ctx->saved, &b, r->pool);
-        }
-
+    if (can_send_content_length && eos) {
         ap_set_content_length(r, r->bytes_sent);
     }
 
-    APR_BRIGADE_PREPEND(b, ctx->saved);
-
-    ctx->curr_len = 0;
+    ctx->data_sent = 1;
     return ap_pass_brigade(f->next, b);
 }