An H1 request carrying "Connection: upgrade" sent to an H2 server is
converted to an Extended CONNECT (RFC8441) whatever its method, while
RFC8441 only defines GET requests, and an Extended CONNECT received by
haproxy on the H2 side is always converted back to a GET. So through an
H2 hop (H1 -> haproxy -> h2 -> haproxy -> H1 server), a bodyless DELETE,
OPTIONS, POST or PUT request with an upgrade reaches the server as a GET
(bodies are rejected however). There's no direct implication to this but
it could at least be confusing, and maybe cause trouble in certain cases
(idempotent methods, HEAD etc).
Let's reject the conversion for methods other than GET, like it is
already done for upgrade requests with a payload since commit
001fb1a54
("BUG/MEDIUM: mux-h1/mux-h2: Reject upgrades with payload on H2 side
only"). This way we don't block them in H1 where they are supported
(and used as shown in the commit above).
The protocol_upgrade reg-test was extended to check that a bodyless
DELETE upgrade request is rejected instead of reaching the server
as a GET.
This could be backported to all stable versions.
expect resp.http.upgrade == "custom_protocol"
} -run
+## connect to h1 translation frontend with a method other than GET: it
+## cannot be converted to an Extended CONNECT, which would be turned into
+## a GET by the h2 frontend, so it must be rejected without reaching srv_h1.
+client c1_h1_h2_delete -connect ${hap_frt_h1_h2_sock} {
+ txreq \
+ -req "DELETE" \
+ -url "/" \
+ -hdr "host: 127.0.0.1" \
+ -hdr "connection: upgrade" \
+ -hdr "upgrade: custom_protocol"
+
+ rxresp
+ expect resp.status == 502
+} -run
+
# connect to h2 server frontend
client c2_h2 -connect ${hap_frt_h2_sock} {
txpri
goto fail;
}
+ /* RFC8441 only maps GET + Upgrade requests. An Extended
+ * CONNECT is turned into a GET on the other side, so
+ * other methods cannot be converted.
+ */
+ if (sl->info.req.meth != HTTP_METH_GET) {
+ TRACE_STATE("cannot convert upgrade for non-GET request", H2_EV_TX_FRAME|H2_EV_TX_HDR, h2c->conn, h2s);
+ goto fail;
+ }
+
do {
if (isteqi(iststop(connection_ist, ','),
ist("upgrade"))) {