summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuediger Pluem <rpluem@apache.org>2005-12-18 12:07:39 +0000
committerRuediger Pluem <rpluem@apache.org>2005-12-18 12:07:39 +0000
commitdcc563844c890820a3e56b9cd30f0d04e8115a8c (patch)
tree23a173c31ce4edef69cc6f36f8c99574a706bb73
parenta26a74de01de154303624885762a7a29e44d6619 (diff)
downloadhttpd-dcc563844c890820a3e56b9cd30f0d04e8115a8c.tar.gz
* If the mod_proxy backend connection broke in the middle of the response,
then - Do not cache it. - Signal the client that something went wrong by closing the connection and not sending the last-chunk marker if the response was T-E chunked. server/core_filters.c : Close the connection to the client by setting c->keepalive to AP_CONN_CLOSE. modules/http/chunk_filter.c : Do not send last-chunk marker in the case the backend broke. modules/proxy/mod_proxy_http.c: Signal that the backend connection broke. modules/cache/mod_disk_cache.c: Respect r->no_cache for discarding the response Submitted by: Roy T. Fielding, Jim Jagielski, Ruediger Pluem Reviewed by: Roy T. Fielding, Jim Jagielski, Ruediger Pluem git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@357461 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES3
-rw-r--r--modules/cache/mod_disk_cache.c2
-rw-r--r--modules/http/chunk_filter.c18
-rw-r--r--modules/proxy/mod_proxy_http.c12
-rw-r--r--server/core_filters.c23
5 files changed, 46 insertions, 12 deletions
diff --git a/CHANGES b/CHANGES
index 1c5865deb2..34a9763cf8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -6,7 +6,8 @@ Changes with Apache 2.3.0
connection as errored out. [Justin Erenkrantz]
*) mod_proxy: If we get an error reading the upstream response,
- close the connection. [Brian Akins, Justin Erenkrantz]
+ close the connection.
+ [Justin Erenkrantz, Roy T. Fielding, Jim Jagielski, Ruediger Pluem]
*) mod_ssl: Fix a possible crash during access control checks if a
non-SSL request is processed for an SSL vhost (such as the
diff --git a/modules/cache/mod_disk_cache.c b/modules/cache/mod_disk_cache.c
index 1fa9bc1472..79ed6f14b4 100644
--- a/modules/cache/mod_disk_cache.c
+++ b/modules/cache/mod_disk_cache.c
@@ -1010,7 +1010,7 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
* sanity checks.
*/
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
- if (r->connection->aborted) {
+ if (r->connection->aborted || r->no_cache) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"disk_cache: Discarding body for URL %s "
"because connection has been aborted.",
diff --git a/modules/http/chunk_filter.c b/modules/http/chunk_filter.c
index 71f4662fe4..bbcd4f59e3 100644
--- a/modules/http/chunk_filter.c
+++ b/modules/http/chunk_filter.c
@@ -47,6 +47,7 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
apr_bucket_brigade *more;
apr_bucket *e;
apr_status_t rv;
+ int bad_gateway_seen = 0;
for (more = NULL; b; b = more, more = NULL) {
apr_off_t bytes = 0;
@@ -67,6 +68,13 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
eos = e;
break;
}
+ if (AP_BUCKET_IS_ERROR(e)
+ && (((ap_bucket_error *)(e->data))->status
+ == HTTP_BAD_GATEWAY)) {
+ /* We had a broken backend. Memorize this. */
+ bad_gateway_seen = 1;
+ continue;
+ }
if (APR_BUCKET_IS_FLUSH(e)) {
flush = e;
more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
@@ -146,13 +154,19 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
* 2) the trailer
* 3) the end-of-chunked body CRLF
*
- * If there is no EOS bucket, then do nothing.
+ * If there is no EOS bucket, or if we had seen an error bucket with
+ * status HTTP_BAD_GATEWAY then do nothing.
+ * The error bucket with status HTTP_BAD_GATEWAY indicates that the
+ * connection to the backend (mod_proxy) broke in the middle of the
+ * response. In order to signal the client that something went wrong
+ * we do not create the last-chunk marker and set c->keepalive to
+ * AP_CONN_CLOSE in the core output filter.
*
* XXX: it would be nice to combine this with the end-of-chunk
* marker above, but this is a bit more straight-forward for
* now.
*/
- if (eos != NULL) {
+ if (eos && !bad_gateway_seen) {
/* XXX: (2) trailers ... does not yet exist */
e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
/* <trailers> */
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index 250b7601d0..83e40432a4 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -1481,12 +1481,18 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
}
else if (rv != APR_SUCCESS) {
/* In this case, we are in real trouble because
- * our backend bailed on us, so abort our
- * connection to our user too.
+ * our backend bailed on us.
*/
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c,
"proxy: error reading response");
- c->aborted = 1;
+ r->no_cache = 1;
+ e = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL,
+ c->pool, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ e = apr_bucket_eos_create(c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ ap_pass_brigade(r->output_filters, bb);
+ backend->close = 1;
break;
}
/* next time try a non-blocking read */
diff --git a/server/core_filters.c b/server/core_filters.c
index 5f6ff31ec4..90f5d4bf7b 100644
--- a/server/core_filters.c
+++ b/server/core_filters.c
@@ -315,7 +315,9 @@ static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
apr_size_t *bytes_written,
conn_rec *c);
-static void remove_empty_buckets(apr_bucket_brigade *bb);
+static void detect_error_bucket(apr_bucket *bucket, conn_rec *c);
+
+static void remove_empty_buckets(apr_bucket_brigade *bb, conn_rec *c);
static apr_status_t send_brigade_blocking(apr_socket_t *s,
apr_bucket_brigade *bb,
@@ -487,7 +489,7 @@ static void setaside_remaining_output(ap_filter_t *f,
if (bb == NULL) {
return;
}
- remove_empty_buckets(bb);
+ remove_empty_buckets(bb, c);
if (!APR_BRIGADE_EMPTY(bb)) {
c->data_in_output_filters = 1;
if (make_a_copy) {
@@ -526,7 +528,7 @@ static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
struct iovec vec[MAX_IOVEC_TO_WRITE];
apr_size_t nvec = 0;
- remove_empty_buckets(bb);
+ remove_empty_buckets(bb, c);
for (bucket = APR_BRIGADE_FIRST(bb);
bucket != APR_BRIGADE_SENTINEL(bb);
@@ -596,16 +598,26 @@ static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
}
}
- remove_empty_buckets(bb);
+ remove_empty_buckets(bb, c);
return APR_SUCCESS;
}
-static void remove_empty_buckets(apr_bucket_brigade *bb)
+static void detect_error_bucket(apr_bucket *bucket, conn_rec *c)
+{
+ if (AP_BUCKET_IS_ERROR(bucket)
+ && (((ap_bucket_error *)(bucket->data))->status == HTTP_BAD_GATEWAY)) {
+ /* stream aborted and we have not ended it yet */
+ c->keepalive = AP_CONN_CLOSE;
+ }
+}
+
+static void remove_empty_buckets(apr_bucket_brigade *bb, conn_rec *c)
{
apr_bucket *bucket;
while (((bucket = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) &&
(APR_BUCKET_IS_METADATA(bucket) || (bucket->length == 0))) {
+ detect_error_bucket(bucket, c);
APR_BUCKET_REMOVE(bucket);
apr_bucket_destroy(bucket);
}
@@ -678,6 +690,7 @@ static apr_status_t writev_nonblocking(apr_socket_t *s,
for (i = offset; i < nvec; ) {
apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
if (APR_BUCKET_IS_METADATA(bucket)) {
+ detect_error_bucket(bucket, c);
APR_BUCKET_REMOVE(bucket);
apr_bucket_destroy(bucket);
}