From: Christopher Faulet Date: Wed, 23 Sep 2026 04:39:07 +0000 (+0000) Subject: BUG/MINOR: mux-h1: only mark C-L and T-E as sent once the header is emitted X-Git-Tag: v3.5-dev8~123 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=3d416630ceff56885d98fa823e9e88b2862f1a0e;p=haproxy.git BUG/MINOR: mux-h1: only mark C-L and T-E as sent once the header is emitted In h1_make_headers(), the H1S_F_HAVE_CLEN and H1S_F_HAVE_CHNK flags, and for the content-length the message lengths, were set before the header was formatted into the output buffer. If h1_format_htx_hdr() failed because the buffer was full, the function stopped and left the header block in the HTX message so that it was processed again on the next call, which was the expected way to resume. But then the flag was already set and the header was skipped as a duplicate. h1_make_eoh() did not add it back either, since its check relied on the same flags. The message would then be sent with a body but without the header announcing its framing. This requires the output buffer to be full in the middle of the headers. It is not possible by construction for now. But this relies on the state of the output buffer when a message starts. Let's only set these flags and lengths once the header was emitted. This may be backported to stable versions. --- diff --git a/src/mux_h1.c b/src/mux_h1.c index 5ae5a7bae..a047749a4 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -2632,6 +2632,8 @@ static size_t h1_make_headers(struct h1s *h1s, struct h1m *h1m, struct htx *htx, struct buffer outbuf; enum htx_blk_type type; struct ist n, v; + unsigned long long body_len; + unsigned int hdr_flags; uint32_t sz; size_t ret = 0; @@ -2650,6 +2652,12 @@ static size_t h1_make_headers(struct h1s *h1s, struct h1m *h1m, struct htx *htx, if (sz > count) goto error; + /* H1S flags to set once the header is emitted. They must + * not be set before, because if the header does not fit, + * it is processed again on the next call. + */ + hdr_flags = 0; + n = htx_get_blk_name(htx, blk); v = htx_get_blk_value(htx, blk); @@ -2665,10 +2673,10 @@ static size_t h1_make_headers(struct h1s *h1s, struct h1m *h1m, struct htx *htx, if (h1s->flags & H1S_F_HAVE_CHNK) goto nextblk; v = ist("chunked"); - h1s->flags |= H1S_F_HAVE_CHNK; + hdr_flags = H1S_F_HAVE_CHNK; } else if (isteq(n, ist("content-length"))) { - unsigned long long body_len = h1m->body_len; + body_len = h1m->body_len; /* Report error for invalid content-length. * Skip custom content-length headers except "content-length: 0" @@ -2682,8 +2690,7 @@ static size_t h1_make_headers(struct h1s *h1s, struct h1m *h1m, struct htx *htx, goto nextblk; if (h1s->flags & H1S_F_HAVE_CLEN) goto nextblk; - h1m->curr_len = h1m->body_len = body_len; - h1s->flags |= H1S_F_HAVE_CLEN; + hdr_flags = H1S_F_HAVE_CLEN; } else if (isteq(n, ist("connection"))) { /* copy the value because it can be modified, but the HTX blocks will not */ @@ -2722,6 +2729,10 @@ static size_t h1_make_headers(struct h1s *h1s, struct h1m *h1m, struct htx *htx, if (!h1_format_htx_hdr(n, v, &outbuf, hdrs_map)) goto full; + + if (hdr_flags & H1S_F_HAVE_CLEN) + h1m->curr_len = h1m->body_len = body_len; + h1s->flags |= hdr_flags; } else if (type == HTX_BLK_EOH) { h1m->state = H1_MSG_LAST_LF;