]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: mux-h1: reset the content-length/chunking state after a 1xx response
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 7 Oct 2026 09:26:21 +0000 (11:26 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 16 Oct 2026 17:10:27 +0000 (19:10 +0200)
Since commit a6b32922f ("MINOR: mux-h1: Keep custom "Content-Length: 0"
header in 1xx and 204 messages"), h1_make_headers() emits a
"content-length: 0" header found in a 1xx response and marks it as emitted
with H1S_F_HAVE_CLEN. But once the 1xx response is sent, h1_make_eoh() resets
the response state for the final response and only clears H1S_F_HAVE_O_CONN.
H1S_F_HAVE_CLEN remains set, so the final response's content-length header
is either considered as a duplicate and compared to the reset body length
(0), which makes the response fail with a processing error after its status
line was sent if it is not 0, or skipped when it is 0, leaving a keep-alive
response without any length. H1S_F_HAVE_CHNK could be affected the same way.

The H1 parser drops content-length from 1xx responses, so this can only
happen with servers using another protocol (e.g. H2), which may send it
despite RFC9110 forbidding it.

Now both flags are reset with H1S_F_HAVE_O_CONN after a 1xx response.

A reg-test was added to verify this.

This must be backported wherever commit a6b32922f was backported, so as far
as 3.2.

reg-tests/http-messaging/h2_1xx_clen_to_h1.vtc [new file with mode: 0644]
src/mux_h1.c

diff --git a/reg-tests/http-messaging/h2_1xx_clen_to_h1.vtc b/reg-tests/http-messaging/h2_1xx_clen_to_h1.vtc
new file mode 100644 (file)
index 0000000..5af3b5b
--- /dev/null
@@ -0,0 +1,41 @@
+varnishtest "1xx with content-length 0 from an H2 server followed by a final response with a content-length"
+
+# A "content-length: 0" header is kept on 1xx responses by the H1 mux. It must
+# not prevent the content-length of the final response from being emitted.
+feature ignore_unknown_macro
+
+server s1 {
+       rxpri
+       stream 0 {
+               txsettings
+               rxsettings
+               txsettings -ack
+       } -run
+
+       stream 1 {
+               rxreq
+               txresp -status 103 -nostrend -hdr "content-length" "0" -hdr "link" "</a.css>"
+               txresp -status 200 -hdr "content-length" "5" -body "hello"
+       } -run
+} -start
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    listen li1
+        bind "fd@${li1}"
+        server s1 ${s1_addr}:${s1_port} proto h2
+} -start
+
+client c1 -connect ${h1_li1_sock} {
+       txreq
+       rxresp
+       expect resp.status == 103
+       rxresp
+       expect resp.status == 200
+       expect resp.bodylen == 5
+} -run
index 588eb5dc10a9a7a7351d4ae6e30856f6de9254ba..906126c52da66eba1b40ba42e24513e0f3bcae15 100644 (file)
@@ -2998,7 +2998,8 @@ static size_t h1_make_eoh(struct h1s *h1s, struct h1m *h1m, struct htx *htx, siz
                /* 1xx response was sent, reset response processing */
                h1m_init_res(h1m);
                h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
-               h1s->flags &= ~H1S_F_HAVE_O_CONN;
+               /* "content-length: 0" may have been emitted for the 1xx */
+               h1s->flags &= ~(H1S_F_HAVE_O_CONN|H1S_F_HAVE_CLEN|H1S_F_HAVE_CHNK);
                TRACE_STATE("1xx response xferred", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
        }
        else if (blk->flags & HTX_BLK_FL_EOM) {