BUG/MEDIUM: mux-h2: fix the detection of the ext connect support

As reported by Huangbin Zhan (@zhanhb) in github issue #3355, latest
commit 96f7ff4fdd ("MINOR: mux-h2: add a new message flag to indicate
ext connect support") was not correct and can break RFC8441-compliant
clients, as it did for them with a variant of Chrome 142.

The problem is that while RFC9113 says that new pseudo-headers are only
permitted with *negotiated* extensions, and RFC8441 doesn't indicate
whether or not SETTINGS_ENABLE_CONNECT_PROTOCOL is needed from clients,
it only says that clients know that servers support the extension when
seeing it in their settings and can use it, which seems to imply that
they don't need to send it to indicate their willingness to use it.
This also means that the server cannot know if a client is expected to
use it or not by default. It only know that a client is not allowed to
use it if the server didn't emit support mentioning it, which haproxy
can do using h2-workaround-bogus-websocket-clients.

Thus the fix proposed by @zhanhb is right, when presetting the flag for
the parser to indicate whether or not we're willing to accept RFC8441's
:protocol pseudo-header, we should:
  - consider the received setting on the backend side (though the
    pseudo-header is neither used nor supported there, but at least
    we pass the info regarding the support of the extension)
  - consider the configuration for the frontend (since it's the only
    place where we can decide on support or not)

This patch does just that and reverts the accompanying changes to the
regtests that made them want to see the client's setting. It must be
backported to 2.6.

In the mean time, placing this option in the global section will force
the clients to downgrade to h1:

    h2-workaround-bogus-websocket-clients

Many thanks again to @zhanbb this feedback and proposing a tested fix.
This commit is contained in:
Willy Tarreau 2026-05-07 17:15:06 +02:00
parent b587ea1f27
commit 00941af7b7
3 changed files with 15 additions and 62 deletions

View File

@ -72,7 +72,7 @@ server srv_h2 {
txresp \
-status 200
} -run
} -repeat 4 -start
} -repeat 2 -start
# http2 server without support for RFC8441
server srv_h2_no_ws {
@ -219,8 +219,7 @@ client c1_h1_h2 -connect ${hap_frt_h1_h2_sock} {
client c2_h2 -connect ${hap_frt_h2_sock} {
txpri
stream 0 {
# manually send RFC8441 SETTINGS_ENABLE_CONNECT_PROTOCOL
sendhex "00 00 06 04 00 00 00 00 00 00 08 00 00 00 01"
txsettings
rxsettings
txsettings -ack
rxsettings
@ -242,62 +241,11 @@ client c2_h2 -connect ${hap_frt_h2_sock} {
} -run
} -run
# connect to h2 server frontend without extension: must fail
client c2_h2_rej1 -connect ${hap_frt_h2_sock} {
txpri
stream 0 {
# no extension sent
txsettings
rxsettings
txsettings -ack
rxsettings
expect settings.ack == true
} -run
stream 1 {
txreq \
-req "CONNECT" \
-scheme "http" \
-url "/" \
-hdr ":authority" "127.0.0.1" \
-hdr ":protocol" "custom_protocol" \
-nostrend
rxrst
} -run
} -run
# connect to h2 server with ext but :proto with bad method: must fail
client c2_h2_rej2 -connect ${hap_frt_h2_sock} {
txpri
stream 0 {
# manually send RFC8441 SETTINGS_ENABLE_CONNECT_PROTOCOL
sendhex "00 00 06 04 00 00 00 00 00 00 08 00 00 00 01"
rxsettings
txsettings -ack
rxsettings
expect settings.ack == true
} -run
stream 1 {
txreq \
-req "GET" \
-scheme "http" \
-url "/" \
-hdr ":authority" "127.0.0.1" \
-hdr ":protocol" "custom_protocol" \
-nostrend
rxrst
} -run
} -run
# connect to h2 translation frontend
client c3_h2_h1 -connect ${hap_frt_h2_h1_sock} {
txpri
stream 0 {
# manually send RFC8441 SETTINGS_ENABLE_CONNECT_PROTOCOL
sendhex "00 00 06 04 00 00 00 00 00 00 08 00 00 00 01"
txsettings
rxsettings
txsettings -ack
rxsettings
@ -391,8 +339,7 @@ client c8_h2c -connect ${hap_frt_h1_h2c_sock} {
client c9_h2c -connect ${hap_frt_h2_h1_sock} {
txpri
stream 0 {
# manually send RFC8441 SETTINGS_ENABLE_CONNECT_PROTOCOL
sendhex "00 00 06 04 00 00 00 00 00 00 08 00 00 00 01"
txsettings
rxsettings
txsettings -ack
rxsettings

View File

@ -177,8 +177,7 @@ client c3 -connect ${hap_fe2_sock} {
client c4 -connect ${hap_fe3_sock} {
txpri
stream 0 {
# manually send RFC8441 SETTINGS_ENABLE_CONNECT_PROTOCOL
sendhex "00 00 06 04 00 00 00 00 00 00 08 00 00 00 01"
txsettings
rxsettings
txsettings -ack
rxsettings
@ -206,8 +205,7 @@ client c4 -connect ${hap_fe3_sock} {
client c5 -connect ${hap_fe4_sock} {
txpri
stream 0 {
# manually send RFC8441 SETTINGS_ENABLE_CONNECT_PROTOCOL
sendhex "00 00 06 04 00 00 00 00 00 00 08 00 00 00 01"
txsettings
rxsettings
txsettings -ack
rxsettings

View File

@ -6286,8 +6286,16 @@ next_frame:
/* If an Extended CONNECT has been sent on this stream, set message flag
* to convert 200 response to 101 htx response. We only support this if
* the connection supports RFC8441.
* On the backend, that means the origin server advertised the setting.
* On the frontend, RFC 8441 §3 only requires the server (us) to
* advertise it; clients are not required to echo it back. Use whether
* we ourselves advertised it as the gate.
*/
msgf |= (h2c->flags & H2_CF_RCVD_RFC8441) ? H2_MSGF_EXT_CONN_OK : 0;
if (h2c->flags & H2_CF_IS_BACK)
msgf |= (h2c->flags & H2_CF_RCVD_RFC8441) ? H2_MSGF_EXT_CONN_OK : 0;
else if (!(global.tune.options & GTUNE_DISABLE_H2_WEBSOCKET))
msgf |= H2_MSGF_EXT_CONN_OK;
msgf |= (*flags & H2_SF_EXT_CONNECT_SENT) ? H2_MSGF_EXT_CONNECT: 0;
/* when dealing with trailers, we need to check the content-length */