]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: mux-h1: Don't subscribe for sends while output is blocked
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 29 Sep 2026 12:20:26 +0000 (14:20 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 29 Sep 2026 12:38:53 +0000 (14:38 +0200)
A wakup loop could be experienced when tunnel data remained blocked on
server side while waiting for the server response.

Indeed, after sending a CONNECT request, the backend H1 mux blocks output
processing until the server response decides whether a tunnel is
established. Early tunnel data must remain pending during this time.

When the stream connector subscribes for sends, h1_subscribe() normally
subscribes to the transport too if the mux has no pending send
subscription. However, when output processing is blocked, the socket may
already be writable. The transport then wakes the mux, which wakes the
stream, which cannot send and subscribes again. This causes a wakeup loop
while waiting for the response.

Do not add a transport send subscription while H1S_F_TX_BLK is set. Keep the
stream's subscription so that the receive path can wake it when the tunnel
is established, the rejection response completes, or EOS is reported. This
does not prevent flushing the mux's output buffer: h1_send() maintains its
own transport subscription for pending output.

This should be backported to stable versions after a period of observation.

src/mux_h1.c

index a047749a447ebcba853df4be27bb5ff5258ba7b7..385342635d2314bd5fda78e3f53e9457deb0ff28 100644 (file)
@@ -4797,10 +4797,13 @@ static int h1_subscribe(struct stconn *sc, int event_type, struct wait_event *es
                 * If the stconn attempts to subscribe, and the
                 * mux isn't subscribed to the connection, then it
                 * probably means the connection wasn't established
-                * yet, so we have to subscribe.
+                * yet, so we have to subscribe. However, if output
+                * processing is blocked waiting for the response,
+                * connection send readiness cannot unblock it. The
+                * receive path will wake the stream instead.
                 */
                h1c = h1s->h1c;
-               if (!(h1c->wait_event.events & SUB_RETRY_SEND))
+               if (!(h1c->wait_event.events & SUB_RETRY_SEND) && !(h1s->flags & H1S_F_TX_BLK))
                        h1c->conn->xprt->subscribe(h1c->conn,
                                                   h1c->conn->xprt_ctx,
                                                   SUB_RETRY_SEND,