Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

h2 websockets backport for 2.4.x #366

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1af7a87
Merge 1907696,1907697 from trunk:
icing Jun 20, 2023
5790a21
Merge r1909769 from trunk (plus sync mod_http2.xml)
icing Jun 20, 2023
f78ede8
Merge r1910157 from trunk
icing Jun 20, 2023
5ddf583
Merge of 1910331,1910386,1910441 from trunk
icing Jun 20, 2023
41f28fc
*) mod_http2: added support for bootstrapping WebSockets via HTTP/2…
icing Jun 20, 2023
28c014a
*) mod_http2: adding checks for websocket support on platform and
icing Jun 21, 2023
e82bf6f
mod_http2: optimization for c1 output passing when collecting
icing Jun 28, 2023
768591d
adjust mmn checks to 2.4.x backport numbers
icing Jun 29, 2023
6d71adb
HTTP/2 WebSockets: adding throughput tests and test client improvements
icing Jun 28, 2023
acda877
tests: add the websockets python module version checks needed for our…
icing Jun 28, 2023
ec5d0cf
*) mod_http2: new directive `H2ProxyRequests on|off` to enable hand…
icing Jun 28, 2023
9af2ab5
lognos for the new debugs
icing Jun 28, 2023
6ee3cb4
test_800_websockets: AH10467 is an expected error for the _fail_proto…
ylavic Jun 28, 2023
17f9f5a
mod_http2: move get_pollfd_from_conn hook outside the HAS_RESPONSE_BU…
icing Jun 29, 2023
6c869df
mod_http2: when failing a request, make sure the request_rec fields
icing Jun 29, 2023
e6610cc
merge aplogno de-duplication fix from trunk
icing Jun 29, 2023
da7d510
mod_http2: add aplogno where makefile did not detect it missing?
icing Jun 29, 2023
ff5fbea
Sync test cases and their version checks with mod_h2
icing Jun 30, 2023
cbd8893
tests: sync with other branches
icing Jun 30, 2023
8f51885
proxy: in proxy tunnels, use the smaller timeout value of
icing Jun 30, 2023
893efea
proxy_util: revert timeout selection on a proxy tunnel back to
icing Jul 6, 2023
eb1198c
HTTP/2, websockets
icing Aug 14, 2023
832142a
check new mmn version and allow websocket CONNECT, even without it
icing Aug 14, 2023
a4c4ffd
Latest sync with mod_h2, mostly cosmetic
icing Aug 16, 2023
c22917d
Fixes after review by rpluem
icing Aug 23, 2023
b3e89ef
Remove line mistakenly left in by merge conflict.
icing Aug 23, 2023
80b3883
increase curl buffering limit, as it changes for version used
icing Aug 23, 2023
2d619de
Port more relaxed stutter check from trunk for test_h2_712
icing Aug 23, 2023
51c6dbe
Fixes after review by @tititiou36
icing Aug 28, 2023
51f9ac0
Increase delay in stutter check 712_02 for cpu starved CI runs
icing Aug 28, 2023
578c1ea
disable test_h2_600-32. only works reliable with r1911964 from trunk
icing Aug 28, 2023
f2b3db5
more test stability adjustments for low cpu availabililty
icing Aug 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ jobs:
# -------------------------------------------------------------------------
- name: HTTP/2 test suite
config: --enable-mods-shared=reallyall --with-mpm=event --enable-mpms-shared=all
pkgs: curl python3-pytest nghttp2-client python3-cryptography python3-requests python3-multipart
pkgs: curl python3-pytest nghttp2-client python3-cryptography python3-requests python3-multipart python3-filelock python3-websockets
env: |
APR_VERSION=1.7.4
APU_VERSION=1.6.3
Expand Down Expand Up @@ -211,7 +211,7 @@ jobs:
### TODO: fix caching here.
- name: MOD_TLS test suite
config: --enable-mods-shared=reallyall --with-mpm=event --enable-mpms-shared=event
pkgs: curl python3-pytest nghttp2-client python3-cryptography python3-requests python3-multipart cargo cbindgen
pkgs: curl python3-pytest nghttp2-client python3-cryptography python3-requests python3-multipart python3-filelock python3-websockets cargo cbindgen
env: |
APR_VERSION=1.7.4
APU_VERSION=1.6.3
Expand Down
19 changes: 10 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -453,15 +453,16 @@ SET(mod_http2_extra_defines ssize_t=long)
SET(mod_http2_extra_includes ${NGHTTP2_INCLUDE_DIR})
SET(mod_http2_extra_libs ${NGHTTP2_LIBRARIES})
SET(mod_http2_extra_sources
modules/http2/h2_bucket_beam.c modules/http2/h2_bucket_eos.c
modules/http2/h2_c1.c modules/http2/h2_c1_io.c
modules/http2/h2_c2.c modules/http2/h2_c2_filter.c
modules/http2/h2_config.c modules/http2/h2_conn_ctx.c
modules/http2/h2_mplx.c modules/http2/h2_headers.c
modules/http2/h2_protocol.c modules/http2/h2_push.c
modules/http2/h2_request.c modules/http2/h2_session.c
modules/http2/h2_stream.c modules/http2/h2_switch.c
modules/http2/h2_util.c modules/http2/h2_workers.c
modules/http2/h2_bucket_beam.c modules/http2/h2_bucket_eos.c
modules/http2/h2_c1.c modules/http2/h2_c1_io.c
modules/http2/h2_c2.c modules/http2/h2_c2_filter.c
modules/http2/h2_config.c modules/http2/h2_conn_ctx.c
modules/http2/h2_mplx.c modules/http2/h2_headers.c
modules/http2/h2_protocol.c modules/http2/h2_push.c
modules/http2/h2_request.c modules/http2/h2_session.c
modules/http2/h2_stream.c modules/http2/h2_switch.c
modules/http2/h2_util.c modules/http2/h2_workers.c
modules/http2/h2_ws.c
)
SET(mod_ldap_extra_defines LDAP_DECLARE_EXPORT)
SET(mod_ldap_extra_libs wldap32)
Expand Down
6 changes: 6 additions & 0 deletions changes-entries/h2_proxyrequests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*) mod_http2: new directive `H2ProxyRequests on|off` to enable handling
of HTTP/2 requests in a forward proxy configuration.
General forward proxying is enabled via `ProxyRequests`. If the
HTTP/2 protocol is also enabled for such a server/host, this new
directive is needed in addition.
[Stefan Eissing]
10 changes: 10 additions & 0 deletions changes-entries/h2_websockets.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*) 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.
[Stefan Eissing]
1 change: 1 addition & 0 deletions configure.in
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,7 @@ fi
if test -d ./test/modules/http2; then
APACHE_FAST_OUTPUT(test/Makefile)
AC_CONFIG_FILES([test/pyhttpd/config.ini])
APACHE_FAST_OUTPUT(test/clients/Makefile)
fi

dnl ## Finalize the variables
Expand Down
62 changes: 62 additions & 0 deletions docs/manual/mod/mod_http2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1080,4 +1080,66 @@ H2EarlyHint Link "</my.css>;rel=preload;as=style"
</usage>
</directivesynopsis>

<directivesynopsis>
<name>H2WebSockets</name>
<description>En-/Disable WebSockets via HTTP/2</description>
<syntax>H2WebSockets on|off</syntax>
<default>H2WebSockets off</default>
<contextlist>
<context>server config</context>
<context>virtual host</context>
</contextlist>
<compatibility>Available in version 2.5.1 and later.</compatibility>

<usage>
<p>
Use <directive>H2WebSockets</directive> to enable or disable
bootstrapping of WebSockets via the HTTP/2 protocol. This
protocol extension is defined in RFC 8441.
</p><p>
Such requests come as a CONNECT with an extra ':protocol'
header. Such requests are transformed inside the module to
their HTTP/1.1 equivalents before passing it to internal
processing.
</p><p>
This means that HTTP/2 WebSockets can be used for a
<directive module="mod_proxy">ProxyPass</directive> with
'upgrade=websocket' parameter without further changes.
</p><p>
For (3rd party) modules that handle WebSockets directly in the
server, the protocol bootstrapping itself will also work. However
the transfer of data does require extra support in case of HTTP/2.
The negotiated WebSocket will not be able to use the client connection
socket for polling IO related events.
</p><p>
Because enabling this feature might break backward compatibility
for such 3rd party modules, it is not enabled by default.
</p>
</usage>
</directivesynopsis>

<directivesynopsis>
<name>H2ProxyRequests</name>
<description>En-/Disable forward proxy requests via HTTP/2</description>
<syntax>H2ProxyRequests on|off</syntax>
<default>H2ProxyRequests off</default>
<contextlist>
<context>server config</context>
<context>virtual host</context>
</contextlist>
<compatibility>Available in version 2.5.1 and later.</compatibility>

<usage>
<p>
Use <directive>H2ProxyRequests</directive> to enable or disable
handling of HTTP/2 requests in a forward proxy configuration.
</p><p>
Similar to <directive module="proxy">ProxyRequests</directive>, this
triggers the needed treatment of requests when HTTP/2 is enabled
in a forward proxy configuration. Both directive should be enabled.
</p><p>
</p>
</usage>
</directivesynopsis>

</modulesynopsis>
3 changes: 2 additions & 1 deletion include/ap_mmn.h
Original file line number Diff line number Diff line change
Expand Up @@ -596,14 +596,15 @@
* 20120211.126 (2.4.55-dev) Add additional hcmethod_t enums and PROXY_WORKER_IS_ERROR
* 20120211.127 (2.4.56-dev) Add ap_proxy_canonenc_ex
* 20120211.128 (2.4.55-dev) Add AP_CTIME_OPTION_GMTOFF to util_time.h
* 20120211.129 (2.4.58-dev) Add ap_get_pollfd_from_conn()
icing marked this conversation as resolved.
Show resolved Hide resolved
*/

#define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */

#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20120211
#endif
#define MODULE_MAGIC_NUMBER_MINOR 128 /* 0...n */
#define MODULE_MAGIC_NUMBER_MINOR 129 /* 0...n */

/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
Expand Down
26 changes: 26 additions & 0 deletions include/http_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "apr_optional.h"
#include "util_filter.h"
#include "ap_expr.h"
#include "apr_poll.h"
#include "apr_tables.h"

#include "http_config.h"
Expand Down Expand Up @@ -1060,6 +1061,31 @@ AP_DECLARE(int) ap_state_query(int query_code);
/** only dump some parts of the config */
#define AP_SQ_RM_CONFIG_DUMP 4

/** Get a apr_pollfd_t populated with descriptor and descriptor type
* and the timeout to use for it.
* @return APR_ENOTIMPL if not supported for a connection.
*/
AP_DECLARE_HOOK(apr_status_t, get_pollfd_from_conn,
(conn_rec *c, struct apr_pollfd_t *pfd,
apr_interval_time_t *ptimeout))

/**
* Pass in a `struct apr_pollfd_t*` and get `desc_type` and `desc`
* populated with a suitable value for polling connection input.
* For primary connection (c->master == NULL), this will be the connection
* socket. For secondary connections this may differ or not be available
* at all.
* Note that APR_NO_DESC may be set to indicate that the connection
* input is already closed.
*
* @param pfd the pollfd to set the descriptor in
* @param ptimeout != NULL to retrieve the timeout in effect
* @return ARP_SUCCESS when the information was assigned.
*/
AP_CORE_DECLARE(apr_status_t) ap_get_pollfd_from_conn(conn_rec *c,
struct apr_pollfd_t *pfd,
apr_interval_time_t *ptimeout);

#ifdef __cplusplus
}
#endif
Expand Down
1 change: 1 addition & 0 deletions modules/http2/config2.m4
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ h2_stream.lo dnl
h2_switch.lo dnl
h2_util.lo dnl
h2_workers.lo dnl
h2_ws.lo dnl
"

dnl
Expand Down
15 changes: 15 additions & 0 deletions modules/http2/h2.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ struct h2_stream;
#define H2_USE_PIPES (APR_FILES_AS_SOCKETS && APR_VERSION_AT_LEAST(1,6,0))
#endif

#if AP_MODULE_MAGIC_AT_LEAST(20120211, 129)
#define H2_USE_POLLFD_FROM_CONN 1
#else
#define H2_USE_POLLFD_FROM_CONN 0
#endif

#if H2_USE_PIPES
#define H2_USE_WEBSOCKETS 1
#else
#define H2_USE_WEBSOCKETS 0
#endif

/**
* The magic PRIamble of RFC 7540 that is always sent when starting
* a h2 communication.
Expand Down Expand Up @@ -62,6 +74,8 @@ extern const char *H2_MAGIC_TOKEN;
#define H2_HEADER_AUTH_LEN 10
#define H2_HEADER_PATH ":path"
#define H2_HEADER_PATH_LEN 5
#define H2_HEADER_PROTO ":protocol"
#define H2_HEADER_PROTO_LEN 9
#define H2_CRLF "\r\n"

/* Size of the frame header itself in HTTP/2 */
Expand Down Expand Up @@ -153,6 +167,7 @@ struct h2_request {
const char *scheme;
const char *authority;
const char *path;
const char *protocol;
apr_table_t *headers;

apr_time_t request_time;
Expand Down
35 changes: 35 additions & 0 deletions modules/http2/h2_bucket_beam.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ static void beam_shutdown(h2_bucket_beam *beam, apr_shutdown_how_e how)
if (how == APR_SHUTDOWN_READWRITE) {
beam->cons_io_cb = NULL;
beam->recv_cb = NULL;
beam->eagain_cb = NULL;
}

/* shutdown sender (or both)? */
Expand Down Expand Up @@ -747,6 +748,9 @@ apr_status_t h2_beam_receive(h2_bucket_beam *beam,

leave:
H2_BEAM_LOG(beam, to, APLOG_TRACE2, rv, "end receive", bb);
if (rv == APR_EAGAIN && beam->eagain_cb) {
beam->eagain_cb(beam->eagain_ctx, beam);
}
apr_thread_mutex_unlock(beam->lock);
return rv;
}
Expand All @@ -769,6 +773,15 @@ void h2_beam_on_received(h2_bucket_beam *beam,
apr_thread_mutex_unlock(beam->lock);
}

void h2_beam_on_eagain(h2_bucket_beam *beam,
h2_beam_ev_callback *eagain_cb, void *ctx)
{
apr_thread_mutex_lock(beam->lock);
beam->eagain_cb = eagain_cb;
beam->eagain_ctx = ctx;
apr_thread_mutex_unlock(beam->lock);
}

void h2_beam_on_send(h2_bucket_beam *beam,
h2_beam_ev_callback *send_cb, void *ctx)
{
Expand Down Expand Up @@ -846,3 +859,25 @@ int h2_beam_report_consumption(h2_bucket_beam *beam)
apr_thread_mutex_unlock(beam->lock);
return rv;
}

int h2_beam_is_complete(h2_bucket_beam *beam)
{
int rv = 0;

apr_thread_mutex_lock(beam->lock);
if (beam->closed)
rv = 1;
else {
apr_bucket *b;
for (b = H2_BLIST_FIRST(&beam->buckets_to_send);
b != H2_BLIST_SENTINEL(&beam->buckets_to_send);
b = APR_BUCKET_NEXT(b)) {
if (APR_BUCKET_IS_EOS(b)) {
rv = 1;
break;
}
}
}
apr_thread_mutex_unlock(beam->lock);
return rv;
}
18 changes: 18 additions & 0 deletions modules/http2/h2_bucket_beam.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ struct h2_bucket_beam {
void *recv_ctx;
h2_beam_ev_callback *send_cb; /* event: buckets were added in h2_beam_send() */
void *send_ctx;
h2_beam_ev_callback *eagain_cb; /* event: a receive results in ARP_EAGAIN */
void *eagain_ctx;

apr_off_t recv_bytes; /* amount of bytes transferred in h2_beam_receive() */
apr_off_t recv_bytes_reported; /* amount of bytes reported as received via callback */
Expand Down Expand Up @@ -205,6 +207,16 @@ void h2_beam_on_consumed(h2_bucket_beam *beam,
void h2_beam_on_received(h2_bucket_beam *beam,
h2_beam_ev_callback *recv_cb, void *ctx);

/**
* Register a callback to be invoked on the receiver side whenever
* APR_EAGAIN is being returned in h2_beam_receive().
* @param beam the beam to set the callback on
* @param egain_cb the callback or NULL, called before APR_EAGAIN is returned
* @param ctx the context to use in callback invocation
*/
void h2_beam_on_eagain(h2_bucket_beam *beam,
h2_beam_ev_callback *eagain_cb, void *ctx);

/**
* Register a call back from the sender side to be invoked when send
* has added buckets to the beam.
Expand Down Expand Up @@ -246,4 +258,10 @@ apr_off_t h2_beam_get_buffered(h2_bucket_beam *beam);
*/
apr_off_t h2_beam_get_mem_used(h2_bucket_beam *beam);

/**
* @return != 0 iff beam has been closed or has an EOS bucket buffered
* waiting to be received.
*/
int h2_beam_is_complete(h2_bucket_beam *beam);

#endif /* h2_bucket_beam_h */
2 changes: 1 addition & 1 deletion modules/http2/h2_c1_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ static apr_status_t pass_output(h2_c1_io *io, int flush)
/* recursive call, may be triggered by an H2EOS bucket
* being destroyed and triggering sending more data? */
AP_DEBUG_ASSERT(0);
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(10456)
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(10456)
"h2_c1_io(%ld): recursive call of h2_c1_io_pass. "
"Denied to prevent output corruption. This "
"points to a bug in the HTTP/2 implementation.",
Expand Down
Loading