]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: mux-h1: don't stall when draining more than a buffer of data
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 7 Oct 2026 09:25:10 +0000 (11:25 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 16 Oct 2026 17:10:27 +0000 (19:10 +0200)
When the H1 connection is in DRAINING state, h1_process() parses the input
data using a stream rxbuf, which limits the amount of data consumed per call
to a buffer minus the reserve. But the input buffer may contain more data
than that. If no more data are received, nothing calls h1_process() again:
the connection only subscribes for reads. The remaining data, the end of the
request payload and possibly the next pipelined request, are then left
unprocessed until the client timeout strikes, which kills the connection and
the pipelined request is lost.

Now, when data are still pending in the input buffer after a parsing pass in
DRAINING state, the parsing is retried.

This was introduced by commit 077906da1 ("MAJOR: mux-h1: Drain requests on
client side before shut a stream down") in 3.0. It must be backported as far
as 3.0.

src/mux_h1.c

index 63f3637960293c49a3a30e024e967666933bcece..588eb5dc10a9a7a7351d4ae6e30856f6de9254ba 100644 (file)
@@ -4296,6 +4296,7 @@ static int h1_process(struct h1c * h1c)
                if (h1s->sess->t_idle == -1)
                        h1s->sess->t_idle = ns_to_ms(now_ns - h1s->sess->accept_ts) - h1s->sess->t_handshake;
 
+       retry_parsing:
                /* Get the stream rxbuf */
                buf = h1_get_rxbuf(h1s);
                if (!buf) {
@@ -4321,6 +4322,10 @@ static int h1_process(struct h1c * h1c)
                                TRACE_ERROR("parsing error detected", H1_EV_H1C_WAKE|H1_EV_H1C_ERR);
                        }
                        else {
+                               /* When draining, if input data remain to parse, retry the parsing now the rxbuf was emptied */
+                               if (h1c->state == H1_CS_DRAINING && (h1s->flags & H1S_F_RX_CONGESTED) && b_data(&h1c->ibuf))
+                                       goto retry_parsing;
+
                                TRACE_STATE("Incomplete message, subscribing", H1_EV_RX_DATA|H1_EV_H1C_BLK|H1_EV_H1C_WAKE, h1c->conn, h1s);
                                h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
                        }