--- modules/proxy/mod_proxy.h (revision 1605809) +++ modules/proxy/mod_proxy.h (working copy) @@ -234,6 +234,9 @@ typedef struct { apr_array_header_t* cookie_domains; } proxy_req_conf; +/* Opaque aside connection record */ +typedef struct ap_proxy_aside_rec ap_proxy_aside_rec; + typedef struct { conn_rec *connection; request_rec *r; /* Request record of the backend request @@ -254,6 +257,7 @@ typedef struct { * filter chain or not */ unsigned int inreslist:1; /* connection in apr_reslist? */ const char *uds_path; /* Unix domain socket path */ + ap_proxy_aside_rec *aside; /* Aside context */ } proxy_conn_rec; typedef struct { @@ -834,7 +838,43 @@ PROXY_DECLARE(int) ap_proxy_acquire_connection(con proxy_conn_rec **conn, proxy_worker *worker, server_rec *s); + /** + * Function types usable by ap_proxy_acquire_connection_ex(). + */ +typedef apr_status_t (*ap_proxy_acquire_fn)(void *baton, proxy_conn_rec **conn, + proxy_worker *worker, + server_rec *s); +typedef apr_status_t (*ap_proxy_release_fn)(void *baton, proxy_conn_rec *conn); + +/** + * Acquire a connection using the given @acquire function and register + * a @release callback for it on cleanup, or use the worker connection + * pool if the associated @baton is NULL. + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param worker worker used for obtaining connection + * @param s current server record + * @param baton associated baton (or conn_rec if @acquire is NULL) + * @param acquire function (if not NULL) used to acquire aside connections, + * given @baton, @conn, @worker, and @s + * @param release function (if not NULL) used to release acquired connection, + * given @baton, @conn, @worker, and @s, as callback + * @return OK or HTTP_XXX error + * @note If the connection limit has been reached, the function will + * block until a connection becomes available or the timeout has + * elapsed. + * @note If the given @baton is not NULL and @acquire is NULL, the baton is + * considered to be a conn_rec to which the connection is attached (by worker). + */ +PROXY_DECLARE(int) ap_proxy_acquire_connection_ex(const char *proxy_function, + proxy_conn_rec **conn, + proxy_worker *worker, + server_rec *s, void *baton, + ap_proxy_acquire_fn acquire, + ap_proxy_release_fn release); + +/** * Release a connection back to worker connection pool * @param proxy_function calling proxy scheme (http, ajp, ...) * @param conn acquired connection --- modules/proxy/mod_proxy_http.c (revision 1605809) +++ modules/proxy/mod_proxy_http.c (working copy) @@ -1924,8 +1924,16 @@ static int proxy_http_handler(request_rec *r, prox /* create space for state information */ - if ((status = ap_proxy_acquire_connection(proxy_function, &backend, - worker, r->server)) != OK) + if (apr_table_get(r->subprocess_env, "proxy-aside-c")) { + status = ap_proxy_acquire_connection_ex(proxy_function, &backend, + worker, r->server, c, + NULL, NULL); + } + else { + status = ap_proxy_acquire_connection(proxy_function, &backend, + worker, r->server); + } + if (status != OK) goto cleanup; --- modules/proxy/mod_proxy_ajp.c (revision 1605809) +++ modules/proxy/mod_proxy_ajp.c (working copy) @@ -718,6 +718,12 @@ static int proxy_ajp_handler(request_rec *r, proxy ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00895) "serving URL %s", url); /* create space for state information */ + if (apr_table_get(r->subprocess_env, "proxy-aside-c")) { + status = ap_proxy_acquire_connection_ex(scheme, &backend, worker, + r->server, r->connection, + NULL, NULL); + } + else status = ap_proxy_acquire_connection(scheme, &backend, worker, r->server); if (status != OK) { --- modules/proxy/proxy_util.c (revision 1605809) +++ modules/proxy/proxy_util.c (working copy) @@ -71,6 +71,13 @@ static struct wstat { {0x0, '\0', NULL} }; +/* Opaque aside connection record */ +struct ap_proxy_aside_rec { + void *baton; + ap_proxy_release_fn release; + apr_time_t expiry; /* idle connection expiry */ +}; + /* Global balancer counter */ int PROXY_DECLARE_DATA proxy_lb_workers = 0; static int lb_workers_limit = 0; @@ -1347,6 +1354,13 @@ static void init_conn_pool(apr_pool_t *p, proxy_wo worker->cp = cp; } +static void socket_cleanup(proxy_conn_rec *conn) +{ + conn->sock = NULL; + conn->connection = NULL; + apr_pool_clear(conn->scpool); +} + static apr_status_t connection_cleanup(void *theconn) { proxy_conn_rec *conn = (proxy_conn_rec *)theconn; @@ -1374,6 +1388,21 @@ static apr_status_t connection_cleanup(void *theco return APR_SUCCESS; } + /* If the connection is aside (not acquired from the reslist), + * let it be released by the callback if any, or live with its + * parent pool otherwise (still ensuring it is closed if asked to). + */ + if (conn->aside) { + if (conn->close) { + socket_cleanup(conn); + conn->close = 0; + } + if (conn->aside->release) { + conn->aside->release(conn->aside->baton, conn); + } + return APR_SUCCESS; + } + /* determine if the connection need to be closed */ if (conn->close || !worker->s->is_address_reusable || worker->s->disablereuse) { apr_pool_t *p = conn->pool; @@ -1398,13 +1427,6 @@ static apr_status_t connection_cleanup(void *theco return APR_SUCCESS; } -static void socket_cleanup(proxy_conn_rec *conn) -{ - conn->sock = NULL; - conn->connection = NULL; - apr_pool_clear(conn->scpool); -} - PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn, request_rec *r) { @@ -2049,11 +2071,61 @@ PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr return connected ? 0 : 1; } -PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, - proxy_conn_rec **conn, - proxy_worker *worker, - server_rec *s) +/* For the given conn_rec (baton), handle a connection per worker */ +static apr_status_t proxy_acquire_from_c(void *baton, proxy_conn_rec **conn, + proxy_worker *worker, server_rec *s) { + apr_hash_t *conns; + ap_proxy_aside_rec *aside; + conn_rec *c = baton; + + conns = ap_get_module_config(c->conn_config, &proxy_module); + if (!conns) { + conns = apr_hash_make(c->pool); + ap_set_module_config(c->conn_config, &proxy_module, conns); + *conn = NULL; + } + else { + *conn = apr_hash_get(conns, &worker->s, sizeof worker->s); + } + if (!*conn) { + connection_constructor((void **)conn, worker, c->pool); + apr_hash_set(conns, &worker->s, sizeof worker->s, *conn); + aside = apr_pcalloc((*conn)->pool, sizeof *aside); + (*conn)->aside = aside; + } + else { + aside = (*conn)->aside; + if (aside->expiry) { + if (apr_time_now() >= aside->expiry) { + socket_cleanup(*conn); + } + aside->expiry = 0; + } + } + + return APR_SUCCESS; +} + +static apr_status_t proxy_release_to_c(void *baton, proxy_conn_rec *conn) +{ + if (conn->worker->s->ttl) { + conn->aside->expiry = apr_time_now() + + apr_time_from_sec(conn->worker->s->ttl); + } + else { + conn->aside->expiry = 0; + } + return APR_SUCCESS; +} + +PROXY_DECLARE(int) ap_proxy_acquire_connection_ex(const char *proxy_function, + proxy_conn_rec **conn, + proxy_worker *worker, + server_rec *s, void *baton, + ap_proxy_acquire_fn acquire, + ap_proxy_release_fn release) +{ apr_status_t rv; if (!PROXY_WORKER_IS_USABLE(worker)) { @@ -2068,7 +2140,37 @@ PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr } } - if (worker->s->hmax && worker->cp->res) { + /* Aside connection case, use the given functions. + * When a baton is given with no acquire function, it is + * assumed to be a conn_rec (to acquire from), hence the + * corresponding functions are installed. + */ + if (acquire || (baton && (acquire = proxy_acquire_from_c) + && (release = proxy_release_to_c))) { + rv = acquire(baton, conn, worker, s); + if (rv == APR_SUCCESS) { + if (!(*conn)->aside) { + (*conn)->aside = apr_pcalloc((*conn)->pool, + sizeof *(*conn)->aside); + } + (*conn)->aside->release = release; + (*conn)->aside->baton = baton; + } + else if (rv != APR_NOTFOUND) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO() + "%s: failed to acquire aside connection for (%s)", + proxy_function, worker->s->hostname); + return HTTP_SERVICE_UNAVAILABLE; + } + } + else { + rv = APR_NOTFOUND; + } + + if (rv == APR_SUCCESS) { + /* got/use aside connection */ + } + else if (worker->s->hmax && worker->cp->res) { rv = apr_reslist_acquire(worker->cp->res, (void **)conn); } else { @@ -2090,8 +2192,9 @@ PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr return HTTP_SERVICE_UNAVAILABLE; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00942) - "%s: has acquired connection for (%s)", - proxy_function, worker->s->hostname); + "%s: has acquired %sconnection for (%s)", + proxy_function, (*conn)->aside ? "aside " : "", + worker->s->hostname); (*conn)->worker = worker; (*conn)->close = 0; @@ -2123,13 +2226,23 @@ PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr return OK; } +PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, + proxy_conn_rec **conn, + proxy_worker *worker, + server_rec *s) +{ + return ap_proxy_acquire_connection_ex(proxy_function, conn, + worker, s, NULL, NULL, NULL); +} + PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function, proxy_conn_rec *conn, server_rec *s) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00943) - "%s: has released connection for (%s)", - proxy_function, conn->worker->s->hostname); + "%s: has released %sconnection for (%s)", + proxy_function, conn->aside ? "aside " : "", + conn->worker->s->hostname); connection_cleanup(conn); return OK;