diff options
author | Brian Pane <brianp@apache.org> | 2002-08-28 20:37:48 +0200 |
---|---|---|
committer | Brian Pane <brianp@apache.org> | 2002-08-28 20:37:48 +0200 |
commit | 2c3170ce19c2839505449d88628e6d31ce28df87 (patch) | |
tree | e66a5ad99e880b0974a115aced9c67a642565070 /server | |
parent | Win32: Lower the default stack size from 1MB to 256KB. This will allow (diff) | |
download | apache2-2c3170ce19c2839505449d88628e6d31ce28df87.tar.xz apache2-2c3170ce19c2839505449d88628e6d31ce28df87.zip |
Changed the content-length filter to allow streaming delivery
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
Diffstat (limited to 'server')
-rw-r--r-- | server/protocol.c | 177 |
1 files changed, 63 insertions, 114 deletions
diff --git a/server/protocol.c b/server/protocol.c index cf01a4a2e1..b228e81127 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -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); } |