]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: mux-h1: only mark C-L and T-E as sent once the header is emitted
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 23 Sep 2026 04:39:07 +0000 (04:39 +0000)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 23 Sep 2026 12:05:26 +0000 (14:05 +0200)
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.

src/mux_h1.c

index 5ae5a7bae3fc16c696fd71db4b9d21650899b031..a047749a447ebcba853df4be27bb5ff5258ba7b7 100644 (file)
@@ -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;