From 2aea38d158067d555928ab889f2fad77d845f1bf Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Fri, 15 Oct 2021 11:09:32 +0000 Subject: [PATCH 1/2] mod_proxy_connect: Honor the smallest of the backend or client timeout. It seems that mod_proxy_connect has never applied any timeout in its tunneling loop. Address this by setting a default timeout in ap_proxy_tunnel_create() since mod_proxy_connect does not overwrite tunnel->timeout (while proxy_http and proxy_wstunnel do). This default timeout is set to the smallest of the backend side or the client side timeout. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1894290 13f79535-47bb-0310-9956-ffa450edef68 (cherry picked from commit dcbf44fb14497add211020ef3629f1f12daac6e1) --- changes-entries/proxy_connect_timeout.txt | 2 ++ modules/proxy/proxy_util.c | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 changes-entries/proxy_connect_timeout.txt diff --git a/changes-entries/proxy_connect_timeout.txt b/changes-entries/proxy_connect_timeout.txt new file mode 100644 index 00000000000..f3ef01580bc --- /dev/null +++ b/changes-entries/proxy_connect_timeout.txt @@ -0,0 +1,2 @@ + *) mod_proxy_connect: Honor the smallest of the backend or client timeout + while tunneling. [Yann Ylavic] diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index f291a0d55f2..4c889d9ed38 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -4673,6 +4673,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel, { apr_status_t rv; conn_rec *c_i = r->connection; + apr_interval_time_t timeout = -1; proxy_tunnel_rec *tunnel; *ptunnel = NULL; @@ -4712,6 +4713,13 @@ PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel, tunnel->origin->pfd->desc.s = ap_get_conn_socket(c_o); tunnel->origin->pfd->client_data = tunnel->origin; + /* Defaults to the smallest timeout of both connections */ + apr_socket_timeout_get(tunnel->client->pfd->desc.s, &timeout); + apr_socket_timeout_get(tunnel->origin->pfd->desc.s, &tunnel->timeout); + if (timeout >= 0 && (tunnel->timeout < 0 || tunnel->timeout > timeout)) { + tunnel->timeout = timeout; + } + /* We should be nonblocking from now on the sockets */ apr_socket_opt_set(tunnel->client->pfd->desc.s, APR_SO_NONBLOCK, 1); apr_socket_opt_set(tunnel->origin->pfd->desc.s, APR_SO_NONBLOCK, 1); From ff0c3b5ad2d7a1f527785398c094b82e3e29baf6 Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Wed, 24 Nov 2021 17:49:47 +0000 Subject: [PATCH 2/2] mod_proxy: SetEnv proxy-nohalfclose to disable half-close tunneling. PR 65662. Some connect/wstunnel protocols might want half-close forwarding while some might not, let's provide an r->subprocess_env opt-out. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1895304 13f79535-47bb-0310-9956-ffa450edef68 (cherry picked from commit 5338e45798e2be5296b57c5b31c0de763f2d8962) --- changes-entries/proxy_half_close.txt | 2 ++ modules/proxy/mod_proxy.h | 3 +- modules/proxy/proxy_util.c | 42 ++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 changes-entries/proxy_half_close.txt diff --git a/changes-entries/proxy_half_close.txt b/changes-entries/proxy_half_close.txt new file mode 100644 index 00000000000..266cbb7b501 --- /dev/null +++ b/changes-entries/proxy_half_close.txt @@ -0,0 +1,2 @@ + *) mod_proxy: SetEnv proxy-nohalfclose (or alike) allows to disable TCP + half-close forwarding when tunneling protocols. [Yann Ylavic] diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 1219e9f1727..35acc49a4a3 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -1335,7 +1335,8 @@ typedef struct { struct proxy_tunnel_conn *client, *origin; apr_size_t read_buf_size; - int replied; + int replied; /* TODO 2.5+: one bit to merge in below bitmask */ + unsigned int nohalfclose :1; } proxy_tunnel_rec; /** diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 4c889d9ed38..a3cf5460487 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -4741,6 +4741,11 @@ PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel, c_i->keepalive = AP_CONN_CLOSE; c_o->keepalive = AP_CONN_CLOSE; + /* Disable half-close forwarding for this request? */ + if (apr_table_get(r->subprocess_env, "proxy-nohalfclose")) { + tunnel->nohalfclose = 1; + } + /* Start with POLLOUT and let ap_proxy_tunnel_run() schedule both * directions when there are no output data pending (anymore). */ @@ -4846,6 +4851,12 @@ static int proxy_tunnel_forward(proxy_tunnel_rec *tunnel, ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, tunnel->r, "proxy: %s: %s read shutdown", tunnel->scheme, in->name); + if (tunnel->nohalfclose) { + /* No half-close forwarding, we are done both ways as + * soon as one side shuts down. + */ + return DONE; + } in->down_in = 1; } else { @@ -4862,7 +4873,7 @@ static int proxy_tunnel_forward(proxy_tunnel_rec *tunnel, PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel) { - int rc = OK; + int status = OK, rc; request_rec *r = tunnel->r; apr_pollset_t *pollset = tunnel->pollset; struct proxy_tunnel_conn *client = tunnel->client, @@ -4897,14 +4908,14 @@ PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel) "(client=%hx, origin=%hx)", scheme, client->pfd->reqevents, origin->pfd->reqevents); - rc = HTTP_GATEWAY_TIME_OUT; + status = HTTP_GATEWAY_TIME_OUT; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10214) "proxy: %s: polling failed", scheme); - rc = HTTP_INTERNAL_SERVER_ERROR; + status = HTTP_INTERNAL_SERVER_ERROR; } - return rc; + goto done; } ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, APLOGNO(10215) @@ -4923,7 +4934,8 @@ PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel) && pfd->desc.s != origin->pfd->desc.s) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10222) "proxy: %s: unknown socket in pollset", scheme); - return HTTP_INTERNAL_SERVER_ERROR; + status = HTTP_INTERNAL_SERVER_ERROR; + goto done; } if (!(pfd->rtnevents & (APR_POLLIN | APR_POLLOUT | @@ -4932,7 +4944,8 @@ PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10220) "proxy: %s: polling events error (%x)", scheme, pfd->rtnevents); - return HTTP_INTERNAL_SERVER_ERROR; + status = HTTP_INTERNAL_SERVER_ERROR; + goto done; } /* We want to write if we asked for POLLOUT and got: @@ -4962,7 +4975,8 @@ PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10221) "proxy: %s: %s flushing failed (%i)", scheme, out->name, rc); - return rc; + status = rc; + goto done; } /* No more pending data. If the other side is not readable @@ -4992,7 +5006,8 @@ PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel) */ rc = proxy_tunnel_forward(tunnel, in); if (rc != OK) { - return rc; + status = rc; + goto done; } } } @@ -5007,15 +5022,20 @@ PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel) || !(pfd->rtnevents & APR_POLLOUT))) { rc = proxy_tunnel_forward(tunnel, tc); if (rc != OK) { - return rc; + status = rc; + goto done; } } } } while (!client->down_out || !origin->down_out); +done: ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(10223) - "proxy: %s: tunnel finished", scheme); - return OK; + "proxy: %s: tunneling returns (%i)", scheme, status); + if (status == DONE) { + status = OK; + } + return status; } PROXY_DECLARE (const char *) ap_proxy_show_hcmethod(hcmethod_t method)