From c54018b07a9085c0a3aedbc2bd01a85a3b3e20cf Mon Sep 17 00:00:00 2001
From: Daniel Baumann
Date: Sat, 25 May 2024 06:41:27 +0200
Subject: Merging upstream version 2.4.59.
Signed-off-by: Daniel Baumann
---
modules/http/byterange_filter.c | 7 +-
modules/http/http_core.c | 16 +-
modules/http/http_etag.c | 349 +++++++++++++++++++++++++++++++---------
modules/http/http_filters.c | 290 ++++++++++++++++++---------------
modules/http/http_protocol.c | 128 +++++++--------
modules/http/http_request.c | 33 ++--
modules/http/mod_mime.c | 19 ++-
7 files changed, 532 insertions(+), 310 deletions(-)
(limited to 'modules/http')
diff --git a/modules/http/byterange_filter.c b/modules/http/byterange_filter.c
index de585c5..5ebe853 100644
--- a/modules/http/byterange_filter.c
+++ b/modules/http/byterange_filter.c
@@ -152,7 +152,6 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
*indexes = apr_array_make(r->pool, ranges, sizeof(indexes_t));
while ((cur = ap_getword(r->pool, &range, ','))) {
char *dash;
- char *errp;
apr_off_t number, start, end;
if (!*cur)
@@ -169,7 +168,7 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
if (dash == cur) {
/* In the form "-5" */
- if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
+ if (!ap_parse_strict_length(&number, dash+1)) {
return 0;
}
if (number < 1) {
@@ -180,12 +179,12 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
}
else {
*dash++ = '\0';
- if (apr_strtoff(&number, cur, &errp, 10) || *errp) {
+ if (!ap_parse_strict_length(&number, cur)) {
return 0;
}
start = number;
if (*dash) {
- if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
+ if (!ap_parse_strict_length(&number, dash)) {
return 0;
}
end = number;
diff --git a/modules/http/http_core.c b/modules/http/http_core.c
index 35869b4..c6cb473 100644
--- a/modules/http/http_core.c
+++ b/modules/http/http_core.c
@@ -140,16 +140,17 @@ static int ap_process_http_async_connection(conn_rec *c)
AP_DEBUG_ASSERT(cs != NULL);
AP_DEBUG_ASSERT(cs->state == CONN_STATE_READ_REQUEST_LINE);
- while (cs->state == CONN_STATE_READ_REQUEST_LINE) {
+ if (cs->state == CONN_STATE_READ_REQUEST_LINE) {
ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
-
+ if (ap_extended_status) {
+ ap_set_conn_count(c->sbh, r, c->keepalives);
+ }
if ((r = ap_read_request(c))) {
-
- c->keepalive = AP_CONN_UNKNOWN;
- /* process the request if it was read without error */
-
if (r->status == HTTP_OK) {
cs->state = CONN_STATE_HANDLER;
+ if (ap_extended_status) {
+ ap_set_conn_count(c->sbh, r, c->keepalives + 1);
+ }
ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
ap_process_async_request(r);
/* After the call to ap_process_request, the
@@ -200,9 +201,6 @@ static int ap_process_http_sync_connection(conn_rec *c)
keep_alive_timeout = c->base_server->keep_alive_timeout;
}
- c->keepalive = AP_CONN_UNKNOWN;
- /* process the request if it was read without error */
-
if (r->status == HTTP_OK) {
if (cs)
cs->state = CONN_STATE_HANDLER;
diff --git a/modules/http/http_etag.c b/modules/http/http_etag.c
index 7f3c6d9..af74549 100644
--- a/modules/http/http_etag.c
+++ b/modules/http/http_etag.c
@@ -16,6 +16,9 @@
#include "apr_strings.h"
#include "apr_thread_proc.h" /* for RLIMIT stuff */
+#include "apr_sha1.h"
+#include "apr_base64.h"
+#include "apr_buckets.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
@@ -24,9 +27,16 @@
#include "http_config.h"
#include "http_connection.h"
#include "http_core.h"
+#include "http_log.h"
#include "http_protocol.h" /* For index_of_response(). Grump. */
#include "http_request.h"
+#if APR_HAS_MMAP
+#include "apr_mmap.h"
+#endif /* APR_HAS_MMAP */
+
+#define SHA1_DIGEST_BASE64_LEN 4*(APR_SHA1_DIGESTSIZE/3)
+
/* Generate the human-readable hex representation of an apr_uint64_t
* (basically a faster version of 'sprintf("%llx")')
*/
@@ -53,19 +63,159 @@ static char *etag_uint64_to_hex(char *next, apr_uint64_t u)
#define ETAG_WEAK "W/"
#define CHARS_PER_UINT64 (sizeof(apr_uint64_t) * 2)
+
+static void etag_start(char *etag, const char *weak, char **next)
+{
+ if (weak) {
+ while (*weak) {
+ *etag++ = *weak++;
+ }
+ }
+ *etag++ = '"';
+
+ *next = etag;
+}
+
+static void etag_end(char *next, const char *vlv, apr_size_t vlv_len)
+{
+ if (vlv) {
+ *next++ = ';';
+ apr_cpystrn(next, vlv, vlv_len);
+ }
+ else {
+ *next++ = '"';
+ *next = '\0';
+ }
+}
+
+/*
+ * Construct a strong ETag by creating a SHA1 hash across the file content.
+ */
+static char *make_digest_etag(request_rec *r, etag_rec *er, char *vlv,
+ apr_size_t vlv_len, char *weak, apr_size_t weak_len)
+{
+ apr_sha1_ctx_t context;
+ unsigned char digest[APR_SHA1_DIGESTSIZE];
+ apr_file_t *fd = NULL;
+ core_dir_config *cfg;
+ char *etag, *next;
+ apr_bucket_brigade *bb;
+ apr_bucket *e;
+
+ apr_size_t nbytes;
+ apr_off_t offset = 0, zero = 0, len = 0;
+ apr_status_t status;
+
+ cfg = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
+
+ if (er->fd) {
+ fd = er->fd;
+ }
+ else if (er->pathname) {
+ if ((status = apr_file_open(&fd, er->pathname, APR_READ | APR_BINARY,
+ 0, r->pool)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10251)
+ "Make etag: could not open %s", er->pathname);
+ return "";
+ }
+ }
+ if (!fd) {
+ return "";
+ }
+
+ if ((status = apr_file_seek(fd, APR_CUR, &offset)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10252)
+ "Make etag: could not seek");
+ if (er->pathname) {
+ apr_file_close(fd);
+ }
+ return "";
+ }
+
+ if ((status = apr_file_seek(fd, APR_END, &len)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10258)
+ "Make etag: could not seek");
+ if (er->pathname) {
+ apr_file_close(fd);
+ }
+ return "";
+ }
+
+ if ((status = apr_file_seek(fd, APR_SET, &zero)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10253)
+ "Make etag: could not seek");
+ if (er->pathname) {
+ apr_file_close(fd);
+ }
+ return "";
+ }
+
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ e = apr_brigade_insert_file(bb, fd, 0, len, r->pool);
+
+#if APR_HAS_MMAP
+ if (cfg->enable_mmap == ENABLE_MMAP_OFF) {
+ (void)apr_bucket_file_enable_mmap(e, 0);
+ }
+#endif
+
+ apr_sha1_init(&context);
+ while (!APR_BRIGADE_EMPTY(bb))
+ {
+ const char *str;
+
+ e = APR_BRIGADE_FIRST(bb);
+
+ if ((status = apr_bucket_read(e, &str, &nbytes, APR_BLOCK_READ)) != APR_SUCCESS) {
+ apr_brigade_destroy(bb);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10254)
+ "Make etag: could not read");
+ if (er->pathname) {
+ apr_file_close(fd);
+ }
+ return "";
+ }
+
+ apr_sha1_update(&context, str, nbytes);
+ apr_bucket_delete(e);
+ }
+
+ if ((status = apr_file_seek(fd, APR_SET, &offset)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(10255)
+ "Make etag: could not seek");
+ if (er->pathname) {
+ apr_file_close(fd);
+ }
+ return "";
+ }
+ apr_sha1_final(digest, &context);
+
+ etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
+ SHA1_DIGEST_BASE64_LEN + vlv_len + 4);
+
+ etag_start(etag, weak, &next);
+ next += apr_base64_encode_binary(next, digest, APR_SHA1_DIGESTSIZE) - 1;
+ etag_end(next, vlv, vlv_len);
+
+ if (er->pathname) {
+ apr_file_close(fd);
+ }
+
+ return etag;
+}
+
/*
* 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
* is newer than (request-time minus 1 second), mark the ETag as weak - it
- * could be modified again in as short an interval. We rationalize the
- * modification time we're given to keep it from being in the future.
+ * could be modified again in as short an interval.
*/
-AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
+AP_DECLARE(char *) ap_make_etag_ex(request_rec *r, etag_rec *er)
{
- char *weak;
- apr_size_t weak_len;
- char *etag;
- char *next;
+ char *weak = NULL;
+ apr_size_t weak_len = 0, vlv_len = 0;
+ char *etag, *next, *vlv;
core_dir_config *cfg;
etag_components_t etag_bits;
etag_components_t bits_added;
@@ -73,13 +223,62 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
cfg = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
+ if (er->force_weak) {
+ weak = ETAG_WEAK;
+ weak_len = sizeof(ETAG_WEAK);
+ }
+
+ if (r->vlist_validator) {
+
+ /* If we have a variant list validator (vlv) due to the
+ * response being negotiated, then we create a structured
+ * entity tag which merges the variant etag with the variant
+ * list validator (vlv). This merging makes revalidation
+ * somewhat safer, ensures that caches which can deal with
+ * Vary will (eventually) be updated if the set of variants is
+ * changed, and is also a protocol requirement for transparent
+ * content negotiation.
+ */
+
+ /* if the variant list validator is weak, we make the whole
+ * structured etag weak. If we would not, then clients could
+ * have problems merging range responses if we have different
+ * variants with the same non-globally-unique strong etag.
+ */
+
+ vlv = r->vlist_validator;
+ if (vlv[0] == 'W') {
+ vlv += 3;
+ weak = ETAG_WEAK;
+ weak_len = sizeof(ETAG_WEAK);
+ }
+ else {
+ vlv++;
+ }
+ vlv_len = strlen(vlv);
+
+ }
+ else {
+ vlv = NULL;
+ vlv_len = 0;
+ }
+
+ /*
+ * Did a module flag the need for a strong etag, or did the
+ * configuration tell us to generate a digest?
+ */
+ if (er->finfo->filetype == APR_REG &&
+ (AP_REQUEST_IS_STRONG_ETAG(r) || (etag_bits & ETAG_DIGEST))) {
+
+ return make_digest_etag(r, er, vlv, vlv_len, weak, weak_len);
+ }
+
/*
* If it's a file (or we wouldn't be here) and no ETags
* should be set for files, return an empty string and
* note it for the header-sender to ignore.
*/
if (etag_bits & ETAG_NONE) {
- apr_table_setn(r->notes, "no-etag", "omit");
return "";
}
@@ -98,123 +297,117 @@ AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
* be modified again later in the second, and the validation
* would be incorrect.
*/
- if ((r->request_time - r->mtime > (1 * APR_USEC_PER_SEC)) &&
- !force_weak) {
- weak = NULL;
- weak_len = 0;
- }
- else {
+ if ((er->request_time - er->finfo->mtime < (1 * APR_USEC_PER_SEC))) {
weak = ETAG_WEAK;
weak_len = sizeof(ETAG_WEAK);
}
- if (r->finfo.filetype != APR_NOFILE) {
+ if (er->finfo->filetype != APR_NOFILE) {
/*
* ETag gets set to [W/]"inode-size-mtime", modulo any
* FileETag keywords.
*/
etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
- 3 * CHARS_PER_UINT64 + 1);
- next = etag;
- if (weak) {
- while (*weak) {
- *next++ = *weak++;
- }
- }
- *next++ = '"';
+ 3 * CHARS_PER_UINT64 + vlv_len + 2);
+
+ etag_start(etag, weak, &next);
+
bits_added = 0;
if (etag_bits & ETAG_INODE) {
- next = etag_uint64_to_hex(next, r->finfo.inode);
+ next = etag_uint64_to_hex(next, er->finfo->inode);
bits_added |= ETAG_INODE;
}
if (etag_bits & ETAG_SIZE) {
if (bits_added != 0) {
*next++ = '-';
}
- next = etag_uint64_to_hex(next, r->finfo.size);
+ next = etag_uint64_to_hex(next, er->finfo->size);
bits_added |= ETAG_SIZE;
}
if (etag_bits & ETAG_MTIME) {
if (bits_added != 0) {
*next++ = '-';
}
- next = etag_uint64_to_hex(next, r->mtime);
+ next = etag_uint64_to_hex(next, er->finfo->mtime);
}
- *next++ = '"';
- *next = '\0';
+
+ etag_end(next, vlv, vlv_len);
+
}
else {
/*
* Not a file document, so just use the mtime: [W/]"mtime"
*/
etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
- CHARS_PER_UINT64 + 1);
- next = etag;
- if (weak) {
- while (*weak) {
- *next++ = *weak++;
- }
- }
- *next++ = '"';
- next = etag_uint64_to_hex(next, r->mtime);
- *next++ = '"';
- *next = '\0';
+ CHARS_PER_UINT64 + vlv_len + 2);
+
+ etag_start(etag, weak, &next);
+ next = etag_uint64_to_hex(next, er->finfo->mtime);
+ etag_end(next, vlv, vlv_len);
+
}
return etag;
}
+AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
+{
+ etag_rec er;
+
+ er.vlist_validator = NULL;
+ er.request_time = r->request_time;
+ er.finfo = &r->finfo;
+ er.pathname = r->filename;
+ er.fd = NULL;
+ er.force_weak = force_weak;
+
+ return ap_make_etag_ex(r, &er);
+}
+
AP_DECLARE(void) ap_set_etag(request_rec *r)
{
char *etag;
- char *variant_etag, *vlv;
- int vlv_weak;
- if (!r->vlist_validator) {
- etag = ap_make_etag(r, 0);
+ etag_rec er;
- /* If we get a blank etag back, don't set the header. */
- if (!etag[0]) {
- return;
- }
+ er.vlist_validator = r->vlist_validator;
+ er.request_time = r->request_time;
+ er.finfo = &r->finfo;
+ er.pathname = r->filename;
+ er.fd = NULL;
+ er.force_weak = 0;
+
+ etag = ap_make_etag_ex(r, &er);
+
+ if (etag && etag[0]) {
+ apr_table_setn(r->headers_out, "ETag", etag);
}
else {
- /* If we have a variant list validator (vlv) due to the
- * response being negotiated, then we create a structured
- * entity tag which merges the variant etag with the variant
- * list validator (vlv). This merging makes revalidation
- * somewhat safer, ensures that caches which can deal with
- * Vary will (eventually) be updated if the set of variants is
- * changed, and is also a protocol requirement for transparent
- * content negotiation.
- */
+ apr_table_setn(r->notes, "no-etag", "omit");
+ }
- /* if the variant list validator is weak, we make the whole
- * structured etag weak. If we would not, then clients could
- * have problems merging range responses if we have different
- * variants with the same non-globally-unique strong etag.
- */
+}
- vlv = r->vlist_validator;
- vlv_weak = (vlv[0] == 'W');
+AP_DECLARE(void) ap_set_etag_fd(request_rec *r, apr_file_t *fd)
+{
+ char *etag;
- variant_etag = ap_make_etag(r, vlv_weak);
+ etag_rec er;
- /* If we get a blank etag back, don't append vlv and stop now. */
- if (!variant_etag[0]) {
- return;
- }
+ er.vlist_validator = r->vlist_validator;
+ er.request_time = r->request_time;
+ er.finfo = &r->finfo;
+ er.pathname = NULL;
+ er.fd = fd;
+ er.force_weak = 0;
- /* merge variant_etag and vlv into a structured etag */
- variant_etag[strlen(variant_etag) - 1] = '\0';
- if (vlv_weak) {
- vlv += 3;
- }
- else {
- vlv++;
- }
- etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
+ etag = ap_make_etag_ex(r, &er);
+
+ if (etag && etag[0]) {
+ apr_table_setn(r->headers_out, "ETag", etag);
+ }
+ else {
+ apr_table_setn(r->notes, "no-etag", "omit");
}
- apr_table_setn(r->headers_out, "ETag", etag);
}
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
index 9828cdf..f20aee7 100644
--- a/modules/http/http_filters.c
+++ b/modules/http/http_filters.c
@@ -79,7 +79,8 @@ typedef struct http_filter_ctx
BODY_CHUNK_END_LF, /* got CR after data, expect LF */
BODY_CHUNK_TRAILER /* trailers */
} state;
- unsigned int eos_sent :1;
+ unsigned int eos_sent :1,
+ seen_data:1;
apr_bucket_brigade *bb;
} http_ctx_t;
@@ -348,7 +349,6 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
http_ctx_t *ctx = f->ctx;
apr_status_t rv;
int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE;
- apr_bucket_brigade *bb;
int again;
/* just get out of the way of things we don't want. */
@@ -361,7 +361,6 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
ctx->state = BODY_NONE;
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.
@@ -379,8 +378,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
lenp = apr_table_get(f->r->headers_in, "Content-Length");
if (tenc) {
- if (strcasecmp(tenc, "chunked") == 0 /* fast path */
- || ap_find_last_token(f->r->pool, tenc, "chunked")) {
+ if (ap_is_chunked(f->r->pool, tenc)) {
ctx->state = BODY_CHUNK;
}
else if (f->r->proxyreq == PROXYREQ_RESPONSE) {
@@ -406,16 +404,13 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
lenp = NULL;
}
if (lenp) {
- char *endstr;
-
ctx->state = BODY_LENGTH;
/* Protects against over/underflow, non-digit chars in the
- * string (excluding leading space) (the endstr checks)
- * and a negative number. */
- if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10)
- || endstr == lenp || *endstr || ctx->remaining < 0) {
-
+ * string, leading plus/minus signs, trailing characters and
+ * a negative number.
+ */
+ if (!ap_parse_strict_length(&ctx->remaining, lenp)) {
ctx->remaining = 0;
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01587)
"Invalid Content-Length");
@@ -452,42 +447,46 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
ctx->eos_sent = 1;
return APR_SUCCESS;
}
+ }
- /* Since we're about to read data, send 100-Continue if needed.
- * 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->eos_sent || f->r->bytes_sent)) {
- if (!ap_is_HTTP_SUCCESS(f->r->status)) {
- ctx->state = BODY_NONE;
- ctx->eos_sent = 1;
- }
- else {
- char *tmp;
- int len;
-
- /* if we send an interim response, we're no longer
- * in a state of expecting one.
- */
- f->r->expecting_100 = 0;
- tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL " ",
- ap_get_status_line(HTTP_CONTINUE), CRLF CRLF,
- NULL);
- len = strlen(tmp);
- ap_xlate_proto_to_ascii(tmp, len);
- apr_brigade_cleanup(bb);
- e = apr_bucket_pool_create(tmp, len, f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_HEAD(bb, e);
- e = apr_bucket_flush_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
-
- rv = ap_pass_brigade(f->c->output_filters, bb);
- if (rv != APR_SUCCESS) {
- return AP_FILTER_ERROR;
- }
- }
+ /* Since we're about to read data, send 100-Continue if needed.
+ * Only valid on chunked and C-L bodies where the C-L is > 0.
+ *
+ * If the read is to be nonblocking though, the caller may not want to
+ * handle this just now (e.g. mod_proxy_http), and is prepared to read
+ * nothing if the client really waits for 100 continue, so we don't
+ * send it now and wait for later blocking read.
+ *
+ * In any case, even if r->expecting remains set at the end of the
+ * request handling, ap_set_keepalive() will finally do the right
+ * thing (i.e. "Connection: close" the connection).
+ */
+ if (block == APR_BLOCK_READ
+ && (ctx->state == BODY_CHUNK
+ || (ctx->state == BODY_LENGTH && ctx->remaining > 0))
+ && f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1)
+ && !(ctx->eos_sent || f->r->eos_sent || f->r->bytes_sent)) {
+ if (!ap_is_HTTP_SUCCESS(f->r->status)) {
+ ctx->state = BODY_NONE;
+ ctx->eos_sent = 1; /* send EOS below */
+ }
+ else if (!ctx->seen_data) {
+ int saved_status = f->r->status;
+ const char *saved_status_line = f->r->status_line;
+ f->r->status = HTTP_CONTINUE;
+ f->r->status_line = NULL;
+ ap_send_interim_response(f->r, 0);
+ AP_DEBUG_ASSERT(!f->r->expecting_100);
+ f->r->status_line = saved_status_line;
+ f->r->status = saved_status;
+ }
+ else {
+ /* https://tools.ietf.org/html/rfc7231#section-5.1.1
+ * A server MAY omit sending a 100 (Continue) response if it
+ * has already received some or all of the message body for
+ * the corresponding request [...]
+ */
+ f->r->expecting_100 = 0;
}
}
@@ -538,9 +537,11 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
int parsing = 0;
rv = apr_bucket_read(e, &buffer, &len, APR_BLOCK_READ);
-
if (rv == APR_SUCCESS) {
parsing = 1;
+ if (len > 0) {
+ ctx->seen_data = 1;
+ }
rv = parse_chunk_size(ctx, buffer, len,
f->r->server->limit_req_fieldsize, strict);
}
@@ -602,6 +603,9 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
/* How many bytes did we just read? */
apr_brigade_length(b, 0, &totalread);
+ if (totalread > 0) {
+ ctx->seen_data = 1;
+ }
/* If this happens, we have a bucket of unknown length. Die because
* it means our assumptions have changed. */
@@ -774,6 +778,18 @@ static APR_INLINE int check_headers(request_rec *r)
struct check_header_ctx ctx;
core_server_config *conf =
ap_get_core_module_config(r->server->module_config);
+ const char *val;
+
+ if ((val = apr_table_get(r->headers_out, "Transfer-Encoding"))) {
+ if (apr_table_get(r->headers_out, "Content-Length")) {
+ apr_table_unset(r->headers_out, "Content-Length");
+ r->connection->keepalive = AP_CONN_CLOSE;
+ }
+ if (!ap_is_chunked(r->pool, val)) {
+ r->connection->keepalive = AP_CONN_CLOSE;
+ return 0;
+ }
+ }
ctx.r = r;
ctx.strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
@@ -872,7 +888,7 @@ static int uniq_field_values(void *d, const char *key, const char *val)
*/
for (i = 0, strpp = (char **) values->elts; i < values->nelts;
++i, ++strpp) {
- if (*strpp && strcasecmp(*strpp, start) == 0) {
+ if (*strpp && ap_cstr_casecmp(*strpp, start) == 0) {
break;
}
}
@@ -1290,6 +1306,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
request_rec *r = f->r;
conn_rec *c = r->connection;
const char *clheader;
+ int header_only = (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status));
const char *protocol = NULL;
apr_bucket *e;
apr_bucket_brigade *b2;
@@ -1307,7 +1324,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
}
else if (ctx->headers_sent) {
/* Eat body if response must not have one. */
- if (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status)) {
+ if (header_only) {
/* Still next filters may be waiting for EOS, so pass it (alone)
* when encountered and be done with this filter.
*/
@@ -1348,6 +1365,9 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
*/
apr_table_clear(r->headers_out);
apr_table_clear(r->err_headers_out);
+ r->content_type = r->content_encoding = NULL;
+ r->content_languages = NULL;
+ r->clength = r->chunked = 0;
apr_brigade_cleanup(b);
/* Don't recall ap_die() if we come back here (from its own internal
@@ -1364,8 +1384,6 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
APR_BRIGADE_INSERT_TAIL(b, e);
e = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(b, e);
- r->content_type = r->content_encoding = NULL;
- r->content_languages = NULL;
ap_set_content_length(r, 0);
recursive_error = 1;
}
@@ -1392,6 +1410,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
if (!apr_is_empty_table(r->err_headers_out)) {
r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
r->headers_out);
+ apr_table_clear(r->err_headers_out);
}
/*
@@ -1411,6 +1430,17 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
fixup_vary(r);
}
+
+ /*
+ * Control cachability for non-cacheable responses if not already set by
+ * some other part of the server configuration.
+ */
+ if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) {
+ char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
+ ap_recent_rfc822_date(date, r->request_time);
+ apr_table_addn(r->headers_out, "Expires", date);
+ }
+
/*
* Now remove any ETag response header field if earlier processing
* says so (such as a 'FileETag None' directive).
@@ -1423,6 +1453,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
basic_http_header_check(r, &protocol);
ap_set_keepalive(r);
+ /* 204/304 responses don't have content related headers */
if (AP_STATUS_IS_HEADER_ONLY(r->status)) {
apr_table_unset(r->headers_out, "Transfer-Encoding");
apr_table_unset(r->headers_out, "Content-Length");
@@ -1453,7 +1484,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
while (field && (token = ap_get_list_item(r->pool, &field)) != NULL) {
for (i = 0; i < r->content_languages->nelts; ++i) {
- if (!strcasecmp(token, languages[i]))
+ if (!ap_cstr_casecmp(token, languages[i]))
break;
}
if (i == r->content_languages->nelts) {
@@ -1465,16 +1496,6 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
apr_table_setn(r->headers_out, "Content-Language", field);
}
- /*
- * Control cachability for non-cacheable responses if not already set by
- * some other part of the server configuration.
- */
- if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) {
- char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
- ap_recent_rfc822_date(date, r->request_time);
- apr_table_addn(r->headers_out, "Expires", date);
- }
-
/* This is a hack, but I can't find anyway around it. The idea is that
* we don't want to send out 0 Content-Lengths if it is a head request.
* This happens when modules try to outsmart the server, and return
@@ -1499,37 +1520,25 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
h.pool = r->pool;
h.bb = b2;
- if (r->status == HTTP_NOT_MODIFIED) {
- apr_table_do((int (*)(void *, const char *, const char *)) form_header_field,
- (void *) &h, r->headers_out,
- "Connection",
- "Keep-Alive",
- "ETag",
- "Content-Location",
- "Expires",
- "Cache-Control",
- "Vary",
- "Warning",
- "WWW-Authenticate",
- "Proxy-Authenticate",
- "Set-Cookie",
- "Set-Cookie2",
- NULL);
- }
- else {
- send_all_header_fields(&h, r);
- }
+ send_all_header_fields(&h, r);
terminate_header(b2);
- rv = ap_pass_brigade(f->next, b2);
- if (rv != APR_SUCCESS) {
- goto out;
+ if (header_only) {
+ e = APR_BRIGADE_LAST(b);
+ if (e != APR_BRIGADE_SENTINEL(b) && APR_BUCKET_IS_EOS(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(b2, e);
+ ap_remove_output_filter(f);
+ }
+ apr_brigade_cleanup(b);
}
+
+ rv = ap_pass_brigade(f->next, b2);
+ apr_brigade_cleanup(b2);
ctx->headers_sent = 1;
- if (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status)) {
- apr_brigade_cleanup(b);
+ if (rv != APR_SUCCESS || header_only) {
goto out;
}
@@ -1605,9 +1614,9 @@ AP_DECLARE(int) ap_map_http_request_error(apr_status_t rv, int status)
*/
AP_DECLARE(int) ap_discard_request_body(request_rec *r)
{
+ int rc = OK;
+ conn_rec *c = r->connection;
apr_bucket_brigade *bb;
- int seen_eos;
- apr_status_t rv;
/* Sometimes we'll get in a state where the input handling has
* detected an error where we want to drop the connection, so if
@@ -1616,54 +1625,57 @@ AP_DECLARE(int) ap_discard_request_body(request_rec *r)
*
* This function is also a no-op on a subrequest.
*/
- if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
- ap_status_drops_connection(r->status)) {
+ if (r->main || c->keepalive == AP_CONN_CLOSE) {
+ return OK;
+ }
+ if (ap_status_drops_connection(r->status)) {
+ c->keepalive = AP_CONN_CLOSE;
return OK;
}
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- seen_eos = 0;
- do {
- apr_bucket *bucket;
+ for (;;) {
+ apr_status_t rv;
rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
APR_BLOCK_READ, HUGE_STRING_LEN);
-
if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
+ rc = ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
+ goto cleanup;
}
- for (bucket = APR_BRIGADE_FIRST(bb);
- bucket != APR_BRIGADE_SENTINEL(bb);
- bucket = APR_BUCKET_NEXT(bucket))
- {
- const char *data;
- apr_size_t len;
-
- if (APR_BUCKET_IS_EOS(bucket)) {
- seen_eos = 1;
- break;
- }
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ apr_bucket *b = APR_BRIGADE_FIRST(bb);
- /* These are metadata buckets. */
- if (bucket->length == 0) {
- continue;
+ if (APR_BUCKET_IS_EOS(b)) {
+ goto cleanup;
}
- /* We MUST read because in case we have an unknown-length
- * bucket or one that morphs, we want to exhaust it.
+ /* There is no need to read empty or metadata buckets or
+ * buckets of known length, but we MUST read buckets of
+ * unknown length in order to exhaust them.
*/
- rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
- if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
+ if (b->length == (apr_size_t)-1) {
+ apr_size_t len;
+ const char *data;
+
+ rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ rc = HTTP_BAD_REQUEST;
+ goto cleanup;
+ }
}
+
+ apr_bucket_delete(b);
}
- apr_brigade_cleanup(bb);
- } while (!seen_eos);
+ }
- return OK;
+cleanup:
+ apr_brigade_cleanup(bb);
+ if (rc != OK) {
+ c->keepalive = AP_CONN_CLOSE;
+ }
+ return rc;
}
/* Here we deal with getting the request message body from the client.
@@ -1707,13 +1719,14 @@ AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
{
const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
const char *lenp = apr_table_get(r->headers_in, "Content-Length");
+ apr_off_t limit_req_body = ap_get_limit_req_body(r);
r->read_body = read_policy;
r->read_chunked = 0;
r->remaining = 0;
if (tenc) {
- if (strcasecmp(tenc, "chunked")) {
+ if (ap_cstr_casecmp(tenc, "chunked")) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01592)
"Unknown Transfer-Encoding %s", tenc);
return HTTP_NOT_IMPLEMENTED;
@@ -1727,13 +1740,10 @@ AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
r->read_chunked = 1;
}
else if (lenp) {
- char *endstr;
-
- if (apr_strtoff(&r->remaining, lenp, &endstr, 10)
- || *endstr || r->remaining < 0) {
+ if (!ap_parse_strict_length(&r->remaining, lenp)) {
r->remaining = 0;
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01594)
- "Invalid Content-Length");
+ "Invalid Content-Length '%s'", lenp);
return HTTP_BAD_REQUEST;
}
}
@@ -1745,6 +1755,11 @@ AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
+ if (limit_req_body > 0 && (r->remaining > limit_req_body)) {
+ /* will be logged when the body is discarded */
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
#ifdef AP_DEBUG
{
/* Make sure ap_getline() didn't leave any droppings. */
@@ -1852,6 +1867,7 @@ AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer,
/* Context struct for ap_http_outerror_filter */
typedef struct {
int seen_eoc;
+ int first_error;
} outerror_filter_ctx_t;
/* Filter to handle any error buckets on output */
@@ -1880,10 +1896,18 @@ apr_status_t ap_http_outerror_filter(ap_filter_t *f,
/* stream aborted and we have not ended it yet */
r->connection->keepalive = AP_CONN_CLOSE;
}
+ /*
+ * Memorize the status code of the first error bucket for possible
+ * later use.
+ */
+ if (!ctx->first_error) {
+ ctx->first_error = ((ap_bucket_error *)(e->data))->status;
+ }
continue;
}
/* Detect EOC buckets and memorize this in the context. */
if (AP_BUCKET_IS_EOC(e)) {
+ r->connection->keepalive = AP_CONN_CLOSE;
ctx->seen_eoc = 1;
}
}
@@ -1907,6 +1931,18 @@ apr_status_t ap_http_outerror_filter(ap_filter_t *f,
* EOS bucket.
*/
if (ctx->seen_eoc) {
+ /*
+ * Set the request status to the status of the first error bucket.
+ * This should ensure that we log an appropriate status code in
+ * the access log.
+ * We need to set r->status on each call after we noticed an EOC as
+ * data bucket generators like ap_die might have changed the status
+ * code. But we know better in this case and insist on the status
+ * code that we have seen in the error bucket.
+ */
+ if (ctx->first_error) {
+ r->status = ctx->first_error;
+ }
for (e = APR_BRIGADE_FIRST(b);
e != APR_BRIGADE_SENTINEL(b);
e = APR_BUCKET_NEXT(e))
diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c
index e419eb6..d031f24 100644
--- a/modules/http/http_protocol.c
+++ b/modules/http/http_protocol.c
@@ -60,7 +60,7 @@
APLOG_USE_MODULE(http);
-/* New Apache routine to map status codes into array indicies
+/* New Apache routine to map status codes into array indices
* e.g. 100 -> 0, 101 -> 1, 200 -> 2 ...
* The number of status lines must equal the value of
* RESPONSE_CODES (httpd.h) and must be listed in order.
@@ -257,10 +257,9 @@ AP_DECLARE(int) ap_set_keepalive(request_rec *r)
&& (r->header_only
|| AP_STATUS_IS_HEADER_ONLY(r->status)
|| apr_table_get(r->headers_out, "Content-Length")
- || ap_find_last_token(r->pool,
+ || ap_is_chunked(r->pool,
apr_table_get(r->headers_out,
- "Transfer-Encoding"),
- "chunked")
+ "Transfer-Encoding"))
|| ((r->proto_num >= HTTP_VERSION(1,1))
&& (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
&& r->server->keep_alive
@@ -987,14 +986,17 @@ AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum)
* from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
* or use NULL to fill the gaps.
*/
-AP_DECLARE(int) ap_index_of_response(int status)
+static int index_of_response(int status)
{
- static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
- LEVEL_500, RESPONSE_CODES};
+ static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400, LEVEL_500,
+ RESPONSE_CODES};
int i, pos;
- if (status < 100) { /* Below 100 is illegal for HTTP status */
- return LEVEL_500;
+ if (status < 100) { /* Below 100 is illegal for HTTP status */
+ return -1;
+ }
+ if (status > 999) { /* Above 999 is also illegal for HTTP status */
+ return -1;
}
for (i = 0; i < 5; i++) {
@@ -1005,11 +1007,29 @@ AP_DECLARE(int) ap_index_of_response(int status)
return pos;
}
else {
- return LEVEL_500; /* status unknown (falls in gap) */
+ break;
}
}
}
- return LEVEL_500; /* 600 or above is also illegal */
+ return -2; /* Status unknown (falls in gap) or above 600 */
+}
+
+AP_DECLARE(int) ap_index_of_response(int status)
+{
+ int index = index_of_response(status);
+ return (index < 0) ? LEVEL_500 : index;
+}
+
+AP_DECLARE(const char *) ap_get_status_line_ex(apr_pool_t *p, int status)
+{
+ int index = index_of_response(status);
+ if (index >= 0) {
+ return status_lines[index];
+ }
+ else if (index == -2) {
+ return apr_psprintf(p, "%i Status %i", status, status);
+ }
+ return status_lines[LEVEL_500];
}
AP_DECLARE(const char *) ap_get_status_line(int status)
@@ -1132,13 +1152,10 @@ static const char *get_canned_error_string(int status,
"\">here.
\n",
NULL));
case HTTP_USE_PROXY:
- return(apr_pstrcat(p,
- "This resource is only accessible "
- "through the proxy\n",
- ap_escape_html(r->pool, location),
- "
\nYou will need to configure "
- "your client to use that proxy.
\n",
- NULL));
+ return("This resource is only accessible "
+ "through the proxy\n"
+ "
\nYou will need to configure "
+ "your client to use that proxy.
\n");
case HTTP_PROXY_AUTHENTICATION_REQUIRED:
case HTTP_UNAUTHORIZED:
return("This server could not verify that you\n"
@@ -1154,34 +1171,20 @@ static const char *get_canned_error_string(int status,
"error-notes",
"
\n"));
case HTTP_FORBIDDEN:
- s1 = apr_pstrcat(p,
- "You don't have permission to access ",
- ap_escape_html(r->pool, r->uri),
- "\non this server.
\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
+ return(add_optional_notes(r, "You don't have permission to access this resource.", "error-notes", "
\n"));
case HTTP_NOT_FOUND:
- return(apr_pstrcat(p,
- "The requested URL ",
- ap_escape_html(r->pool, r->uri),
- " was not found on this server.
\n",
- NULL));
+ return("The requested URL was not found on this server.
\n");
case HTTP_METHOD_NOT_ALLOWED:
return(apr_pstrcat(p,
"The requested method ",
ap_escape_html(r->pool, r->method),
- " is not allowed for the URL ",
- ap_escape_html(r->pool, r->uri),
- ".
\n",
+ " is not allowed for this URL.\n",
NULL));
case HTTP_NOT_ACCEPTABLE:
- s1 = apr_pstrcat(p,
- "An appropriate representation of the "
- "requested resource ",
- ap_escape_html(r->pool, r->uri),
- " could not be found on this server.
\n",
- NULL);
- return(add_optional_notes(r, s1, "variant-list", ""));
+ return(add_optional_notes(r,
+ "An appropriate representation of the requested resource "
+ "could not be found on this server.
\n",
+ "variant-list", ""));
case HTTP_MULTIPLE_CHOICES:
return(add_optional_notes(r, "", "variant-list", ""));
case HTTP_LENGTH_REQUIRED:
@@ -1192,18 +1195,13 @@ static const char *get_canned_error_string(int status,
NULL);
return(add_optional_notes(r, s1, "error-notes", "\n"));
case HTTP_PRECONDITION_FAILED:
- return(apr_pstrcat(p,
- "The precondition on the request "
- "for the URL ",
- ap_escape_html(r->pool, r->uri),
- " evaluated to false.
\n",
- NULL));
+ return("The precondition on the request "
+ "for this URL evaluated to false.
\n");
case HTTP_NOT_IMPLEMENTED:
s1 = apr_pstrcat(p,
"",
- ap_escape_html(r->pool, r->method), " to ",
- ap_escape_html(r->pool, r->uri),
- " not supported.
\n",
+ ap_escape_html(r->pool, r->method),
+ " not supported for current URL.
\n",
NULL);
return(add_optional_notes(r, s1, "error-notes", "
\n"));
case HTTP_BAD_GATEWAY:
@@ -1211,29 +1209,19 @@ static const char *get_canned_error_string(int status,
"response from an upstream server.
" CRLF;
return(add_optional_notes(r, s1, "error-notes", "\n"));
case HTTP_VARIANT_ALSO_VARIES:
- return(apr_pstrcat(p,
- "A variant for the requested "
- "resource\n
\n",
- ap_escape_html(r->pool, r->uri),
- "\n
\nis itself a negotiable resource. "
- "This indicates a configuration error.\n",
- NULL));
+ return("A variant for the requested "
+ "resource\n
\n"
+ "\n
\nis itself a negotiable resource. "
+ "This indicates a configuration error.\n");
case HTTP_REQUEST_TIME_OUT:
return("Server timeout waiting for the HTTP request from the client.
\n");
case HTTP_GONE:
- return(apr_pstrcat(p,
- "The requested resource
",
- ap_escape_html(r->pool, r->uri),
- "
\nis no longer available on this server "
- "and there is no forwarding address.\n"
- "Please remove all references to this "
- "resource.
\n",
- NULL));
+ return("The requested resource is no longer available on this server"
+ " and there is no forwarding address.\n"
+ "Please remove all references to this resource.
\n");
case HTTP_REQUEST_ENTITY_TOO_LARGE:
return(apr_pstrcat(p,
- "The requested resource
",
- ap_escape_html(r->pool, r->uri), "
\n",
- "does not allow request data with ",
+ "The requested resource does not allow request data with ",
ap_escape_html(r->pool, r->method),
" requests, or the amount of data provided in\n"
"the request exceeds the capacity limit.\n",
@@ -1317,11 +1305,9 @@ static const char *get_canned_error_string(int status,
"the Server Name Indication (SNI) in use for this\n"
"connection.\n");
case HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
- s1 = apr_pstrcat(p,
- "Access to ", ap_escape_html(r->pool, r->uri),
- "\nhas been denied for legal reasons.
\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
+ return(add_optional_notes(r,
+ "Access to this URL has been denied for legal reasons.
\n",
+ "error-notes", "
\n"));
default: /* HTTP_INTERNAL_SERVER_ERROR */
/*
* This comparison to expose error-notes could be modified to
diff --git a/modules/http/http_request.c b/modules/http/http_request.c
index 9e7c4db..d59cfe2 100644
--- a/modules/http/http_request.c
+++ b/modules/http/http_request.c
@@ -249,7 +249,7 @@ AP_DECLARE(apr_status_t) ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb,
apr_brigade_cleanup(bb);
rv = ap_get_brigade(c->input_filters, bb, mode,
APR_NONBLOCK_READ, len);
- if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb) || !max_blank_lines) {
+ if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
if (mode == AP_MODE_READBYTES) {
/* Unexpected error, stop with this connection */
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02967)
@@ -257,23 +257,22 @@ AP_DECLARE(apr_status_t) ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb,
c->keepalive = AP_CONN_CLOSE;
rv = APR_EGENERAL;
}
- else if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
- if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
- /* Pipe is dead */
- c->keepalive = AP_CONN_CLOSE;
- }
- else {
- /* Pipe is up and empty */
- rv = APR_EAGAIN;
- }
+ else if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
+ /* Pipe is dead */
+ c->keepalive = AP_CONN_CLOSE;
}
else {
- apr_off_t n = 0;
- /* Single read asked, (non-meta-)data available? */
- rv = apr_brigade_length(bb, 0, &n);
- if (rv == APR_SUCCESS && n <= 0) {
- rv = APR_EAGAIN;
- }
+ /* Pipe is up and empty */
+ rv = APR_EAGAIN;
+ }
+ break;
+ }
+ if (!max_blank_lines) {
+ apr_off_t n = 0;
+ /* Single read asked, (non-meta-)data available? */
+ rv = apr_brigade_length(bb, 0, &n);
+ if (rv == APR_SUCCESS && n <= 0) {
+ rv = APR_EAGAIN;
}
break;
}
@@ -681,7 +680,7 @@ static request_rec *internal_internal_redirect(const char *new_uri,
* to do their thing on internal redirects as well. Perhaps this is a
* misnamed function.
*/
- if ((access_status = ap_run_post_read_request(new))) {
+ if ((access_status = ap_post_read_request(new))) {
ap_die(access_status, new);
return NULL;
}
diff --git a/modules/http/mod_mime.c b/modules/http/mod_mime.c
index 28c53be..700f824 100644
--- a/modules/http/mod_mime.c
+++ b/modules/http/mod_mime.c
@@ -755,7 +755,7 @@ static int find_ct(request_rec *r)
mime_dir_config *conf;
apr_array_header_t *exception_list;
char *ext;
- const char *fn, *fntmp, *type, *charset = NULL, *resource_name;
+ const char *fn, *fntmp, *type, *charset = NULL, *resource_name, *qm;
int found_metadata = 0;
if (r->finfo.filetype == APR_DIR) {
@@ -775,6 +775,19 @@ static int find_ct(request_rec *r)
if (conf->use_path_info & 1) {
resource_name = apr_pstrcat(r->pool, r->filename, r->path_info, NULL);
}
+ /*
+ * In the reverse proxy case r->filename might contain a query string if
+ * the nocanon option was used with ProxyPass.
+ * If this is the case cut off the query string as the last parameter in
+ * this query string might end up on an extension we take care about, but
+ * we only want to match against path components not against query
+ * parameters.
+ */
+ else if ((r->proxyreq == PROXYREQ_REVERSE)
+ && (apr_table_get(r->notes, "proxy-nocanon"))
+ && ((qm = ap_strchr_c(r->filename, '?')) != NULL)) {
+ resource_name = apr_pstrmemdup(r->pool, r->filename, qm - r->filename);
+ }
else {
resource_name = r->filename;
}
@@ -989,9 +1002,7 @@ static int find_ct(request_rec *r)
if (!r->content_languages && conf->default_language) {
const char **new;
- if (!r->content_languages) {
- r->content_languages = apr_array_make(r->pool, 2, sizeof(char *));
- }
+ r->content_languages = apr_array_make(r->pool, 2, sizeof(char *));
new = (const char **)apr_array_push(r->content_languages);
*new = conf->default_language;
}
--
cgit v1.2.3