1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

*) mod_http2: added support for bootstrapping WebSockets via HTTP/2, as

described in RFC 8441. A new directive 'H2WebSockets on|off' has been
     added. The feature is by default not enabled.
     As also discussed in the manual, this feature should work for setups
     using "ProxyPass backend-url upgrade=websocket" without further changes.
     Special server modules for WebSockets will have to be adapted,
     most likely, as the handling if IO events is different with HTTP/2.
     HTTP/2 WebSockets are supported on platforms with native pipes. This
     excludes Windows.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1910507 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Eissing
2023-06-20 12:01:09 +00:00
parent 93b072e61c
commit 3ed9d65b05
41 changed files with 2530 additions and 95 deletions

View File

@@ -21,6 +21,7 @@
#include "apr_version.h"
#include "apr_strings.h"
#include "apr_hash.h"
#include "http_core.h"
#include "proxy_util.h"
#include "ajp.h"
#include "scgi.h"
@@ -4871,7 +4872,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;
apr_interval_time_t client_timeout = -1, origin_timeout = -1;
proxy_tunnel_rec *tunnel;
*ptunnel = NULL;
@@ -4898,9 +4899,16 @@ PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel,
tunnel->client->bb = apr_brigade_create(c_i->pool, c_i->bucket_alloc);
tunnel->client->pfd = &APR_ARRAY_PUSH(tunnel->pfds, apr_pollfd_t);
tunnel->client->pfd->p = r->pool;
tunnel->client->pfd->desc_type = APR_POLL_SOCKET;
tunnel->client->pfd->desc.s = ap_get_conn_socket(c_i);
tunnel->client->pfd->desc_type = APR_NO_DESC;
rv = ap_get_pollfd_from_conn(tunnel->client->c,
tunnel->client->pfd, &client_timeout);
if (rv != APR_SUCCESS) {
return rv;
}
tunnel->client->pfd->client_data = tunnel->client;
if (tunnel->client->pfd->desc_type == APR_POLL_SOCKET) {
apr_socket_opt_set(tunnel->client->pfd->desc.s, APR_SO_NONBLOCK, 1);
}
tunnel->origin->c = c_o;
tunnel->origin->name = "origin";
@@ -4910,17 +4918,12 @@ PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel,
tunnel->origin->pfd->desc_type = APR_POLL_SOCKET;
tunnel->origin->pfd->desc.s = ap_get_conn_socket(c_o);
tunnel->origin->pfd->client_data = tunnel->origin;
apr_socket_timeout_get(tunnel->origin->pfd->desc.s, &origin_timeout);
apr_socket_opt_set(tunnel->origin->pfd->desc.s, APR_SO_NONBLOCK, 1);
/* Defaults to the biggest 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);
tunnel->timeout = (origin_timeout >= 0 && origin_timeout > client_timeout)?
origin_timeout : client_timeout;
/* Bidirectional non-HTTP stream will confuse mod_reqtimeoout */
ap_remove_input_filter_byhandle(c_i->input_filters, "reqtimeout");
@@ -4938,14 +4941,43 @@ PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel,
tunnel->nohalfclose = 1;
}
/* Start with POLLOUT and let ap_proxy_tunnel_run() schedule both
* directions when there are no output data pending (anymore).
*/
tunnel->client->pfd->reqevents = APR_POLLOUT | APR_POLLERR;
tunnel->origin->pfd->reqevents = APR_POLLOUT | APR_POLLERR;
if ((rv = apr_pollset_add(tunnel->pollset, tunnel->client->pfd))
|| (rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd))) {
return rv;
if (tunnel->client->pfd->desc_type == APR_POLL_SOCKET) {
/* Both ends are sockets, the poll strategy is:
* - poll both sides POLLOUT
* - when one side is writable, remove the POLLOUT
* and add POLLIN to the other side.
* - tunnel arriving data, remove POLLIN from the source
* again and add POLLOUT to the receiving side
* - on EOF on read, remove the POLLIN from that side
* Repeat until both sides are down */
tunnel->client->pfd->reqevents = APR_POLLOUT | APR_POLLERR;
tunnel->origin->pfd->reqevents = APR_POLLOUT | APR_POLLERR;
if ((rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd)) ||
(rv = apr_pollset_add(tunnel->pollset, tunnel->client->pfd))) {
return rv;
}
}
else if (tunnel->client->pfd->desc_type == APR_POLL_FILE) {
/* Input is a PIPE fd, the poll strategy is:
* - always POLLIN on origin
* - use socket strategy described above for client only
* otherwise the same
*/
tunnel->client->pfd->reqevents = 0;
tunnel->origin->pfd->reqevents = APR_POLLIN | APR_POLLHUP |
APR_POLLOUT | APR_POLLERR;
if ((rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd))) {
return rv;
}
}
else {
/* input is already closed, unsual, but we know nothing about
* the tunneled protocol. */
tunnel->client->down_in = 1;
tunnel->origin->pfd->reqevents = APR_POLLIN | APR_POLLHUP;
if ((rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd))) {
return rv;
}
}
*ptunnel = tunnel;
@@ -5054,7 +5086,23 @@ static int proxy_tunnel_transfer(proxy_tunnel_rec *tunnel,
}
del_pollset(tunnel->pollset, in->pfd, APR_POLLIN);
add_pollset(tunnel->pollset, out->pfd, APR_POLLOUT);
if (out->pfd->desc_type == APR_POLL_SOCKET) {
/* if the output is a SOCKET, we can stop polling the input
* until the output signals POLLOUT again. */
add_pollset(tunnel->pollset, out->pfd, APR_POLLOUT);
}
else {
/* We can't use POLLOUT in this direction for the only
* APR_POLL_FILE case we have so far (mod_h2's "signal" pipe),
* we assume that the client's ouput filters chain will block/flush
* if necessary (i.e. no pending data), hence that the origin
* is EOF when reaching here. This direction is over. */
ap_assert(in->down_in && APR_STATUS_IS_EOF(rv));
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, tunnel->r,
"proxy: %s: %s write shutdown",
tunnel->scheme, out->name);
out->down_out = 1;
}
}
return OK;