I came across a few things that needed attention in my review of the http filter code:
- ctx->remaining was uninitialized. - I'm a little uncomfortable with the Content-Length: parsing, please see my comments in the diff. - a logic reduction in a do-while loop (reduces the number of times we call APR_BRIGADE_EMPTY by half). - made the ctx->remaining condition check more robust. -aaron Index: modules/http/http_protocol.c =================================================================== RCS file: /home/cvspublic/httpd-2.0/modules/http/http_protocol.c,v retrieving revision 1.369 diff -u -r1.369 http_protocol.c --- modules/http/http_protocol.c 2001/10/02 21:13:41 1.369 +++ modules/http/http_protocol.c 2001/10/04 18:34:25 @@ -504,6 +504,7 @@ if (!ctx) { const char *tenc, *lenp; f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx)); + ctx->remaining = 0; ctx->state = BODY_NONE; tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding"); @@ -523,26 +524,32 @@ else if (lenp) { const char *pos = lenp; + /* XXX: Is this safe? I can do "Content-Length: 3 3 3" ... */ while (apr_isdigit(*pos) || apr_isspace(*pos)) ++pos; if (*pos == '\0') { ctx->state = BODY_LENGTH; ctx->remaining = atol(lenp); + /* XXX: Are there any platforms where apr_off_t != long? + aka: Would it be possible to overflow here? */ + /* XXX: Can we get here with an invalid number? (ie <0) */ } } } - if (!ctx->remaining) + if (ctx->remaining <= 0) { switch (ctx->state) { case BODY_NONE: break; case BODY_LENGTH: + /* We've already consumed the Content-length size, so flush. */ e = apr_bucket_eos_create(); APR_BRIGADE_INSERT_TAIL(b, e); return APR_SUCCESS; case BODY_CHUNK: + /* Done with this chunk, go get the next chunk length. */ { char line[30]; @@ -560,8 +567,7 @@ ctx->state = BODY_CHUNK; ctx->remaining = get_chunk_size(line); - if (!ctx->remaining) - { + if (ctx->remaining <= 0) { /* Handle trailers by calling get_mime_headers again! */ e = apr_bucket_eos_create(); APR_BRIGADE_INSERT_TAIL(b, e); @@ -572,13 +578,13 @@ } } - rv = ap_get_brigade(f->next, b, mode, readbytes); - - if (rv != APR_SUCCESS) + if ((rv = ap_get_brigade(f->next, b, mode, readbytes)) != APR_SUCCESS) { return rv; + } - if (ctx->state != BODY_NONE) + if (ctx->state != BODY_NONE) { ctx->remaining -= *readbytes; + } return APR_SUCCESS; } @@ -1360,19 +1366,17 @@ &core_module); apr_bucket_brigade *bb = req_cfg->bb; - do { - if (APR_BRIGADE_EMPTY(bb)) { - if (ap_get_brigade(r->input_filters, bb, AP_MODE_BLOCKING, - &r->remaining) != APR_SUCCESS) { - /* if we actually fail here, we want to just return and - * stop trying to read data from the client. - */ - r->connection->keepalive = -1; - apr_brigade_destroy(bb); - return -1; - } + while (APR_BRIGADE_EMPTY(bb)) { + if (ap_get_brigade(r->input_filters, bb, AP_MODE_BLOCKING, + &r->remaining) != APR_SUCCESS) { + /* if we actually fail here, we want to just return and + * stop trying to read data from the client. + */ + r->connection->keepalive = -1; + apr_brigade_destroy(bb); + return -1; } - } while (APR_BRIGADE_EMPTY(bb)); + } b = APR_BRIGADE_FIRST(bb); if (APR_BUCKET_IS_EOS(b)) { /* reached eos on previous invocation */