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.
--- /dev/null
+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
/* 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) {