You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by mi...@apache.org on 2008/11/16 20:22:42 UTC
svn commit: r718082 - /httpd/httpd/trunk/modules/filters/mod_buffer.c
Author: minfrin
Date: Sun Nov 16 11:22:41 2008
New Revision: 718082
URL: http://svn.apache.org/viewvc?rev=718082&view=rev
Log:
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.
Modified:
httpd/httpd/trunk/modules/filters/mod_buffer.c
Modified: httpd/httpd/trunk/modules/filters/mod_buffer.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/filters/mod_buffer.c?rev=718082&r1=718081&r2=718082&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/filters/mod_buffer.c (original)
+++ httpd/httpd/trunk/modules/filters/mod_buffer.c Sun Nov 16 11:22:41 2008
@@ -43,7 +43,10 @@
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_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 @@
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;
-
- rv = ap_get_brigade(f->next, tmp, mode, block, remaining);
-
- /* if an error was received, bail out now */
- if (rv != APR_SUCCESS) {
- APR_BRIGADE_CONCAT(bb, tmp);
- return rv;
- }
-
- 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;
+ /* 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;
+
+ while (!ctx->seen_eos && ctx->remaining > 0) {
+ const char *data;
+ apr_size_t size = 0;
+
+ rv = ap_get_brigade(f->next, ctx->tmp, mode, block, ctx->remaining);
+
+ /* 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 flush buckets through */
- if (APR_BUCKET_IS_FLUSH(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)) {
- /* pass metadata buckets through */
- if (APR_BUCKET_IS_METADATA(e)) {
- APR_BUCKET_REMOVE(e);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- continue;
- }
+ /* 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;
Re: svn commit: r718082 - /httpd/httpd/trunk/modules/filters/mod_buffer.c
Posted by Graham Leggett <mi...@sharp.fm>.
Ruediger Pluem wrote:
>> + /* pass flush and metadata buckets through */
>> + if (APR_BUCKET_IS_FLUSH(e) || APR_BUCKET_IS_METADATA(e)) {
>
> This is redundant as flush buckets are metadata buckets :-).
>
>> + APR_BUCKET_REMOVE(e);
>> + APR_BRIGADE_INSERT_TAIL(bb, e);
>> + continue;
>> + }
Hmmm... true. A metadata bucket should be passed over into the buffer,
but flush buckets should cause the buffer to be read straight away. Will
fix.
Regards,
Graham
--
Re: svn commit: r718082 - /httpd/httpd/trunk/modules/filters/mod_buffer.c
Posted by Ruediger Pluem <rp...@apache.org>.
On 11/16/2008 08:22 PM, minfrin@apache.org wrote:
> Author: minfrin
> Date: Sun Nov 16 11:22:41 2008
> New Revision: 718082
>
> URL: http://svn.apache.org/viewvc?rev=718082&view=rev
> Log:
> 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.
>
> Modified:
> httpd/httpd/trunk/modules/filters/mod_buffer.c
>
> Modified: httpd/httpd/trunk/modules/filters/mod_buffer.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/filters/mod_buffer.c?rev=718082&r1=718081&r2=718082&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/filters/mod_buffer.c (original)
> +++ httpd/httpd/trunk/modules/filters/mod_buffer.c Sun Nov 16 11:22:41 2008
> @@ -170,65 +171,84 @@
> 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;
> -
> - rv = ap_get_brigade(f->next, tmp, mode, block, remaining);
> -
> - /* if an error was received, bail out now */
> - if (rv != APR_SUCCESS) {
> - APR_BRIGADE_CONCAT(bb, tmp);
> - return rv;
> - }
> -
> - 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;
> + /* 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;
> +
> + while (!ctx->seen_eos && ctx->remaining > 0) {
> + const char *data;
> + apr_size_t size = 0;
> +
> + rv = ap_get_brigade(f->next, ctx->tmp, mode, block, ctx->remaining);
> +
> + /* 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 flush buckets through */
> - if (APR_BUCKET_IS_FLUSH(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)) {
>
> - /* pass metadata buckets through */
> - if (APR_BUCKET_IS_METADATA(e)) {
> - APR_BUCKET_REMOVE(e);
> - APR_BRIGADE_INSERT_TAIL(bb, e);
> - continue;
> - }
> + /* 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)) {
This is redundant as flush buckets are metadata buckets :-).
> + 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);
> }
This should be possible without a loop over the brigade. See
brigade_move in core_filters for a similar function.
Regards
RĂ¼diger