summaryrefslogtreecommitdiff
path: root/modules/http
diff options
context:
space:
mode:
Diffstat (limited to 'modules/http')
-rw-r--r--modules/http/byterange_filter.c21
-rw-r--r--modules/http/chunk_filter.c4
-rw-r--r--modules/http/http_core.c16
-rw-r--r--modules/http/http_etag.c27
-rw-r--r--modules/http/http_filters.c327
-rw-r--r--modules/http/http_protocol.c12
-rw-r--r--modules/http/mod_mime.c3
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;
}