Commit 9299b743 authored by Stefan Eissing's avatar Stefan Eissing
Browse files

Merge of r1750392,r1750412,r1750416,r1750474,r1750494,r1750508 from trunk:

mod_proxy_{http,ajp,fcgi}: don't reuse backend connections with data
     available before the request is sent.  PR 57832.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1766372 13f79535-47bb-0310-9956-ffa450edef68
parent 4caefa01
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -66,6 +66,9 @@ Changes with Apache 2.4.24
  *) core: CVE-2016-5387: Mitigate [f]cgi "httpoxy" issues.
     [Dominic Scheirlinck <dominic vendhq.com>, Yann Ylavic]

  *) mod_proxy_{http,ajp,fcgi}: don't reuse backend connections with data
     available before the request is sent.  PR 57832.  [Yann Ylavic]

  *) mod_proxy_balancer: Prevent redirect loops between workers within a
     balancer by limiting the number of redirects to the number balancer
     members. PR 59864 [Ruediger Pluem]
+6 −1
Original line number Diff line number Diff line
@@ -482,6 +482,11 @@
 *                          dav_finish_multistatus, dav_send_multistatus,
 *                          dav_handle_err, dav_failed_proppatch,
 *                          dav_success_proppatch.
 * 20120211.64 (2.4.24-dev) Add ap_proxy_check_backend(), and tmp_bb to struct
 *                          proxy_conn_rec.
 * 20120211.65 (2.4.24-dev) Add ap_check_pipeline().
 * 20120211.66 (2.4.24-dev) Rename ap_proxy_check_backend() to
 *                          ap_proxy_check_connection().
 */

#define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
@@ -489,7 +494,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20120211
#endif
#define MODULE_MAGIC_NUMBER_MINOR 63                   /* 0...n */
#define MODULE_MAGIC_NUMBER_MINOR 66                   /* 0...n */

/**
 * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
+15 −0
Original line number Diff line number Diff line
@@ -337,6 +337,21 @@ void ap_process_async_request(request_rec *r);
 */
AP_DECLARE(void) ap_die(int type, request_rec *r);

/**
 * Check whether a connection is still established and has data available,
 * optionnaly consuming blank lines ([CR]LF).
 * @param c The current connection
 * @param bb The brigade to filter
 * @param max_blank_lines Max number of blank lines to consume, or zero
 *                        to consider them as data (single read).
 * @return APR_SUCCESS: connection established with data available,
 *         APR_EAGAIN: connection established and empty,
 *         APR_NOTFOUND: too much blank lines,
 *         APR_E*: connection/general error.
 */
AP_DECLARE(apr_status_t) ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb,
                                           unsigned int max_blank_lines);

/* Hooks */

/**
+52 −26
Original line number Diff line number Diff line
@@ -228,41 +228,51 @@ AP_DECLARE(void) ap_die(int type, request_rec *r)
    ap_die_r(type, r, r->status);
}

static void check_pipeline(conn_rec *c, apr_bucket_brigade *bb)
AP_DECLARE(apr_status_t) ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb,
                                           unsigned int max_blank_lines)
{
    apr_status_t rv;
    int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES;
    apr_status_t rv = APR_EOF;
    ap_input_mode_t mode = AP_MODE_SPECULATIVE;
    unsigned int num_blank_lines = 0;
    apr_size_t cr = 0;
    char buf[2];

    c->data_in_input_filters = 0;
    while (c->keepalive != AP_CONN_CLOSE && !c->aborted) {
        apr_size_t len = cr + 1;

        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)) {
            /*
             * Error or empty brigade: There is no data present in the input
             * filter
             */
        if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb) || !max_blank_lines) {
            if (mode == AP_MODE_READBYTES) {
                /* Unexpected error, stop with this connection */
                ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02967)
                              "Can't consume pipelined empty lines");
                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 {
                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;
        }

        /* Ignore trailing blank lines (which must not be interpreted as
         * pipelined requests) up to the limit, otherwise we would block
         * on the next read without flushing data, and hence possibly delay
         * pending response(s) until the next/real request comes in or the
         * keepalive timeout expires.
         */
        /* Lookup and consume blank lines */
        rv = apr_brigade_flatten(bb, buf, &len);
        if (rv != APR_SUCCESS || len != cr + 1) {
            int log_level;
@@ -270,14 +280,15 @@ static void check_pipeline(conn_rec *c, apr_bucket_brigade *bb)
                /* Unexpected error, stop with this connection */
                c->keepalive = AP_CONN_CLOSE;
                log_level = APLOG_ERR;
                rv = APR_EGENERAL;
            }
            else {
                /* Let outside (non-speculative/blocking) read determine
                 * where this possible failure comes from (metadata,
                 * morphed EOF socket => empty bucket? debug only here).
                 * morphed EOF socket, ...). Debug only here.
                 */
                c->data_in_input_filters = 1;
                log_level = APLOG_DEBUG;
                rv = APR_SUCCESS;
            }
            ap_log_cerror(APLOG_MARK, log_level, rv, c, APLOGNO(02968)
                          "Can't check pipelined data");
@@ -285,40 +296,47 @@ static void check_pipeline(conn_rec *c, apr_bucket_brigade *bb)
        }

        if (mode == AP_MODE_READBYTES) {
            /* [CR]LF consumed, try next */
            mode = AP_MODE_SPECULATIVE;
            cr = 0;
        }
        else if (cr) {
            AP_DEBUG_ASSERT(len == 2 && buf[0] == APR_ASCII_CR);
            if (buf[1] == APR_ASCII_LF) {
                /* consume this CRLF */
                mode = AP_MODE_READBYTES;
                num_blank_lines--;
                num_blank_lines++;
            }
            else {
                c->data_in_input_filters = 1;
                /* CR(?!LF) is data */
                break;
            }
        }
        else {
            if (buf[0] == APR_ASCII_LF) {
                /* consume this LF */
                mode = AP_MODE_READBYTES;
                num_blank_lines--;
                num_blank_lines++;
            }
            else if (buf[0] == APR_ASCII_CR) {
                cr = 1;
            }
            else {
                c->data_in_input_filters = 1;
                /* Not [CR]LF, some data */
                break;
            }
        }
        /* Enough blank lines with this connection?
         * Stop and don't recycle it.
        if (num_blank_lines > max_blank_lines) {
            /* Enough blank lines with this connection,
             * stop and don't recycle it.
             */
        if (num_blank_lines < 0) {
            c->keepalive = AP_CONN_CLOSE;
            rv = APR_NOTFOUND;
            break;
        }
    }

    return rv;
}


@@ -327,6 +345,7 @@ AP_DECLARE(void) ap_process_request_after_handler(request_rec *r)
    apr_bucket_brigade *bb;
    apr_bucket *b;
    conn_rec *c = r->connection;
    apr_status_t rv;

    /* Send an EOR bucket through the output filter chain.  When
     * this bucket is destroyed, the request will be logged and
@@ -351,8 +370,15 @@ AP_DECLARE(void) ap_process_request_after_handler(request_rec *r)
     * already by the EOR bucket's cleanup function.
     */

    check_pipeline(c, bb);
    /* Check pipeline consuming blank lines, they must not be interpreted as
     * the next pipelined request, otherwise we would block on the next read
     * without flushing data, and hence possibly delay pending response(s)
     * until the next/real request comes in or the keepalive timeout expires.
     */
    rv = ap_check_pipeline(c, bb, DEFAULT_LIMIT_BLANK_LINES);
    c->data_in_input_filters = (rv == APR_SUCCESS);
    apr_brigade_destroy(bb);

    if (c->cs)
        c->cs->state = (c->aborted) ? CONN_STATE_LINGER
                                    : CONN_STATE_WRITE_COMPLETION;
+27 −0
Original line number Diff line number Diff line
@@ -271,6 +271,10 @@ typedef struct {
    unsigned int inreslist:1;  /* connection in apr_reslist? */
    const char   *uds_path;    /* Unix domain socket path */
    const char   *ssl_hostname;/* Hostname (SNI) in use by SSL connection */
    apr_bucket_brigade *tmp_bb;/* Temporary brigade created with the connection
                                * and its scpool/bucket_alloc (NULL before),
                                * must be left cleaned when used (locally).
                                */
} proxy_conn_rec;

typedef struct {
@@ -632,6 +636,7 @@ PROXY_DECLARE(int) ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *
PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r);
/* DEPRECATED (will be replaced with ap_proxy_connect_backend */
PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, request_rec *);
/* DEPRECATED (will be replaced with ap_proxy_check_connection */
PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
                                                            request_rec *r);
PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c);
@@ -916,6 +921,28 @@ PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
                                               proxy_conn_rec *conn,
                                               server_rec *s);

#define PROXY_CHECK_CONN_EMPTY (1 << 0)
/**
 * Check a connection to the backend
 * @param scheme calling proxy scheme (http, ajp, ...)
 * @param conn   acquired connection
 * @param server current server record
 * @param max_blank_lines how many blank lines to consume,
 *                        or zero for none (considered data)
 * @param flags  PROXY_CHECK_* bitmask
 * @return APR_SUCCESS: connection established,
 *         APR_ENOTEMPTY: connection established with data,
 *         APR_ENOSOCKET: not connected,
 *         APR_EINVAL: worker in error state (unusable),
 *         other: connection closed/aborted (remotely)
 */
PROXY_DECLARE(apr_status_t) ap_proxy_check_connection(const char *scheme,
                                                      proxy_conn_rec *conn,
                                                      server_rec *server,
                                                      unsigned max_blank_lines,
                                                      int flags);

/**
 * Make a connection to the backend
 * @param proxy_function calling proxy scheme (http, ajp, ...)
Loading