]> git.kaiwu.me - haproxy.git/commitdiff
MEDIUM: mux-h2: Don't mix HTTP and tunneled data in the same buffer
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 11 Sep 2026 15:43:13 +0000 (17:43 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 18 Sep 2026 15:05:18 +0000 (17:05 +0200)
For a tunneled stream, the end of the HTTP message is reported just after
the headers while the tunneled data are received after. Because the
information about the end of the message was a flag on the HTX message
itself, both could be stored in the same buffer without any issue.

This is no longer true. The end of the message is now carried by the tail
block of the HTX message. So nothing must be added after this block,
otherwise the message would no longer be seen as ended. It means HTTP data
and tunneled data must never be mixed in the same buffer.

Two changes are required for that:

  * h2s_may_append_to_rxbuf() now refuses to append data to an rxbuf
    carrying a message already ended. h2_frt_transfer_data() thus gets an
    extra rxbuf to store the tunneled data. If no buffer is available, the
    demux is blocked, as usual.

  * h2_rcv_buf() no longer transfers an rxbuf to the upper layer when the
    HTX message in the channel buffer was already ended. SE_FL_RCV_MORE and
    SE_FL_WANT_ROOM are reported instead, so the transfer is retried once
    the HTTP part was consumed.

Note the H1 multiplexer does not have this issue because the demux is
blocked, thanks to H1S_F_RX_BLK flag, until the tunnel is established.

This patch is a prerequisite to be able to harden the HTX API and forbid
any addition in a message already ended.

src/mux_h2.c

index 42feb750a9d242f6efaecb5c2e7cf97b6e3ee4ea..5cea5cab6eedd5f3a4f9ea0153c4befbd656a8fc 100644 (file)
@@ -983,6 +983,15 @@ static inline int h2s_may_append_to_rxbuf(const struct h2s *h2s)
                return 0;
 
        htx = htxbuf(rxbuf);
+
+       /* Nothing must be added in a message already ended. For a tunneled
+        * stream, the end of the HTTP message is reported just after the
+        * headers. So tunneled data must be stored in a separate buffer to
+        * never mix HTTP data and tunneled data.
+        */
+       if (htx_has_eom(htx))
+               return 0;
+
        return !!htx_free_data_space(htx);
 }
 
@@ -8055,9 +8064,22 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
                htx_to_buf(h2s_htx, rxbuf);
                goto end;
        }
-       ret += h2s_htx->data;
        buf_htx = htx_from_buf(buf);
 
+       /* Nothing must be added in a message already ended. For a tunneled
+        * stream, the end of the HTTP message is reported just after the
+        * headers while tunneled data are stored in a separate buffer. So
+        * these data must be transferred only once the upper layer has
+        * consumed the HTTP part.
+        */
+       if (htx_has_eom(buf_htx)) {
+               htx_to_buf(buf_htx, buf);
+               htx_to_buf(h2s_htx, rxbuf);
+               goto end;
+       }
+
+       ret += h2s_htx->data;
+
        /* <buf> is empty and the message is small enough, swap the
         * buffers. */
        if (htx_is_empty_noerr(buf_htx) && htx_used_space(h2s_htx) <= count) {