Take two.
As Greg suggested, this merges the dechunk and core filters back
to one filter. (This makes the diff in http_protocol.c a bit
gnarly.)
Other significant changes is that the HTTP_IN (ap_http_filter) is
now a request-level filter rather than a connection-level filter.
This makes a lot more sense to me.
This passes all of the tests in httpd-test (not to say that this
is perfect or anything). There is a patch at the bottom for
mod_input_body_filter.c in httpd-test that is required. (It was
expecting > readbytes to be read when asking for readbytes).
Comments and feedback? -- justin
Index: buckets/apr_brigade.c
===================================================================
RCS file: /home/cvs/apr-util/buckets/apr_brigade.c,v
retrieving revision 1.25
diff -u -r1.25 apr_brigade.c
--- buckets/apr_brigade.c 2001/09/03 22:00:36 1.25
+++ buckets/apr_brigade.c 2001/09/24 01:48:04
@@ -221,6 +221,28 @@
return APR_SUCCESS;
}
+APU_DECLARE(apr_status_t) apr_brigade_to_buffer(apr_bucket_brigade *b, char *c,
+ apr_read_type_e mode)
+{
+ apr_bucket *e;
+ char *cur;
+ apr_size_t curlen;
+ apr_status_t status;
+
+ APR_BRIGADE_FOREACH(e, b) {
+
+ status = apr_bucket_read(e, (const char **)&cur, &curlen, mode);
+
+ if (status != APR_SUCCESS)
+ return status;
+
+ memcpy(c, cur, curlen);
+ c += curlen;
+ }
+
+ return APR_SUCCESS;
+}
+
APU_DECLARE(apr_status_t) apr_brigade_to_iovec(apr_bucket_brigade *b,
struct iovec *vec, int *nvec)
{
Index: buckets/apr_buckets_pipe.c
===================================================================
RCS file: /home/cvs/apr-util/buckets/apr_buckets_pipe.c,v
retrieving revision 1.41
diff -u -r1.41 apr_buckets_pipe.c
--- buckets/apr_buckets_pipe.c 2001/09/22 22:36:07 1.41
+++ buckets/apr_buckets_pipe.c 2001/09/24 01:48:04
@@ -110,10 +110,6 @@
*str = a->data;
if (rv == APR_EOF) {
apr_file_close(p);
- if (block == APR_NONBLOCK_READ) {
- /* XXX: this is bogus, should return APR_SUCCESS */
- return APR_EOF;
- }
}
}
return APR_SUCCESS;
Index: buckets/apr_buckets_socket.c
===================================================================
RCS file: /home/cvs/apr-util/buckets/apr_buckets_socket.c,v
retrieving revision 1.30
diff -u -r1.30 apr_buckets_socket.c
--- buckets/apr_buckets_socket.c 2001/09/22 22:36:07 1.30
+++ buckets/apr_buckets_socket.c 2001/09/24 01:48:04
@@ -111,10 +111,6 @@
free(buf);
a = apr_bucket_immortal_make(a, "", 0);
*str = a->data;
- if (rv == APR_EOF && block == APR_NONBLOCK_READ) {
- /* XXX: this is bogus... should return APR_SUCCESS */
- return APR_EOF;
- }
}
return APR_SUCCESS;
}
Index: include/apr_buckets.h
===================================================================
RCS file: /home/cvs/apr-util/include/apr_buckets.h,v
retrieving revision 1.119
diff -u -r1.119 apr_buckets.h
--- include/apr_buckets.h 2001/09/24 01:47:10 1.119
+++ include/apr_buckets.h 2001/09/24 01:48:04
@@ -689,6 +689,17 @@
apr_off_t *length);
/**
+ * create a flat buffer of the elements in a bucket_brigade.
+ * @param b The bucket brigade to create the buffer from
+ * @param c The pointer to the returned character string
+ * @param mode Should we block when reading the buckets
+ * Note: You *must* have enough space allocated to store all of the data.
+ * See apr_brigade_length().
+ */
+APU_DECLARE(apr_status_t) apr_brigade_to_buffer(apr_bucket_brigade *b, char *c,
+ apr_read_type_e mode);
+
+/**
* create an iovec of the elements in a bucket_brigade... return number
* of elements used. This is useful for writing to a file or to the
* network efficiently.
Index: server/core.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/core.c,v
retrieving revision 1.61
diff -u -r1.61 core.c
--- server/core.c 2001/09/19 05:52:42 1.61
+++ server/core.c 2001/09/24 01:45:18
@@ -2757,19 +2757,9 @@
{
apr_bucket *e;
- if (!f->ctx) { /* If we haven't passed up the socket yet... */
- f->ctx = (void *)1;
- e = apr_bucket_socket_create(f->c->client_socket);
- APR_BRIGADE_INSERT_TAIL(b, e);
- return APR_SUCCESS;
- }
- else {
- /* Either some code lost track of the socket
- * bucket or we already found out that the
- * client closed.
- */
- return APR_EOF;
- }
+ e = apr_bucket_socket_create(f->c->client_socket);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ return APR_SUCCESS;
}
/* Default filter. This filter should almost always be used. Its only job
Index: server/protocol.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/protocol.c,v
retrieving revision 1.45
diff -u -r1.45 protocol.c
--- server/protocol.c 2001/09/18 22:13:57 1.45
+++ server/protocol.c 2001/09/24 01:45:18
@@ -219,7 +219,7 @@
while (1) {
if (APR_BRIGADE_EMPTY(b)) {
apr_off_t zero = 0;
- if ((retval = ap_get_brigade(c->input_filters, b,
+ if ((retval = ap_get_brigade(r->input_filters, b,
AP_MODE_BLOCKING,
&zero /* readline */)) != APR_SUCCESS ||
APR_BRIGADE_EMPTY(b)) {
@@ -562,6 +562,9 @@
r->notes = apr_table_make(r->pool, 5);
r->request_config = ap_create_request_config(r->pool);
+ /* Must be set before we run create request hook */
+ r->output_filters = conn->output_filters;
+ r->input_filters = conn->input_filters;
ap_run_create_request(r);
r->per_dir_config = r->server->lookup_defaults;
@@ -572,8 +575,6 @@
r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */
r->the_request = NULL;
- r->output_filters = conn->output_filters;
- r->input_filters = conn->input_filters;
apr_setsocketopt(conn->client_socket, APR_SO_TIMEOUT,
(int)(keptalive
Index: modules/http/http_core.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/http/http_core.c,v
retrieving revision 1.282
diff -u -r1.282 http_core.c
--- modules/http/http_core.c 2001/08/25 23:43:18 1.282
+++ modules/http/http_core.c 2001/09/24 01:45:18
@@ -262,7 +262,6 @@
static int ap_pre_http_connection(conn_rec *c)
{
- ap_add_input_filter("HTTP_IN", NULL, NULL, c);
ap_add_input_filter("CORE_IN", NULL, NULL, c);
ap_add_output_filter("CORE", NULL, NULL, c);
return OK;
@@ -302,6 +301,15 @@
return OK;
}
+static int ap_http_create_req(request_rec *r)
+{
+ if (!r->main)
+ {
+ ap_add_input_filter("HTTP_IN", NULL, r, r->connection);
+ }
+ return OK;
+}
+
static void ap_http_insert_filter(request_rec *r)
{
if (!r->main) {
@@ -320,10 +328,10 @@
ap_hook_map_to_storage(ap_send_http_trace,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_http_method(http_method,NULL,NULL,APR_HOOK_REALLY_LAST);
ap_hook_default_port(http_port,NULL,NULL,APR_HOOK_REALLY_LAST);
+ ap_hook_create_request(ap_http_create_req, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_insert_filter(ap_http_insert_filter, NULL, NULL, APR_HOOK_REALLY_LAST);
ap_register_input_filter("HTTP_IN", ap_http_filter, AP_FTYPE_CONNECTION);
- ap_register_input_filter("DECHUNK", ap_dechunk_filter, AP_FTYPE_TRANSCODE);
ap_register_output_filter("HTTP_HEADER", ap_http_header_filter,
AP_FTYPE_HTTP_HEADER);
ap_register_output_filter("CHUNK", chunk_filter, AP_FTYPE_TRANSCODE);
Index: modules/http/http_protocol.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/http/http_protocol.c,v
retrieving revision 1.362
diff -u -r1.362 http_protocol.c
--- modules/http/http_protocol.c 2001/09/17 13:12:37 1.362
+++ modules/http/http_protocol.c 2001/09/24 01:45:18
@@ -481,125 +481,115 @@
return AP_HTTP_METHODS[methnum];
}
-struct dechunk_ctx {
- apr_size_t chunk_size;
- apr_size_t bytes_delivered;
+static long get_chunk_size(char *);
+
+typedef struct http_filter_ctx {
+ apr_bucket_brigade *b;
+ int status;
+ apr_size_t remaining;
enum {
- WANT_HDR /* must have value zero */,
- WANT_BODY,
- WANT_TRL
+ BODY_NONE /* must have value of zero */,
+ BODY_LENGTH,
+ BODY_CHUNK
} state;
-};
-
-static long get_chunk_size(char *);
+} http_ctx_t;
-apr_status_t ap_dechunk_filter(ap_filter_t *f, apr_bucket_brigade *bb,
- ap_input_mode_t mode, apr_off_t *readbytes)
+/* Hi, I'm the main input filter for HTTP requests.
+ * I handle chunked and content-length bodies. */
+apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t
+mode, apr_off_t *readbytes)
{
- apr_status_t rv;
- struct dechunk_ctx *ctx = f->ctx;
- apr_bucket *b;
- const char *buf;
+ apr_bucket *e;
+ char *buff;
apr_size_t len;
+ char *pos;
+ http_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
if (!ctx) {
- f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(struct dechunk_ctx));
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->b = apr_brigade_create(f->r->pool);
+ ctx->status = f->r->status;
+
+ /* Due to the way the core is designed, this should happen each time
+ * we get initialized. */
+ if ((rv = ap_get_brigade(f->next, ctx->b, mode,
+ readbytes)) != APR_SUCCESS) {
+ return rv;
+ }
}
- do {
- if (ctx->chunk_size == ctx->bytes_delivered) {
- /* Time to read another chunk header or trailer... ap_http_filter() is
- * the next filter in line and it knows how to return a brigade with
- * one line.
- */
- char line[30];
+ /* Basically, we have to stay out of the way until server/protocol.c
+ * says it is okay - which it does by setting r->status to OK. */
+ if (f->r->status != ctx->status)
+ {
+ int old_status;
+ /* Allow us to be reentrant! */
+ old_status = ctx->status;
+ ctx->status = f->r->status;
+
+ if (old_status == HTTP_REQUEST_TIME_OUT && f->r->status == HTTP_OK)
+ {
+ const char *tenc, *lenp;
+ tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding");
+ lenp = apr_table_get(f->r->headers_in, "Content-Length");
+
+ if (tenc) {
+ if (!strcasecmp(tenc, "chunked")) {
+ char line[30];
- if ((rv = ap_getline(line, sizeof(line), f->r,
- 0 /* readline */)) < 0) {
- return rv;
- }
- switch(ctx->state) {
- case WANT_HDR:
- ctx->chunk_size = get_chunk_size(line);
- ctx->bytes_delivered = 0;
- if (ctx->chunk_size == 0) {
- ctx->state = WANT_TRL;
+ if ((rv = ap_getline(line, sizeof(line), f->r, 0)) < 0) {
+ return rv;
+ }
+ ctx->state = BODY_CHUNK;
+ ctx->remaining = get_chunk_size(line);
}
- else {
- ctx->state = WANT_BODY;
- }
- break;
- case WANT_TRL:
- /* XXX sanity check end chunk here */
- if (strlen(line)) {
- /* bad trailer */
- }
- if (ctx->chunk_size == 0) { /* we just finished the last chunk? */
- /* ### woah... ap_http_filter() is doing this, too */
- /* append eos bucket and get out */
- b = apr_bucket_eos_create();
- APR_BRIGADE_INSERT_TAIL(bb, b);
- return APR_SUCCESS;
- }
- ctx->state = WANT_HDR;
- break;
- default:
- ap_assert(ctx->state == WANT_HDR || ctx->state == WANT_TRL);
}
- }
- } while (ctx->state != WANT_BODY);
+ else if (lenp) {
+ const char *pos = lenp;
- if (ctx->state == WANT_BODY) {
- /* Tell ap_http_filter() how many bytes to deliver. */
- apr_off_t chunk_bytes = ctx->chunk_size - ctx->bytes_delivered;
-
- if ((rv = ap_get_brigade(f->next, bb, mode,
- &chunk_bytes)) != APR_SUCCESS) {
- return rv;
- }
-
- /* Walk through the body, accounting for bytes, and removing an eos
- * bucket if ap_http_filter() delivered the entire chunk.
- *
- * ### this shouldn't be necessary. 1) ap_http_filter shouldn't be
- * ### adding EOS buckets. 2) it shouldn't return more bytes than
- * ### we requested, therefore the total len can be found with a
- * ### simple call to apr_brigade_length(). no further munging
- * ### would be needed.
- */
- b = APR_BRIGADE_FIRST(bb);
- while (b != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(b)) {
- apr_bucket_read(b, &buf, &len, mode);
- AP_DEBUG_ASSERT(len <= ctx->chunk_size - ctx->bytes_delivered);
- ctx->bytes_delivered += len;
- b = APR_BUCKET_NEXT(b);
- }
- if (ctx->bytes_delivered == ctx->chunk_size) {
- AP_DEBUG_ASSERT(APR_BUCKET_IS_EOS(b));
- apr_bucket_delete(b);
- ctx->state = WANT_TRL;
+ while (apr_isdigit(*pos) || apr_isspace(*pos))
+ ++pos;
+ if (*pos == '\0') {
+ ctx->state = BODY_LENGTH;
+ ctx->remaining = atol(lenp);
+ }
+ }
}
}
- return APR_SUCCESS;
-}
+ if (!ctx->remaining && ctx->state)
+ {
+ if (ctx->state == BODY_LENGTH)
+ {
+ e = apr_bucket_eos_create();
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ return APR_SUCCESS;
+ }
+ else if (ctx->state == BODY_CHUNK)
+ {
+ char line[30];
+
+ ctx->state = BODY_NONE;
-typedef struct http_filter_ctx {
- apr_bucket_brigade *b;
-} http_ctx_t;
+ /* We need to read the CRLF after the chunk. */
+ if ((rv = ap_getline(line, sizeof(line), f->r, 0)) < 0) {
+ return rv;
+ }
-apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t
mode, apr_off_t *readbytes)
-{
- apr_bucket *e;
- char *buff;
- apr_size_t len;
- char *pos;
- http_ctx_t *ctx = f->ctx;
- apr_status_t rv;
+ /* Read the real chunk line. */
+ if ((rv = ap_getline(line, sizeof(line), f->r, 0)) < 0) {
+ return rv;
+ }
+ ctx->state = BODY_CHUNK;
+ ctx->remaining = get_chunk_size(line);
- if (!ctx) {
- f->ctx = ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
- ctx->b = apr_brigade_create(f->c->pool);
+ if (!ctx->remaining)
+ {
+ e = apr_bucket_eos_create();
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ return APR_SUCCESS;
+ }
+ }
}
if (mode == AP_MODE_PEEK) {
@@ -640,12 +630,6 @@
}
}
- if (APR_BRIGADE_EMPTY(ctx->b)) {
- if ((rv = ap_get_brigade(f->next, ctx->b, mode, readbytes)) != APR_SUCCESS) {
- return rv;
- }
- }
-
/* If readbytes is -1, we want to just read everything until the end
* of the brigade, which in this case means the end of the socket. To
* do this, we loop through the entire brigade, until the socket is
@@ -655,14 +639,21 @@
if (*readbytes == -1) {
apr_bucket *e;
apr_off_t total;
+ const char *str;
+ apr_size_t len;
APR_BRIGADE_FOREACH(e, ctx->b) {
- const char *str;
- apr_size_t len;
+ /* We don't care about these values. We just want to force the
+ * lower level to just read it. */
apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
}
APR_BRIGADE_CONCAT(b, ctx->b);
+
+ /* Force a recompute of the length */
apr_brigade_length(b, 1, &total);
*readbytes = total;
+ /* This had better be equal to zero now! */
+ if (ctx->status)
+ ctx->remaining -= total;
e = apr_bucket_eos_create();
APR_BRIGADE_INSERT_TAIL(b, e);
return APR_SUCCESS;
@@ -670,56 +661,28 @@
/* readbytes == 0 is "read a single line". otherwise, read a block. */
if (*readbytes) {
apr_off_t total;
+ apr_bucket *e;
+ apr_bucket_brigade *newbb;
- /* ### the code below, which moves bytes from one brigade to the
- ### other is probably bogus. presuming the next filter down was
- ### working properly, it should not have returned more than
- ### READBYTES bytes, and we wouldn't have to do any work.
- */
-
- APR_BRIGADE_NORMALIZE(ctx->b);
- if (APR_BRIGADE_EMPTY(ctx->b)) {
- if ((rv = ap_get_brigade(f->next, ctx->b, mode, readbytes)) !=
APR_SUCCESS) {
- return rv;
- }
- }
-
+ newbb = NULL;
+
apr_brigade_partition(ctx->b, *readbytes, &e);
- APR_BRIGADE_CONCAT(b, ctx->b);
+ /* Must do split before CONCAT */
if (e != APR_BRIGADE_SENTINEL(ctx->b)) {
- apr_bucket_brigade *temp;
+ newbb = apr_brigade_split(ctx->b, e);
+ }
+ APR_BRIGADE_CONCAT(b, ctx->b);
- temp = apr_brigade_split(b, e);
+ /* FIXME: Is this really needed? Due to pointer use in sentinels,
+ * I think so. */
+ if (newbb)
+ APR_BRIGADE_CONCAT(ctx->b, newbb);
- /* ### darn. gotta ensure the split brigade is in the proper pool.
- ### this is a band-aid solution; we shouldn't even be doing
- ### all of this brigade munging (per the comment above).
- ### until then, this will get the right lifetimes. */
- APR_BRIGADE_CONCAT(ctx->b, temp);
- }
- else {
- if (!APR_BRIGADE_EMPTY(ctx->b)) {
- ctx->b = NULL; /*XXX*/
- }
- }
apr_brigade_length(b, 1, &total);
- *readbytes -= total;
+ *readbytes = total;
+ if (ctx->status)
+ ctx->remaining -= total;
- /* ### this is a hack. it is saying, "if we have read everything
- ### that was requested, then we are at the end of the request."
- ### it presumes that the next filter up will *only* call us
- ### with readbytes set to the Content-Length of the request.
- ### that may not always be true, and this code is *definitely*
- ### too presumptive of the caller's intent. the point is: this
- ### filter is not the guy that is parsing the headers or the
- ### chunks to determine where the end of the request is, so we
- ### shouldn't be monkeying with EOS buckets.
- */
- if (*readbytes == 0) {
- apr_bucket *eos = apr_bucket_eos_create();
-
- APR_BRIGADE_INSERT_TAIL(b, eos);
- }
return APR_SUCCESS;
}
@@ -731,14 +694,19 @@
}
pos = memchr(buff, APR_ASCII_LF, len);
+ /* We found a match. */
if (pos != NULL) {
apr_bucket_split(e, pos - buff + 1);
+ if (ctx->status)
+ ctx->remaining -= pos - buff + 1;
APR_BUCKET_REMOVE(e);
APR_BRIGADE_INSERT_TAIL(b, e);
return APR_SUCCESS;
}
APR_BUCKET_REMOVE(e);
APR_BRIGADE_INSERT_TAIL(b, e);
+ if (ctx->status)
+ ctx->remaining -= len;
}
return APR_SUCCESS;
}
@@ -1406,7 +1374,6 @@
}
r->read_chunked = 1;
- ap_add_input_filter("DECHUNK", NULL, r, r->connection);
}
else if (lenp) {
const char *pos = lenp;
@@ -1517,14 +1484,12 @@
*/
AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz)
{
- apr_size_t len_read, total;
- apr_status_t rv;
- apr_bucket *b, *old;
- const char *tempbuf;
+ apr_off_t len;
+ apr_bucket *b;
core_request_config *req_cfg =
(core_request_config *)ap_get_module_config(r->request_config,
&core_module);
- apr_bucket_brigade *bb = req_cfg->bb;
+ apr_bucket_brigade *bb = req_cfg->bb, *newbb;
do {
if (APR_BRIGADE_EMPTY(bb)) {
@@ -1546,41 +1511,34 @@
return 0;
}
- /* ### it would be nice to replace the code below with "consume N bytes
- ### from this brigade, placing them into that buffer." there are
- ### other places where we do the same...
- ###
- ### alternatively, we could partition the brigade, then call a
- ### function which serializes a given brigade into a buffer. that
- ### semantic is used elsewhere, too...
- */
-
- total = 0;
- while (total < bufsiz
- && b != APR_BRIGADE_SENTINEL(bb)
- && !APR_BUCKET_IS_EOS(b)) {
-
- if ((rv = apr_bucket_read(b, &tempbuf, &len_read, APR_BLOCK_READ)) !=
APR_SUCCESS) {
- return -1;
- }
- if (total + len_read > bufsiz) {
- apr_bucket_split(b, bufsiz - total);
- len_read = bufsiz - total;
- }
- memcpy(buffer, tempbuf, len_read);
- buffer += len_read;
- total += len_read;
- /* XXX the next two fields shouldn't be mucked with here, as they are in terms
- * of bytes in the unfiltered body; gotta see if anybody else actually uses
- * these
- */
- r->read_length += len_read; /* XXX yank me? */
- old = b;
- b = APR_BUCKET_NEXT(b);
- apr_bucket_delete(old);
- }
+ /* 1) Determine length to see if we may overrun.
+ * 2) Partition the brigade at the appropriate point.
+ * 3) Split the brigade at the new partition.
+ * 4) Read the old brigade into the buffer.
+ * 5) Destroy the old brigade.
+ * 6) Point the context at the new brigade.
+ */
+ apr_brigade_length(bb, 1, &len);
+
+ if (bufsiz < len)
+ len = bufsiz;
+
+ r->read_length += len;
+
+ if (apr_brigade_partition(bb, len, &b) != APR_SUCCESS)
+ return -1;
+
+ newbb = apr_brigade_split(bb, b);
+
+ if (apr_brigade_to_buffer(bb, buffer, APR_BLOCK_READ) != APR_SUCCESS)
+ return -1;
+
+ if (apr_brigade_destroy(bb) != APR_SUCCESS)
+ return -1;
+
+ req_cfg->bb = newbb;
- return total;
+ return len;
}
/* In HTTP/1.1, any method can have a body. However, most GET handlers
Index: perl-framework/c-modules/input_body_filter/mod_input_body_filter.c
===================================================================
RCS file:
/home/cvs/httpd-test/perl-framework/c-modules/input_body_filter/mod_input_body_filter.c,v
retrieving revision 1.4
diff -u -r1.4 mod_input_body_filter.c
--- perl-framework/c-modules/input_body_filter/mod_input_body_filter.c 2001/09/08
22:07:14 1.4
+++ perl-framework/c-modules/input_body_filter/mod_input_body_filter.c 2001/09/24
+01:53:15
@@ -76,25 +76,43 @@
}
}
+typedef struct input_body_ctx_t {
+ apr_bucket_brigade *b;
+} input_body_ctx_t;
+
static int input_body_filter_handler(ap_filter_t *f, apr_bucket_brigade *bb,
ap_input_mode_t mode, apr_off_t *readbytes)
{
apr_status_t rv;
apr_pool_t *p = f->r->pool;
+ input_body_ctx_t *ctx = f->ctx;
- if (APR_BRIGADE_EMPTY(bb)) {
- rv = ap_get_brigade(f->next, bb, mode, readbytes);
- if (rv != APR_SUCCESS) {
+ if (!ctx) {
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->b = apr_brigade_create(f->r->pool);
+ }
+
+ if (APR_BRIGADE_EMPTY(ctx->b))
+ {
+ if ((rv = ap_get_brigade(f->next, ctx->b, mode,
+ readbytes)) != APR_SUCCESS) {
return rv;
}
}
- while (!APR_BRIGADE_EMPTY(bb)) {
+ while (!APR_BRIGADE_EMPTY(ctx->b)) {
const char *data;
apr_size_t len;
apr_bucket *bucket;
- bucket = APR_BRIGADE_FIRST(bb);
+ bucket = APR_BRIGADE_FIRST(ctx->b);
+
+ if (APR_BUCKET_IS_EOS(bucket)) {
+ APR_BUCKET_REMOVE(bucket);
+ APR_BRIGADE_INSERT_TAIL(bb, bucket);
+ break;
+ }
+
rv = apr_bucket_read(bucket, &data, &len, mode);
if (rv != APR_SUCCESS) {
@@ -110,10 +128,6 @@
}
APR_BRIGADE_INSERT_TAIL(bb, bucket);
-
- if (APR_BUCKET_IS_EOS(bucket)) {
- break;
- }
}
return OK;