diff options
Diffstat (limited to 'modules/http/http_protocol.c')
-rw-r--r-- | modules/http/http_protocol.c | 137 |
1 files changed, 104 insertions, 33 deletions
diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index 3ffe91a91d..4bbcf1562e 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -794,14 +794,39 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, } else if (lenp) { const char *pos = lenp; + int conversion_error = 0; + /* This ensures that the number can not be negative. */ while (apr_isdigit(*pos) || apr_isspace(*pos)) { ++pos; } if (*pos == '\0') { + char *endstr; + + errno = 0; ctx->state = BODY_LENGTH; - ctx->remaining = atol(lenp); + ctx->remaining = strtol(lenp, &endstr, 10); + + if (errno || (endstr && *endstr)) { + conversion_error = 1; + } + } + + if (*pos != '\0' || conversion_error) { + apr_bucket_brigade *bb; + + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, + "Invalid Content-Length"); + + bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL, + f->r->pool, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + e = apr_bucket_eos_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + ctx->eos_sent = 1; + return ap_pass_brigade(f->r->output_filters, bb); } /* If we have a limit in effect and we know the C-L ahead of @@ -1683,17 +1708,28 @@ AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy) } else if (lenp) { const char *pos = lenp; + int conversion_error = 0; while (apr_isdigit(*pos) || apr_isspace(*pos)) { ++pos; } - if (*pos != '\0') { + + if (*pos == '\0') { + char *endstr; + + errno = 0; + r->remaining = strtol(lenp, &endstr, 10); + + if (errno || (endstr && *endstr)) { + conversion_error = 1; + } + } + + if (*pos != '\0' || conversion_error) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Invalid Content-Length %s", lenp); + "Invalid Content-Length"); return HTTP_BAD_REQUEST; } - - r->remaining = atol(lenp); } if ((r->read_body == REQUEST_NO_BODY) @@ -1771,44 +1807,79 @@ static long get_chunk_size(char *b) * to read past the data provided by the client, since these reads block. * Returns 0 on End-of-body, -1 on error or premature chunk end. * + * Reading the chunked encoding requires a buffer size large enough to + * hold a chunk-size line, including any extensions. For now, we'll leave + * that to the caller, at least until we can come up with a better solution. */ AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz) { + apr_size_t total; apr_status_t rv; - apr_bucket_brigade *bb; - - bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - - rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, - APR_BLOCK_READ, bufsiz); - - /* We lose the failure code here. This is why ap_get_client_block should - * not be used. - */ - if (rv != 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; - return -1; + apr_bucket *b; + const char *tempbuf; + core_request_config *req_cfg = + (core_request_config *)ap_get_module_config(r->request_config, + &core_module); + apr_bucket_brigade *bb = req_cfg->bb; + + /* read until we get a non-empty brigade */ + while (APR_BRIGADE_EMPTY(bb)) { + if (ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, + APR_BLOCK_READ, bufsiz) != 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; + } } - /* If this fails, it means that a filter is written incorrectly and that - * it needs to learn how to properly handle APR_BLOCK_READ requests by - * returning data when requested. - */ - AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(bb)); - - rv = apr_brigade_flatten(bb, buffer, &bufsiz); - if (rv != APR_SUCCESS) { - return -1; + b = APR_BRIGADE_FIRST(bb); + if (APR_BUCKET_IS_EOS(b)) { /* reached eos on previous invocation */ + apr_bucket_delete(b); + return 0; } - /* XXX yank me? */ - r->read_length += bufsiz; + /* ### 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)) { + apr_size_t len_read; + apr_bucket *old; + + 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 field shouldn't be mucked with here, + * as it is in terms of bytes in the unfiltered body; + * gotta see if anybody else actually uses it + */ + r->read_length += len_read; /* XXX yank me? */ + old = b; + b = APR_BUCKET_NEXT(b); + apr_bucket_delete(old); + } - return bufsiz; + return total; } /* In HTTP/1.1, any method can have a body. However, most GET handlers |