From c79192b88901bbe9647e198ded975b88adc93226 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Sun, 16 Nov 2008 19:22:41 +0000 Subject: [PATCH] Refactor the input buffer filter to use a context as the output buffer filter does. Ensure that the filter properly handles the non blocking case, both when a downstream filter returns EAGAIN, or if a filter returns APR_SUCCESS and an empty brigade. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@718082 13f79535-47bb-0310-9956-ffa450edef68 --- modules/filters/mod_buffer.c | 118 ++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 49 deletions(-) diff --git a/modules/filters/mod_buffer.c b/modules/filters/mod_buffer.c index f7231dad4d..c8e3bf38e2 100644 --- a/modules/filters/mod_buffer.c +++ b/modules/filters/mod_buffer.c @@ -43,7 +43,10 @@ typedef struct buffer_conf { typedef struct buffer_ctx { apr_bucket_brigade *bb; + apr_bucket_brigade *tmp; buffer_conf *conf; + apr_off_t remaining; + int seen_eos; } buffer_ctx; /** @@ -158,11 +161,9 @@ static apr_status_t buffer_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { */ static apr_status_t buffer_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { - apr_bucket *e; - apr_bucket_brigade *tmp; + apr_bucket *e, *after; apr_status_t rv; - buffer_conf *c; - apr_off_t remaining; + buffer_ctx *ctx = f->ctx; /* buffer on main requests only */ if (!ap_is_initial_req(f->r)) { @@ -170,65 +171,84 @@ static apr_status_t buffer_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, return ap_get_brigade(f->next, bb, mode, block, readbytes); } + /* first time in? create a context */ + if (!ctx) { + ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); + ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + ctx->tmp = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + ctx->conf = ap_get_module_config(f->r->per_dir_config, &buffer_module); + } + /* just get out of the way of things we don't want. */ if (mode != AP_MODE_READBYTES) { return ap_get_brigade(f->next, bb, mode, block, readbytes); } - c = ap_get_module_config(f->r->per_dir_config, &buffer_module); - - tmp = apr_brigade_create(f->r->pool, f->c->bucket_alloc); - - remaining = readbytes; - while (remaining > 0) { - const char *data; - apr_off_t len; - apr_size_t size; + /* if our buffer is empty, read off the network until the buffer is full */ + if (APR_BRIGADE_EMPTY(ctx->bb)) { + ctx->remaining = ctx->conf->size; - rv = ap_get_brigade(f->next, tmp, mode, block, remaining); + while (!ctx->seen_eos && ctx->remaining > 0) { + const char *data; + apr_size_t size = 0; - /* if an error was received, bail out now */ - if (rv != APR_SUCCESS) { - APR_BRIGADE_CONCAT(bb, tmp); - return rv; - } + rv = ap_get_brigade(f->next, ctx->tmp, mode, block, ctx->remaining); - apr_brigade_length(tmp, 1, &len); - remaining -= len; - - for (e = APR_BRIGADE_FIRST(tmp); e != APR_BRIGADE_SENTINEL(tmp); e - = APR_BUCKET_NEXT(e)) { - - /* if we see an EOS, we are done */ - if (APR_BUCKET_IS_EOS(e)) { - APR_BUCKET_REMOVE(e); - APR_BRIGADE_INSERT_TAIL(bb, e); - remaining = 0; - break; - } - - /* pass flush buckets through */ - if (APR_BUCKET_IS_FLUSH(e)) { - APR_BUCKET_REMOVE(e); - APR_BRIGADE_INSERT_TAIL(bb, e); - continue; + /* if an error was received, bail out now. If the error is + * EAGAIN and we have not yet seen an EOS, we will definitely + * be called again, at which point we will send our buffered + * data. Instead of sending EAGAIN, some filters return an + * empty brigade instead when data is not yet available. In + * this case, pass through the APR_SUCCESS and emulate the + * underlying filter. + */ + if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(ctx->tmp)) { + return rv; } - /* pass metadata buckets through */ - if (APR_BUCKET_IS_METADATA(e)) { - APR_BUCKET_REMOVE(e); - APR_BRIGADE_INSERT_TAIL(bb, e); - continue; - } + for (e = APR_BRIGADE_FIRST(ctx->tmp); e != APR_BRIGADE_SENTINEL( + ctx->tmp); e = APR_BUCKET_NEXT(e)) { + + /* if we see an EOS, we are done */ + if (APR_BUCKET_IS_EOS(e)) { + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(ctx->bb, e); + ctx->seen_eos = 1; + break; + } + + /* pass flush and metadata buckets through */ + if (APR_BUCKET_IS_FLUSH(e) || APR_BUCKET_IS_METADATA(e)) { + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(bb, e); + continue; + } + + /* read the bucket in, pack it into the buffer */ + if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size, + APR_BLOCK_READ))) { + apr_brigade_write(ctx->bb, NULL, NULL, data, size); + ctx->remaining -= size; + apr_bucket_delete(e); + } else { + return rv; + } - /* read */ - if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size, - APR_BLOCK_READ))) { - apr_brigade_write(bb, NULL, NULL, data, size); } + } + } + /* give the caller the data they asked for from the buffer */ + apr_brigade_partition(ctx->bb, readbytes, &after); + e = APR_BRIGADE_FIRST(ctx->bb); + while (e != after) { + if (APR_BUCKET_IS_EOS(e)) { + /* last bucket read, step out of the way */ + ap_remove_input_filter(f); } - apr_brigade_cleanup(tmp); + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(bb, e); + e = APR_BRIGADE_FIRST(ctx->bb); } return APR_SUCCESS; -- 2.40.0