Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 8BCCE200BA8 for ; Mon, 24 Oct 2016 12:07:25 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 8A605160AEB; Mon, 24 Oct 2016 10:07:25 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 31531160AE1 for ; Mon, 24 Oct 2016 12:07:24 +0200 (CEST) Received: (qmail 31138 invoked by uid 500); 24 Oct 2016 10:07:18 -0000 Mailing-List: contact cvs-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@httpd.apache.org Received: (qmail 31128 invoked by uid 99); 24 Oct 2016 10:07:18 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 24 Oct 2016 10:07:18 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id D7452C00E7 for ; Mon, 24 Oct 2016 10:07:17 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -1.199 X-Spam-Level: X-Spam-Status: No, score=-1.199 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-2.999] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id fqVHGKEjNlDO for ; Mon, 24 Oct 2016 10:07:13 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTP id C1E125FC47 for ; Mon, 24 Oct 2016 10:07:12 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id E9A19E01DC for ; Mon, 24 Oct 2016 10:06:41 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id E739D3A0386 for ; Mon, 24 Oct 2016 10:06:41 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1766372 - in /httpd/httpd/branches/2.4.x: ./ include/ modules/http/ modules/proxy/ Date: Mon, 24 Oct 2016 10:06:41 -0000 To: cvs@httpd.apache.org From: icing@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20161024100641.E739D3A0386@svn01-us-west.apache.org> archived-at: Mon, 24 Oct 2016 10:07:25 -0000 Author: icing Date: Mon Oct 24 10:06:41 2016 New Revision: 1766372 URL: http://svn.apache.org/viewvc?rev=1766372&view=rev Log: 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. Modified: httpd/httpd/branches/2.4.x/CHANGES httpd/httpd/branches/2.4.x/include/ap_mmn.h httpd/httpd/branches/2.4.x/include/http_request.h httpd/httpd/branches/2.4.x/modules/http/http_request.c httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy.h httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_ajp.c httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_fcgi.c httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_http.c httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c Modified: httpd/httpd/branches/2.4.x/CHANGES URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/CHANGES?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/CHANGES [utf-8] (original) +++ httpd/httpd/branches/2.4.x/CHANGES [utf-8] Mon Oct 24 10:06:41 2016 @@ -66,6 +66,9 @@ Changes with Apache 2.4.24 *) core: CVE-2016-5387: Mitigate [f]cgi "httpoxy" issues. [Dominic Scheirlinck , 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] Modified: httpd/httpd/branches/2.4.x/include/ap_mmn.h URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/include/ap_mmn.h?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/include/ap_mmn.h (original) +++ httpd/httpd/branches/2.4.x/include/ap_mmn.h Mon Oct 24 10:06:41 2016 @@ -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 Modified: httpd/httpd/branches/2.4.x/include/http_request.h URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/include/http_request.h?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/include/http_request.h (original) +++ httpd/httpd/branches/2.4.x/include/http_request.h Mon Oct 24 10:06:41 2016 @@ -337,6 +337,21 @@ void ap_process_async_request(request_re */ 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 */ /** Modified: httpd/httpd/branches/2.4.x/modules/http/http_request.c URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http/http_request.c?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/modules/http/http_request.c (original) +++ httpd/httpd/branches/2.4.x/modules/http/http_request.c Mon Oct 24 10:06:41 2016 @@ -228,41 +228,51 @@ AP_DECLARE(void) ap_die(int type, reques 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, /* 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, } 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 < 0) { + if (num_blank_lines > max_blank_lines) { + /* Enough blank lines with this connection, + * stop and don't recycle it. + */ c->keepalive = AP_CONN_CLOSE; + rv = APR_NOTFOUND; + break; } } + + return rv; } @@ -327,6 +345,7 @@ AP_DECLARE(void) ap_process_request_afte 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_afte * 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; Modified: httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy.h URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy.h?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy.h (original) +++ httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy.h Mon Oct 24 10:06:41 2016 @@ -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_checkproxybl 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_conn 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, ...) Modified: httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_ajp.c URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_ajp.c?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_ajp.c (original) +++ httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_ajp.c Mon Oct 24 10:06:41 2016 @@ -769,7 +769,10 @@ static int proxy_ajp_handler(request_rec break; /* Step Two: Make the Connection */ - if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) { + if (ap_proxy_check_connection(scheme, backend, r->server, 0, + PROXY_CHECK_CONN_EMPTY) + && ap_proxy_connect_backend(scheme, backend, worker, + r->server)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00896) "failed to make connection to backend: %s", backend->hostname); Modified: httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_fcgi.c URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_fcgi.c?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_fcgi.c (original) +++ httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_fcgi.c Mon Oct 24 10:06:41 2016 @@ -953,7 +953,10 @@ static int proxy_fcgi_handler(request_re } /* Step Two: Make the Connection */ - if (ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, r->server)) { + if (ap_proxy_check_connection(FCGI_SCHEME, backend, r->server, 0, + PROXY_CHECK_CONN_EMPTY) + && ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, + r->server)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01079) "failed to make connection to backend: %s", backend->hostname); Modified: httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_http.c URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_http.c?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_http.c (original) +++ httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_http.c Mon Oct 24 10:06:41 2016 @@ -1188,7 +1188,7 @@ apr_status_t ap_proxy_http_process_respo const char *buf; char keepchar; apr_bucket *e; - apr_bucket_brigade *bb, *tmp_bb; + apr_bucket_brigade *bb; apr_bucket_brigade *pass_bb; int len, backasswards; int interim_response = 0; /* non-zero whilst interim 1xx responses @@ -1244,16 +1244,17 @@ apr_status_t ap_proxy_http_process_respo backend->r->proxyreq = PROXYREQ_RESPONSE; apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu", origin->local_addr->port)); - tmp_bb = apr_brigade_create(p, c->bucket_alloc); do { apr_status_t rc; apr_brigade_cleanup(bb); - rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), backend->r, 0, &len); + rc = ap_proxygetline(backend->tmp_bb, buffer, sizeof(buffer), + backend->r, 0, &len); if (len == 0) { /* handle one potential stray CRLF */ - rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), backend->r, 0, &len); + rc = ap_proxygetline(backend->tmp_bb, buffer, sizeof(buffer), + backend->r, 0, &len); } if (len <= 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(01102) @@ -1908,13 +1909,8 @@ static int proxy_http_handler(request_re worker, r->server)) != OK) goto cleanup; - backend->is_ssl = is_ssl; - if (is_ssl) { - ap_proxy_ssl_connection_cleanup(backend, r); - } - /* * In the case that we are handling a reverse proxy connection and this * is not a request that is coming over an already kept alive connection @@ -1939,7 +1935,10 @@ static int proxy_http_handler(request_re break; /* Step Two: Make the Connection */ - if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) { + if (ap_proxy_check_connection(proxy_function, backend, r->server, 1, + PROXY_CHECK_CONN_EMPTY) + && ap_proxy_connect_backend(proxy_function, backend, worker, + r->server)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01114) "HTTP: failed to make connection to backend: %s", backend->hostname); Modified: httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c?rev=1766372&r1=1766371&r2=1766372&view=diff ============================================================================== --- httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c (original) +++ httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c Mon Oct 24 10:06:41 2016 @@ -1305,6 +1305,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_ini static void socket_cleanup(proxy_conn_rec *conn) { conn->sock = NULL; + conn->tmp_bb = NULL; conn->connection = NULL; conn->ssl_hostname = NULL; apr_pool_clear(conn->scpool); @@ -1405,10 +1406,10 @@ static apr_status_t connection_cleanup(v return APR_SUCCESS; } +/* DEPRECATED */ PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn, request_rec *r) { - apr_bucket_brigade *bb; apr_status_t rv; /* @@ -1420,22 +1421,21 @@ PROXY_DECLARE(apr_status_t) ap_proxy_ssl * processed. We don't expect any data to be in the returned brigade. */ if (conn->sock && conn->connection) { - bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - rv = ap_get_brigade(conn->connection->input_filters, bb, + rv = ap_get_brigade(conn->connection->input_filters, conn->tmp_bb, AP_MODE_READBYTES, APR_NONBLOCK_READ, HUGE_STRING_LEN); - if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) { - socket_cleanup(conn); - } - if (!APR_BRIGADE_EMPTY(bb)) { + if (!APR_BRIGADE_EMPTY(conn->tmp_bb)) { apr_off_t len; - rv = apr_brigade_length(bb, 0, &len); + rv = apr_brigade_length(conn->tmp_bb, 0, &len); ap_log_rerror(APLOG_MARK, APLOG_TRACE3, rv, r, "SSL cleanup brigade contained %" APR_OFF_T_FMT " bytes of data.", len); + apr_brigade_cleanup(conn->tmp_bb); + } + if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) { + socket_cleanup(conn); } - apr_brigade_destroy(bb); } return APR_SUCCESS; } @@ -2638,13 +2638,103 @@ PROXY_DECLARE(apr_status_t) ap_proxy_con #endif } +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) +{ + apr_status_t rv = APR_SUCCESS; + proxy_worker *worker = conn->worker; + + if (!PROXY_WORKER_IS_USABLE(worker)) { + /* + * The worker is in error likely done by a different thread / process + * e.g. for a timeout or bad status. We should respect this and should + * not continue with a connection via this worker even if we got one. + */ + rv = APR_EINVAL; + } + else if (conn->connection) { + /* We have a conn_rec, check the full filter stack for things like + * SSL alert/shutdown, filters aside data... + */ + rv = ap_check_pipeline(conn->connection, conn->tmp_bb, + max_blank_lines); + apr_brigade_cleanup(conn->tmp_bb); + if (rv == APR_SUCCESS) { + /* Some data available, the caller might not want them. */ + if (flags & PROXY_CHECK_CONN_EMPTY) { + rv = APR_ENOTEMPTY; + } + } + else if (APR_STATUS_IS_EAGAIN(rv)) { + /* Filter chain is OK and empty, yet we can't determine from + * ap_check_pipeline (actually ap_core_input_filter) whether + * an empty non-blocking read is EAGAIN or EOF on the socket + * side (it's always SUCCESS), so check it explicitely here. + */ + if (ap_proxy_is_socket_connected(conn->sock)) { + rv = APR_SUCCESS; + } + else { + rv = APR_EPIPE; + } + } + } + else if (conn->sock) { + /* For modules working with sockets directly, check it. */ + if (!ap_proxy_is_socket_connected(conn->sock)) { + rv = APR_EPIPE; + } + } + else { + rv = APR_ENOSOCKET; + } + + if (rv == APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, server, + "%s: reusing backend connection %pI<>%pI", + scheme, conn->connection->local_addr, + conn->connection->client_addr); + } + else if (conn->sock) { + /* This clears conn->scpool (and associated data), so backup and + * restore any ssl_hostname for this connection set earlier by + * ap_proxy_determine_connection(). + */ + char ssl_hostname[PROXY_WORKER_RFC1035_NAME_SIZE]; + if (rv == APR_EINVAL + || !conn->ssl_hostname + || PROXY_STRNCPY(ssl_hostname, conn->ssl_hostname)) { + ssl_hostname[0] = '\0'; + } + + socket_cleanup(conn); + if (rv != APR_ENOTEMPTY) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, APLOGNO(00951) + "%s: backend socket is disconnected.", scheme); + } + else { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, server, APLOGNO(03408) + "%s: reusable backend connection is not empty: " + "forcibly closed", scheme); + } + + if (ssl_hostname[0]) { + conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname); + } + } + + return rv; +} + PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, proxy_worker *worker, server_rec *s) { apr_status_t rv; - int connected = 0; int loglevel; apr_sockaddr_t *backend_addr = conn->addr; /* the local address to use for the outgoing connection */ @@ -2654,29 +2744,12 @@ PROXY_DECLARE(int) ap_proxy_connect_back proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); - if (conn->sock) { - if (!(connected = ap_proxy_is_socket_connected(conn->sock))) { - /* This clears conn->scpool (and associated data), so backup and - * restore any ssl_hostname for this connection set earlier by - * ap_proxy_determine_connection(). - */ - char ssl_hostname[PROXY_WORKER_RFC1035_NAME_SIZE]; - if (!conn->ssl_hostname || PROXY_STRNCPY(ssl_hostname, - conn->ssl_hostname)) { - ssl_hostname[0] = '\0'; - } - - socket_cleanup(conn); - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00951) - "%s: backend socket is disconnected.", - proxy_function); - - if (ssl_hostname[0]) { - conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname); - } - } + rv = ap_proxy_check_connection(proxy_function, conn, s, 0, 0); + if (rv == APR_EINVAL) { + return DECLINED; } - while ((backend_addr || conn->uds_path) && !connected) { + + while (rv != APR_SUCCESS && (backend_addr || conn->uds_path)) { #if APR_HAVE_SYS_UN_H if (conn->uds_path) { @@ -2848,9 +2921,8 @@ PROXY_DECLARE(int) ap_proxy_connect_back } } } - - connected = 1; } + if (PROXY_WORKER_IS_USABLE(worker)) { /* * Put the entire worker to error state if @@ -2858,7 +2930,7 @@ PROXY_DECLARE(int) ap_proxy_connect_back * Although some connections may be alive * no further connections to the worker could be made */ - if (!connected) { + if (rv != APR_SUCCESS) { if (!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { worker->s->error_time = apr_time_now(); worker->s->status |= PROXY_WORKER_IN_ERROR; @@ -2879,7 +2951,6 @@ PROXY_DECLARE(int) ap_proxy_connect_back worker->s->error_time = 0; worker->s->retries = 0; } - return connected ? OK : DECLINED; } else { /* @@ -2887,11 +2958,13 @@ PROXY_DECLARE(int) ap_proxy_connect_back * e.g. for a timeout or bad status. We should respect this and should * not continue with a connection via this worker even if we got one. */ - if (connected) { + if (rv == APR_SUCCESS) { socket_cleanup(conn); } - return DECLINED; + rv = APR_EINVAL; } + + return rv == APR_SUCCESS ? OK : DECLINED; } static apr_status_t connection_shutdown(void *theconn) @@ -2936,6 +3009,7 @@ PROXY_DECLARE(int) ap_proxy_connection_c } bucket_alloc = apr_bucket_alloc_create(conn->scpool); + conn->tmp_bb = apr_brigade_create(conn->scpool, bucket_alloc); /* * The socket is now open, create a new backend server connection */