From: Yann Ylavic Date: Mon, 27 Jun 2016 17:26:12 +0000 (+0000) Subject: mod_proxy_{http,ajp,fcgi}}: don't reuse backend connections with data available X-Git-Tag: 2.5.0-alpha~1465 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d4aa6476b205febaf2f335baf4877790dc2dad4e;p=apache mod_proxy_{http,ajp,fcgi}}: don't reuse backend connections with data available before the request is sent. PR 57832. ap_proxy_check_backend() can be used before ap_proxy_connect_backend() to try to read available data (including from the filters), and is called by ap_proxy_connect_backend() to check the socket state only (as before, still relevant after ap_proxy_check_backend() due to filter data which may not have triggered a real socket operation). git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1750392 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 229e85f651..22eb092b5a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mod_proxy_{http,ajp,fcgi}: don't reuse backend connections with data + available before the request is sent. PR 57832. [Yann Ylavic] + *) mod_sed: Fix 'x' command processing. [Christophe Jaillet] *) core: Drop an invalid Last-Modified header value coming diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index 1487908673..46cff15d5b 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -3408 +3409 diff --git a/include/ap_mmn.h b/include/ap_mmn.h index c4c4188b01..1d3b07a72d 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -532,6 +532,8 @@ * dav_success_proppatch. * 20160608.4 (2.5.0-dev) Add dav_acl_provider, dav_acl_provider_register * dav_get_acl_providers. + * 20160608.5 (2.5.0-dev) Add ap_proxy_check_backend(), and tmp_bb to struct + * proxy_conn_rec. */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -539,7 +541,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20160608 #endif -#define MODULE_MAGIC_NUMBER_MINOR 4 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 5 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 467984dfa8..21043e401a 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -271,6 +271,7 @@ 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; } proxy_conn_rec; typedef struct { @@ -972,6 +973,20 @@ 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); +/** + * Check a connection to the backend + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param s current server record + * @param expect_empty whether to check for empty (no data available) or not + * @return APR_SUCCESS or error status (APR_ENOTEMPTY if expect_empty + * is set but the connection is not empty) + */ +PROXY_DECLARE(apr_status_t) ap_proxy_check_backend(const char *proxy_function, + proxy_conn_rec *conn, + server_rec *s, + int expect_empty); + /** * Make a connection to the backend * @param proxy_function calling proxy scheme (http, ajp, ...) diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c index 42f6eb6690..63bafe0216 100644 --- a/modules/proxy/mod_proxy_ajp.c +++ b/modules/proxy/mod_proxy_ajp.c @@ -783,6 +783,7 @@ static int proxy_ajp_handler(request_rec *r, proxy_worker *worker, break; /* Step Two: Make the Connection */ + ap_proxy_check_backend(scheme, backend, r->server, 1); if (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", diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c index 930424a216..cc22804bd6 100644 --- a/modules/proxy/mod_proxy_fcgi.c +++ b/modules/proxy/mod_proxy_fcgi.c @@ -935,6 +935,7 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker, */ backend->close = 1; if (worker->s->disablereuse_set && !worker->s->disablereuse) { + ap_proxy_check_backend(FCGI_SCHEME, backend, r->server, 1); backend->close = 0; } diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c index ccaa0e634f..2684bd136f 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -2073,6 +2073,7 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker, } /* Step Two: Make the Connection */ + ap_proxy_check_backend(proxy_function, backend, r->server, 1); if (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", diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 6fee211e5b..9666c1df61 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -2699,13 +2699,81 @@ PROXY_DECLARE(apr_status_t) ap_proxy_connect_uds(apr_socket_t *sock, #endif } +PROXY_DECLARE(apr_status_t) ap_proxy_check_backend(const char *proxy_function, + proxy_conn_rec *conn, + server_rec *s, + int expect_empty) +{ + apr_status_t rv; + + if (!conn->sock) { + return APR_ENOTSOCK; + } + + if (conn->connection) { + conn_rec *c = conn->connection; + if (conn->tmp_bb == NULL) { + conn->tmp_bb = apr_brigade_create(c->pool, c->bucket_alloc); + } + rv = ap_get_brigade(c->input_filters, conn->tmp_bb, + AP_MODE_SPECULATIVE, APR_NONBLOCK_READ, 1); + if (rv == APR_SUCCESS && expect_empty) { + apr_off_t len = 0; + apr_brigade_length(conn->tmp_bb, 0, &len); + if (len) { + rv = APR_ENOTEMPTY; + } + } + else if (APR_STATUS_IS_EAGAIN(rv)) { + rv = APR_SUCCESS; + } + apr_brigade_cleanup(conn->tmp_bb); + } + else if (ap_proxy_is_socket_connected(conn->sock)) { + rv = APR_SUCCESS; + } + else { + rv = APR_EPIPE; + } + + if (rv != APR_SUCCESS) { + /* 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); + if (rv != APR_ENOTEMPTY) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00951) + "%s: backend socket is disconnected.", + proxy_function); + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03408) + "%s: reusable backend connection is not empty: " + "forcibly closed", proxy_function); + } + + 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 connected; int loglevel; apr_sockaddr_t *backend_addr = conn->addr; /* the local address to use for the outgoing connection */ @@ -2715,28 +2783,8 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, 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); - } - } - } + connected = (ap_proxy_check_backend(proxy_function, conn, + s, 0) == APR_SUCCESS); while ((backend_addr || conn->uds_path) && !connected) { #if APR_HAVE_SYS_UN_H if (conn->uds_path)