From: Christopher Faulet Date: Fri, 6 Nov 2026 09:40:06 +0000 (+0100) Subject: BUG/MEDIUM: mux-fcgi: detect an early connection error during the handshake X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=409136c3b73177dcc1597bfff51cd6886ee17e01;p=haproxy.git BUG/MEDIUM: mux-fcgi: detect an early connection error during the handshake fcgi_conn_is_dead() treats a connection with FCGI_CF_ERROR as dead, but not one with FCGI_CF_ERR_PENDING. The latter is set by fcgi_send() when the transport reports an error, and is only turned into FCGI_CF_ERROR once FCGI_CF_EOS is also set, i.e. once a read0 was seen. A connection whose transport failed while the GET_VALUES handshake was still in progress carries FCGI_CF_ERR_PENDING alone, and is therefore considered alive, so nothing closes it: fcgi_send() bails out immediately on the flag, fcgi_recv() is not allowed to run either (fcgi_recv_allowed() refuses when the wait event is subscribed for SEND only... in practice nothing makes progress any more), and h_detach()'s idle-list branch excludes it so it is not parked either. It is only released when the idle-connection purge reaches it. This is the same situation mux-h2 fixed in commit 21fb6bdab ("BUG/MEDIUM: mux-h2: Be able to detect connection error during handshake"), which reports a connection as dead when H2_CF_ERR_PENDING is set before any frame was received. Let's do the same here: a connection with FCGI_CF_ERR_PENDING whose state is still below FCGI_CS_RECORD_H has not exchanged a single FCGI record yet, so it cannot be reused and is dropped. The state threshold differs from mux-h2: there, H2_CS_FRAME_H means "waiting for the next frame header" and is reached before any frame is processed, while FCGI_CS_RECORD_H is the state where records may be exchanged, the three states below it covering only the GET_VALUES handshake. This should be backported to all supported versions. --- diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c index d6673f687..2d4d6253c 100644 --- a/src/mux_fcgi.c +++ b/src/mux_fcgi.c @@ -891,6 +891,8 @@ static inline int fcgi_conn_is_dead(struct fcgi_conn *fconn) if (eb_is_empty(&fconn->streams_by_id) && /* don't close if streams exist */ (!(fconn->flags & FCGI_CF_KEEP_CONN) || /* don't keep the connection alive */ (fconn->flags & FCGI_CF_ERROR) || /* errors close immediately */ + (fconn->flags & FCGI_CF_ERR_PENDING && + fconn->state < FCGI_CS_RECORD_H) || /* early error during connect */ (fconn->state == FCGI_CS_CLOSED && !fconn->task) ||/* a timeout stroke earlier */ (!(fconn->conn->owner)) || /* Nobody's left to take care of the connection, drop it now */ (!br_data(fconn->mbuf) && /* mux buffer empty, also process clean events below */