diff options
Diffstat (limited to 'modules/http')
-rw-r--r-- | modules/http/byterange_filter.c | 21 | ||||
-rw-r--r-- | modules/http/chunk_filter.c | 4 | ||||
-rw-r--r-- | modules/http/http_core.c | 16 | ||||
-rw-r--r-- | modules/http/http_etag.c | 27 | ||||
-rw-r--r-- | modules/http/http_filters.c | 327 | ||||
-rw-r--r-- | modules/http/http_protocol.c | 12 | ||||
-rw-r--r-- | modules/http/mod_mime.c | 3 |
7 files changed, 300 insertions, 110 deletions
diff --git a/modules/http/byterange_filter.c b/modules/http/byterange_filter.c index 073e27e0..a25d1e59 100644 --- a/modules/http/byterange_filter.c +++ b/modules/http/byterange_filter.c @@ -193,12 +193,21 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, "byteranges; boundary=", ctx->boundary, NULL)); - ctx->bound_head = apr_pstrcat(r->pool, - CRLF "--", ctx->boundary, - CRLF "Content-type: ", - orig_ct, - CRLF "Content-range: bytes ", - NULL); + if (strcasecmp(orig_ct, NO_CONTENT_TYPE)) { + ctx->bound_head = apr_pstrcat(r->pool, + CRLF "--", ctx->boundary, + CRLF "Content-type: ", + orig_ct, + CRLF "Content-range: bytes ", + NULL); + } + else { + /* if we have no type for the content, do our best */ + ctx->bound_head = apr_pstrcat(r->pool, + CRLF "--", ctx->boundary, + CRLF "Content-range: bytes ", + NULL); + } ap_xlate_proto_to_ascii(ctx->bound_head, strlen(ctx->bound_head)); } diff --git a/modules/http/chunk_filter.c b/modules/http/chunk_filter.c index 2c94b3ca..b680185b 100644 --- a/modules/http/chunk_filter.c +++ b/modules/http/chunk_filter.c @@ -85,7 +85,9 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b) } if (APR_BUCKET_IS_FLUSH(e)) { flush = e; - more = apr_brigade_split(b, APR_BUCKET_NEXT(e)); + if (e != APR_BRIGADE_LAST(b)) { + more = apr_brigade_split(b, APR_BUCKET_NEXT(e)); + } break; } else if (e->length == (apr_size_t)-1) { diff --git a/modules/http/http_core.c b/modules/http/http_core.c index b52b5477..be1e1138 100644 --- a/modules/http/http_core.c +++ b/modules/http/http_core.c @@ -42,6 +42,8 @@ AP_DECLARE_DATA ap_filter_rec_t *ap_chunk_filter_handle; AP_DECLARE_DATA ap_filter_rec_t *ap_http_outerror_filter_handle; AP_DECLARE_DATA ap_filter_rec_t *ap_byterange_filter_handle; +static int ap_process_http_connection(conn_rec *c); + static const char *set_keep_alive_timeout(cmd_parms *cmd, void *dummy, const char *arg) { @@ -124,6 +126,10 @@ static int ap_process_http_async_connection(conn_rec *c) request_rec *r; conn_state_t *cs = c->cs; + if (c->clogging_input_filters) { + return ap_process_http_connection(c); + } + AP_DEBUG_ASSERT(cs->state == CONN_STATE_READ_REQUEST_LINE); while (cs->state == CONN_STATE_READ_REQUEST_LINE) { @@ -222,6 +228,15 @@ static int http_create_request(request_rec *r) return OK; } +static int http_send_options(request_rec *r) +{ + if ((r->method_number == M_OPTIONS) && r->uri && (r->uri[0] == '*') && + (r->uri[1] == '\0')) { + return DONE; /* Send HTTP pong, without Allow header */ + } + return DECLINED; +} + static void register_hooks(apr_pool_t *p) { /** @@ -240,6 +255,7 @@ static void register_hooks(apr_pool_t *p) } ap_hook_map_to_storage(ap_send_http_trace,NULL,NULL,APR_HOOK_MIDDLE); + ap_hook_map_to_storage(http_send_options,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_http_scheme(http_scheme,NULL,NULL,APR_HOOK_REALLY_LAST); ap_hook_default_port(http_port,NULL,NULL,APR_HOOK_REALLY_LAST); ap_hook_create_request(http_create_request, NULL, NULL, APR_HOOK_REALLY_LAST); diff --git a/modules/http/http_etag.c b/modules/http/http_etag.c index c2dd8813..a7d3d9c0 100644 --- a/modules/http/http_etag.c +++ b/modules/http/http_etag.c @@ -28,16 +28,17 @@ #include "http_protocol.h" /* For index_of_response(). Grump. */ #include "http_request.h" -/* Generate the human-readable hex representation of an unsigned long - * (basically a faster version of 'sprintf("%lx")') +/* Generate the human-readable hex representation of an apr_uint64_t + * (basically a faster version of 'sprintf("%llx")') */ #define HEX_DIGITS "0123456789abcdef" -static char *etag_ulong_to_hex(char *next, unsigned long u) +static char *etag_uint64_to_hex(char *next, apr_uint64_t u) { int printing = 0; - int shift = sizeof(unsigned long) * 8 - 4; + int shift = sizeof(apr_uint64_t) * 8 - 4; do { - unsigned long next_digit = ((u >> shift) & (unsigned long)0xf); + unsigned short next_digit = (unsigned short) + ((u >> shift) & (apr_uint64_t)0xf); if (next_digit) { *next++ = HEX_DIGITS[next_digit]; printing = 1; @@ -47,12 +48,12 @@ static char *etag_ulong_to_hex(char *next, unsigned long u) } shift -= 4; } while (shift); - *next++ = HEX_DIGITS[u & (unsigned long)0xf]; + *next++ = HEX_DIGITS[u & (apr_uint64_t)0xf]; return next; } #define ETAG_WEAK "W/" -#define CHARS_PER_UNSIGNED_LONG (sizeof(unsigned long) * 2) +#define CHARS_PER_UINT64 (sizeof(apr_uint64_t) * 2) /* * Construct an entity tag (ETag) from resource information. If it's a real * file, build in some of the file characteristics. If the modification time @@ -115,7 +116,7 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak) * FileETag keywords. */ etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") + - 3 * CHARS_PER_UNSIGNED_LONG + 1); + 3 * CHARS_PER_UINT64 + 1); next = etag; if (weak) { while (*weak) { @@ -125,21 +126,21 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak) *next++ = '"'; bits_added = 0; if (etag_bits & ETAG_INODE) { - next = etag_ulong_to_hex(next, (unsigned long)r->finfo.inode); + next = etag_uint64_to_hex(next, r->finfo.inode); bits_added |= ETAG_INODE; } if (etag_bits & ETAG_SIZE) { if (bits_added != 0) { *next++ = '-'; } - next = etag_ulong_to_hex(next, (unsigned long)r->finfo.size); + next = etag_uint64_to_hex(next, r->finfo.size); bits_added |= ETAG_SIZE; } if (etag_bits & ETAG_MTIME) { if (bits_added != 0) { *next++ = '-'; } - next = etag_ulong_to_hex(next, (unsigned long)r->mtime); + next = etag_uint64_to_hex(next, r->mtime); } *next++ = '"'; *next = '\0'; @@ -149,7 +150,7 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak) * Not a file document, so just use the mtime: [W/]"mtime" */ etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") + - CHARS_PER_UNSIGNED_LONG + 1); + CHARS_PER_UINT64 + 1); next = etag; if (weak) { while (*weak) { @@ -157,7 +158,7 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak) } } *next++ = '"'; - next = etag_ulong_to_hex(next, (unsigned long)r->mtime); + next = etag_uint64_to_hex(next, r->mtime); *next++ = '"'; *next = '\0'; } diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c index 185f50f8..7ad07ad6 100644 --- a/modules/http/http_filters.c +++ b/modules/http/http_filters.c @@ -55,6 +55,8 @@ #include <unistd.h> #endif +#define INVALID_CHAR -2 + static long get_chunk_size(char *); typedef struct http_filter_ctx { @@ -64,11 +66,145 @@ typedef struct http_filter_ctx { enum { BODY_NONE, BODY_LENGTH, - BODY_CHUNK + BODY_CHUNK, + BODY_CHUNK_PART } state; int eos_sent; + char chunk_ln[32]; + char *pos; + apr_off_t linesize; + apr_bucket_brigade *bb; } http_ctx_t; +static apr_status_t bail_out_on_error(http_ctx_t *ctx, + ap_filter_t *f, + int http_error) +{ + apr_bucket *e; + apr_bucket_brigade *bb = ctx->bb; + + apr_brigade_cleanup(bb); + e = ap_bucket_error_create(http_error, + 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); +} + +static apr_status_t get_remaining_chunk_line(http_ctx_t *ctx, + apr_bucket_brigade *b, + int linelimit) +{ + apr_status_t rv; + apr_off_t brigade_length; + apr_bucket *e; + const char *lineend; + apr_size_t len; + + /* + * As the brigade b should have been requested in mode AP_MODE_GETLINE + * all buckets in this brigade are already some type of memory + * buckets (due to the needed scanning for LF in mode AP_MODE_GETLINE) + * or META buckets. + */ + rv = apr_brigade_length(b, 0, &brigade_length); + if (rv != APR_SUCCESS) { + return rv; + } + /* Sanity check. Should never happen. See above. */ + if (brigade_length == -1) { + return APR_EGENERAL; + } + if (!brigade_length) { + return APR_EAGAIN; + } + ctx->linesize += brigade_length; + if (ctx->linesize > linelimit) { + return APR_ENOSPC; + } + /* + * As all buckets are already some type of memory buckets or META buckets + * (see above), we only need to check the last byte in the last data bucket. + */ + for (e = APR_BRIGADE_LAST(b); + e != APR_BRIGADE_SENTINEL(b); + e = APR_BUCKET_PREV(e)) { + + if (APR_BUCKET_IS_METADATA(e)) { + continue; + } + rv = apr_bucket_read(e, &lineend, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + return rv; + } + if (len > 0) { + break; /* we got the data we want */ + } + /* If we got a zero-length data bucket, we try the next one */ + } + /* We had no data in this brigade */ + if (!len || e == APR_BRIGADE_SENTINEL(b)) { + return APR_EAGAIN; + } + if (lineend[len - 1] != APR_ASCII_LF) { + return APR_EAGAIN; + } + /* Line is complete. So reset ctx->linesize for next round. */ + ctx->linesize = 0; + return APR_SUCCESS; +} + +static apr_status_t get_chunk_line(http_ctx_t *ctx, apr_bucket_brigade *b, + int linelimit) +{ + apr_size_t len; + int tmp_len; + apr_status_t rv; + + tmp_len = sizeof(ctx->chunk_ln) - (ctx->pos - ctx->chunk_ln) - 1; + /* Saveguard ourselves against underflows */ + if (tmp_len < 0) { + len = 0; + } + else { + len = (apr_size_t) tmp_len; + } + /* + * Check if there is space left in ctx->chunk_ln. If not, then either + * the chunk size is insane or we have chunk-extensions. Ignore both + * by discarding the remaining part of the line via + * get_remaining_chunk_line. Only bail out if the line is too long. + */ + if (len > 0) { + rv = apr_brigade_flatten(b, ctx->pos, &len); + if (rv != APR_SUCCESS) { + return rv; + } + ctx->pos += len; + ctx->linesize += len; + *(ctx->pos) = '\0'; + /* + * Check if we really got a full line. If yes the + * last char in the just read buffer must be LF. + * If not advance the buffer and return APR_EAGAIN. + * We do not start processing until we have the + * full line. + */ + if (ctx->pos[-1] != APR_ASCII_LF) { + /* Check if the remaining data in the brigade has the LF */ + return get_remaining_chunk_line(ctx, b, linelimit); + } + /* Line is complete. So reset ctx->pos for next round. */ + ctx->pos = ctx->chunk_ln; + return APR_SUCCESS; + } + return get_remaining_chunk_line(ctx, b, linelimit); +} + + /* This is the HTTP_INPUT filter for HTTP requests and responses from * proxied servers (mod_proxy). It handles chunked and content-length * bodies. This can only be inserted/used after the headers @@ -82,6 +218,8 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, http_ctx_t *ctx = f->ctx; apr_status_t rv; apr_off_t totalread; + int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE; + apr_bucket_brigade *bb; /* just get out of the way of things we don't want. */ if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) { @@ -90,11 +228,11 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, if (!ctx) { const char *tenc, *lenp; - f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx)); + f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); ctx->state = BODY_NONE; - ctx->remaining = 0; - ctx->limit_used = 0; - ctx->eos_sent = 0; + ctx->pos = ctx->chunk_ln; + ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + bb = ctx->bb; /* LimitRequestBody does not apply to proxied responses. * Consider implementing this check in its own filter. @@ -115,8 +253,22 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, if (!strcasecmp(tenc, "chunked")) { ctx->state = BODY_CHUNK; } + /* test lenp, because it gives another case we can handle */ + else if (!lenp) { + /* Something that isn't in HTTP, unless some future + * edition defines new transfer ecodings, is unsupported. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, + "Unknown Transfer-Encoding: %s", tenc); + return bail_out_on_error(ctx, f, HTTP_NOT_IMPLEMENTED); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, + "Unknown Transfer-Encoding: %s; using Content-Length", tenc); + tenc = NULL; + } } - else if (lenp) { + if (lenp && !tenc) { char *endstr; ctx->state = BODY_LENGTH; @@ -127,39 +279,23 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, * and a negative number. */ if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10) || endstr == lenp || *endstr || ctx->remaining < 0) { - apr_bucket_brigade *bb; ctx->remaining = 0; 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); + return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE); } /* If we have a limit in effect and we know the C-L ahead of * time, stop it here if it is invalid. */ if (ctx->limit && ctx->limit < ctx->remaining) { - apr_bucket_brigade *bb; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "Requested content-length of %" APR_OFF_T_FMT " is larger than the configured limit" " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit); - 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); + return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE); } } @@ -185,13 +321,13 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, * Only valid on chunked and C-L bodies where the C-L is > 0. */ if ((ctx->state == BODY_CHUNK || (ctx->state == BODY_LENGTH && ctx->remaining > 0)) && - f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1)) { + f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1) && + !(f->r->eos_sent || f->r->bytes_sent)) { char *tmp; - apr_bucket_brigade *bb; tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ", ap_get_status_line(100), CRLF CRLF, NULL); - bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + apr_brigade_cleanup(bb); e = apr_bucket_pool_create(tmp, strlen(tmp), f->r->pool, f->c->bucket_alloc); APR_BRIGADE_INSERT_HEAD(bb, e); @@ -203,30 +339,31 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, /* We can't read the chunk until after sending 100 if required. */ if (ctx->state == BODY_CHUNK) { - char line[30]; - apr_bucket_brigade *bb; - apr_size_t len = 30; - apr_off_t brigade_length; - - bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + apr_brigade_cleanup(bb); rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, - APR_BLOCK_READ, 0); + block, 0); + + /* for timeout */ + if (block == APR_NONBLOCK_READ && + ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || + (APR_STATUS_IS_EAGAIN(rv)) )) { + ctx->state = BODY_CHUNK_PART; + return APR_EAGAIN; + } if (rv == APR_SUCCESS) { - /* We have to check the length of the brigade we got back. - * We will not accept partial or blank lines. - */ - rv = apr_brigade_length(bb, 1, &brigade_length); - if (rv == APR_SUCCESS - && (!brigade_length || - brigade_length > f->r->server->limit_req_line)) { - rv = APR_ENOSPC; + rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line); + if (APR_STATUS_IS_EAGAIN(rv)) { + apr_brigade_cleanup(bb); + ctx->state = BODY_CHUNK_PART; + return rv; } if (rv == APR_SUCCESS) { - rv = apr_brigade_flatten(bb, line, &len); - if (rv == APR_SUCCESS) { - ctx->remaining = get_chunk_size(line); + ctx->remaining = get_chunk_size(ctx->chunk_ln); + if (ctx->remaining == INVALID_CHAR) { + rv = APR_EGENERAL; + http_error = HTTP_SERVICE_UNAVAILABLE; } } } @@ -236,14 +373,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, if (rv != APR_SUCCESS || ctx->remaining < 0) { ctx->remaining = 0; /* Reset it in case we have to * come back here later */ - 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); + return bail_out_on_error(ctx, f, http_error); } if (!ctx->remaining) { @@ -257,6 +387,9 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, } } } + else { + bb = ctx->bb; + } if (ctx->eos_sent) { e = apr_bucket_eos_create(f->c->bucket_alloc); @@ -274,34 +407,60 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ctx->eos_sent = 1; return APR_SUCCESS; case BODY_CHUNK: + case BODY_CHUNK_PART: { - char line[30]; - apr_bucket_brigade *bb; - apr_size_t len = 30; - apr_status_t http_error = HTTP_REQUEST_ENTITY_TOO_LARGE; - - bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + apr_brigade_cleanup(bb); /* We need to read the CRLF after the chunk. */ - rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, - APR_BLOCK_READ, 0); - apr_brigade_cleanup(bb); + if (ctx->state == BODY_CHUNK) { + rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, + block, 0); + if (block == APR_NONBLOCK_READ && + ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || + (APR_STATUS_IS_EAGAIN(rv)) )) { + return APR_EAGAIN; + } + /* + * We really don't care whats on this line. If it is RFC + * compliant it should be only \r\n. If there is more + * before we just ignore it as long as we do not get over + * the limit for request lines. + */ + rv = get_remaining_chunk_line(ctx, bb, + f->r->server->limit_req_line); + apr_brigade_cleanup(bb); + if (APR_STATUS_IS_EAGAIN(rv)) { + return rv; + } + } else { + rv = APR_SUCCESS; + } if (rv == APR_SUCCESS) { /* Read the real chunk line. */ rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, - APR_BLOCK_READ, 0); + block, 0); + /* Test timeout */ + if (block == APR_NONBLOCK_READ && + ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || + (APR_STATUS_IS_EAGAIN(rv)) )) { + ctx->state = BODY_CHUNK_PART; + return APR_EAGAIN; + } + ctx->state = BODY_CHUNK; if (rv == APR_SUCCESS) { - rv = apr_brigade_flatten(bb, line, &len); + rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line); + if (APR_STATUS_IS_EAGAIN(rv)) { + ctx->state = BODY_CHUNK_PART; + apr_brigade_cleanup(bb); + return rv; + } if (rv == APR_SUCCESS) { - /* Wait a sec, that's a blank line! Oh no. */ - if (!len) { + ctx->remaining = get_chunk_size(ctx->chunk_ln); + if (ctx->remaining == INVALID_CHAR) { rv = APR_EGENERAL; http_error = HTTP_SERVICE_UNAVAILABLE; } - else { - ctx->remaining = get_chunk_size(line); - } } } apr_brigade_cleanup(bb); @@ -309,18 +468,9 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, /* Detect chunksize error (such as overflow) */ if (rv != APR_SUCCESS || ctx->remaining < 0) { - apr_status_t out_error; - ctx->remaining = 0; /* Reset it in case we have to * come back here later */ - e = ap_bucket_error_create(http_error, - 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; - out_error = ap_pass_brigade(f->r->output_filters, bb); + bail_out_on_error(ctx, f, http_error); return rv; } @@ -378,12 +528,11 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, * really count. This seems to be up for interpretation. */ ctx->limit_used += totalread; if (ctx->limit < ctx->limit_used) { - apr_bucket_brigade *bb; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "Read content-length of %" APR_OFF_T_FMT " is larger than the configured limit" " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit); - bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + apr_brigade_cleanup(bb); e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL, f->r->pool, f->c->bucket_alloc); @@ -414,6 +563,13 @@ static long get_chunk_size(char *b) ap_xlate_proto_from_ascii(b, strlen(b)); + if (!apr_isxdigit(*b)) { + /* + * Detect invalid character at beginning. This also works for empty + * chunk size lines. + */ + return INVALID_CHAR; + } /* Skip leading zeros */ while (*b == '0') { ++b; @@ -831,7 +987,7 @@ AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r) if (conf->trace_enable == AP_TRACE_DISABLE) { apr_table_setn(r->notes, "error-notes", "TRACE denied by server configuration"); - return HTTP_FORBIDDEN; + return HTTP_METHOD_NOT_ALLOWED; } if (conf->trace_enable == AP_TRACE_EXTENDED) @@ -925,6 +1081,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_bucket_brigade *b2; header_struct h; header_filter_ctx *ctx = f->ctx; + const char *ctype; AP_DEBUG_ASSERT(!r->main); @@ -1000,8 +1157,10 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_table_unset(r->headers_out, "Content-Length"); } - apr_table_setn(r->headers_out, "Content-Type", - ap_make_content_type(r, r->content_type)); + ctype = ap_make_content_type(r, r->content_type); + if (strcasecmp(ctype, NO_CONTENT_TYPE)) { + apr_table_setn(r->headers_out, "Content-Type", ctype); + } if (r->content_encoding) { apr_table_setn(r->headers_out, "Content-Encoding", diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index d8886af1..87f3f307 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -48,6 +48,7 @@ #include "util_charset.h" #include "util_ebcdic.h" #include "util_time.h" +#include "ap_mpm.h" #include "mod_core.h" @@ -187,6 +188,7 @@ AP_DECLARE(int) ap_set_keepalive(request_rec *r) * or they're a buggy twit coming through a HTTP/1.1 proxy * and the client is requesting an HTTP/1.0-style keep-alive * or the client claims to be HTTP/1.1 compliant (perhaps a proxy); + * and this MPM process is not already exiting * THEN we can be persistent, which requires more headers be output. * * Note that the condition evaluation order is extremely important. @@ -212,7 +214,8 @@ AP_DECLARE(int) ap_set_keepalive(request_rec *r) && (!apr_table_get(r->subprocess_env, "nokeepalive") || apr_table_get(r->headers_in, "Via")) && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive")) - || (r->proto_num >= HTTP_VERSION(1,1)))) { + || (r->proto_num >= HTTP_VERSION(1,1))) + && !ap_graceful_stop_signalled()) { int left = r->server->keep_alive_max - r->connection->keepalives; r->connection->keepalive = AP_CONN_KEEPALIVE; @@ -910,7 +913,8 @@ static const char *get_canned_error_string(int status, NULL)); case HTTP_METHOD_NOT_ALLOWED: return(apr_pstrcat(p, - "<p>The requested method ", r->method, + "<p>The requested method ", + ap_escape_html(r->pool, r->method), " is not allowed for the URL ", ap_escape_html(r->pool, r->uri), ".</p>\n", @@ -928,7 +932,7 @@ static const char *get_canned_error_string(int status, case HTTP_LENGTH_REQUIRED: s1 = apr_pstrcat(p, "<p>A request of the requested method ", - r->method, + ap_escape_html(r->pool, r->method), " requires a valid Content-length.<br />\n", NULL); return(add_optional_notes(r, s1, "error-notes", "</p>\n")); @@ -975,7 +979,7 @@ static const char *get_canned_error_string(int status, "The requested resource<br />", ap_escape_html(r->pool, r->uri), "<br />\n", "does not allow request data with ", - r->method, + ap_escape_html(r->pool, r->method), " requests, or the amount of data provided in\n" "the request exceeds the capacity limit.\n", NULL)); diff --git a/modules/http/mod_mime.c b/modules/http/mod_mime.c index d122d05b..d9d8b101 100644 --- a/modules/http/mod_mime.c +++ b/modules/http/mod_mime.c @@ -140,11 +140,10 @@ static void *overlay_extension_mappings(apr_pool_t *p, const void *base_val, const void *data) { - extension_info *new_info = apr_palloc(p, sizeof(extension_info)); const extension_info *overlay_info = (const extension_info *)overlay_val; const extension_info *base_info = (const extension_info *)base_val; + extension_info *new_info = apr_pmemdup(p, base_info, sizeof(extension_info)); - memcpy(new_info, base_info, sizeof(extension_info)); if (overlay_info->forced_type) { new_info->forced_type = overlay_info->forced_type; } |