diff options
author | Ruediger Pluem <rpluem@apache.org> | 2005-12-18 12:07:39 +0000 |
---|---|---|
committer | Ruediger Pluem <rpluem@apache.org> | 2005-12-18 12:07:39 +0000 |
commit | dcc563844c890820a3e56b9cd30f0d04e8115a8c (patch) | |
tree | 23a173c31ce4edef69cc6f36f8c99574a706bb73 | |
parent | a26a74de01de154303624885762a7a29e44d6619 (diff) | |
download | httpd-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-- | CHANGES | 3 | ||||
-rw-r--r-- | modules/cache/mod_disk_cache.c | 2 | ||||
-rw-r--r-- | modules/http/chunk_filter.c | 18 | ||||
-rw-r--r-- | modules/proxy/mod_proxy_http.c | 12 | ||||
-rw-r--r-- | server/core_filters.c | 23 |
5 files changed, 46 insertions, 12 deletions
@@ -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); } |