mirror of
https://github.com/apache/httpd.git
synced 2025-11-06 16:49:32 +03:00
* 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
This commit is contained in:
3
CHANGES
3
CHANGES
@@ -6,7 +6,8 @@ Changes with Apache 2.3.0
|
|||||||
connection as errored out. [Justin Erenkrantz]
|
connection as errored out. [Justin Erenkrantz]
|
||||||
|
|
||||||
*) mod_proxy: If we get an error reading the upstream response,
|
*) 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
|
*) mod_ssl: Fix a possible crash during access control checks if a
|
||||||
non-SSL request is processed for an SSL vhost (such as the
|
non-SSL request is processed for an SSL vhost (such as the
|
||||||
|
|||||||
2
modules/cache/mod_disk_cache.c
vendored
2
modules/cache/mod_disk_cache.c
vendored
@@ -1010,7 +1010,7 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
|
|||||||
* sanity checks.
|
* sanity checks.
|
||||||
*/
|
*/
|
||||||
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
|
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,
|
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
|
||||||
"disk_cache: Discarding body for URL %s "
|
"disk_cache: Discarding body for URL %s "
|
||||||
"because connection has been aborted.",
|
"because connection has been aborted.",
|
||||||
|
|||||||
@@ -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_brigade *more;
|
||||||
apr_bucket *e;
|
apr_bucket *e;
|
||||||
apr_status_t rv;
|
apr_status_t rv;
|
||||||
|
int bad_gateway_seen = 0;
|
||||||
|
|
||||||
for (more = NULL; b; b = more, more = NULL) {
|
for (more = NULL; b; b = more, more = NULL) {
|
||||||
apr_off_t bytes = 0;
|
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;
|
eos = e;
|
||||||
break;
|
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)) {
|
if (APR_BUCKET_IS_FLUSH(e)) {
|
||||||
flush = e;
|
flush = e;
|
||||||
more = apr_brigade_split(b, APR_BUCKET_NEXT(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
|
* 2) the trailer
|
||||||
* 3) the end-of-chunked body CRLF
|
* 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
|
* XXX: it would be nice to combine this with the end-of-chunk
|
||||||
* marker above, but this is a bit more straight-forward for
|
* marker above, but this is a bit more straight-forward for
|
||||||
* now.
|
* now.
|
||||||
*/
|
*/
|
||||||
if (eos != NULL) {
|
if (eos && !bad_gateway_seen) {
|
||||||
/* XXX: (2) trailers ... does not yet exist */
|
/* XXX: (2) trailers ... does not yet exist */
|
||||||
e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
|
e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
|
||||||
/* <trailers> */
|
/* <trailers> */
|
||||||
|
|||||||
@@ -1481,12 +1481,18 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
|
|||||||
}
|
}
|
||||||
else if (rv != APR_SUCCESS) {
|
else if (rv != APR_SUCCESS) {
|
||||||
/* In this case, we are in real trouble because
|
/* In this case, we are in real trouble because
|
||||||
* our backend bailed on us, so abort our
|
* our backend bailed on us.
|
||||||
* connection to our user too.
|
|
||||||
*/
|
*/
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c,
|
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c,
|
||||||
"proxy: error reading response");
|
"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;
|
break;
|
||||||
}
|
}
|
||||||
/* next time try a non-blocking read */
|
/* next time try a non-blocking read */
|
||||||
|
|||||||
@@ -315,7 +315,9 @@ static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
|
|||||||
apr_size_t *bytes_written,
|
apr_size_t *bytes_written,
|
||||||
conn_rec *c);
|
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,
|
static apr_status_t send_brigade_blocking(apr_socket_t *s,
|
||||||
apr_bucket_brigade *bb,
|
apr_bucket_brigade *bb,
|
||||||
@@ -487,7 +489,7 @@ static void setaside_remaining_output(ap_filter_t *f,
|
|||||||
if (bb == NULL) {
|
if (bb == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
remove_empty_buckets(bb);
|
remove_empty_buckets(bb, c);
|
||||||
if (!APR_BRIGADE_EMPTY(bb)) {
|
if (!APR_BRIGADE_EMPTY(bb)) {
|
||||||
c->data_in_output_filters = 1;
|
c->data_in_output_filters = 1;
|
||||||
if (make_a_copy) {
|
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];
|
struct iovec vec[MAX_IOVEC_TO_WRITE];
|
||||||
apr_size_t nvec = 0;
|
apr_size_t nvec = 0;
|
||||||
|
|
||||||
remove_empty_buckets(bb);
|
remove_empty_buckets(bb, c);
|
||||||
|
|
||||||
for (bucket = APR_BRIGADE_FIRST(bb);
|
for (bucket = APR_BRIGADE_FIRST(bb);
|
||||||
bucket != APR_BRIGADE_SENTINEL(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;
|
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;
|
apr_bucket *bucket;
|
||||||
while (((bucket = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) &&
|
while (((bucket = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) &&
|
||||||
(APR_BUCKET_IS_METADATA(bucket) || (bucket->length == 0))) {
|
(APR_BUCKET_IS_METADATA(bucket) || (bucket->length == 0))) {
|
||||||
|
detect_error_bucket(bucket, c);
|
||||||
APR_BUCKET_REMOVE(bucket);
|
APR_BUCKET_REMOVE(bucket);
|
||||||
apr_bucket_destroy(bucket);
|
apr_bucket_destroy(bucket);
|
||||||
}
|
}
|
||||||
@@ -678,6 +690,7 @@ static apr_status_t writev_nonblocking(apr_socket_t *s,
|
|||||||
for (i = offset; i < nvec; ) {
|
for (i = offset; i < nvec; ) {
|
||||||
apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
|
apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
|
||||||
if (APR_BUCKET_IS_METADATA(bucket)) {
|
if (APR_BUCKET_IS_METADATA(bucket)) {
|
||||||
|
detect_error_bucket(bucket, c);
|
||||||
APR_BUCKET_REMOVE(bucket);
|
APR_BUCKET_REMOVE(bucket);
|
||||||
apr_bucket_destroy(bucket);
|
apr_bucket_destroy(bucket);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user