Commit 06852d0f authored by Ruediger Pluem's avatar Ruediger Pluem
Browse files

* 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
parent 30fb3270
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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.",
+16 −2
Original line number Diff line number Diff line
@@ -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> */
+9 −3
Original line number Diff line number Diff line
@@ -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 */
+18 −5
Original line number Diff line number Diff line
@@ -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);
                }