From: Christopher Faulet Date: Mon, 9 Nov 2026 12:29:50 +0000 (+0100) Subject: BUG/MEDIUM: mux-h2: also consider the fctl_list as pending data for the timeout X-Git-Url: https://git.kaiwu.me/$%7BISSUES%7D/static/gitweb.js?a=commitdiff_plain;h=466c7bd75f468e29139ca40a27f2ccb32a40931e;p=haproxy.git BUG/MEDIUM: mux-h2: also consider the fctl_list as pending data for the timeout The previous commit 87cf2a6b ("BUG/MEDIUM: mux-h2: consider the send list as pending data for the timeout") was incomplete: a stream blocked on the connection window (H2_SF_BLK_MFCTL) may also be orphaned by h2_detach() after its stream connector left, in which case it sits on the fctl_list and not on the send_list. Nothing of its frame was emitted yet (typically an ES/RST frame via a WANT_SHUTW, or the remaining fast-forward bytes of its iobuf), so br_data() on the mux buffer can be zero. Such a connection was still considered idle and, on a backend connection, was armed with no timeout at all while not being reusable: the orphan is still in streams_by_id so it is not moved to the idle list either. It then relied on the peer to send a WINDOW_UPDATE to ever wake up, which a stalled peer never does. The blocked_list is deliberately not considered: streams blocked on their own stream window (H2_SF_BLK_SFCTL) are never orphaned by h2_detach(), so this list is always empty once the last stream connector detached. This matches the original h2c_may_expire() from before commit 3439583dd ("MEDIUM: mux-h2: slightly relax timeout management rules") which considered both lists. mux-fcgi is not affected, it has no flow-control window and no fctl list. This could be backported as far as 2.6, along with 87cf2a6b. Signed-off-by: Christopher Faulet --- diff --git a/src/mux_h2.c b/src/mux_h2.c index 8293ea227..355ec837d 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -1016,11 +1016,13 @@ static void h2c_update_timeout(struct h2c *h2c) if (h2c_may_expire(h2c)) { /* no more streams attached */ - if (br_data(h2c->mbuf) || !LIST_ISEMPTY(&h2c->send_list)) { + if (br_data(h2c->mbuf) || !LIST_ISEMPTY(&h2c->send_list) || + !LIST_ISEMPTY(&h2c->fctl_list)) { /* pending output data, or streams still queued to send - * some (blocked on a missing mux buffer, their data - * being still in their own iobuf): always the regular - * data timeout. + * some (blocked on a missing mux buffer or on the + * connection window, their data or the frame to emit + * being still pending): always the regular data + * timeout. */ h2c->task->expire = tick_add_ifset(now_ms, h2c->timeout); }