summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorBrian Pane <brianp@apache.org>2002-08-28 20:37:48 +0200
committerBrian Pane <brianp@apache.org>2002-08-28 20:37:48 +0200
commit2c3170ce19c2839505449d88628e6d31ce28df87 (patch)
treee66a5ad99e880b0974a115aced9c67a642565070 /server
parentWin32: Lower the default stack size from 1MB to 256KB. This will allow (diff)
downloadapache2-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.c177
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);
}