]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: stream: reset analyse_exp when analysers are stripped on error
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 4 Nov 2026 08:40:29 +0000 (09:40 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 5 Nov 2026 17:20:59 +0000 (18:20 +0100)
When the client or the server connector reports an error or a timeout that
no analyser handled, process_stream() strips all analysers from the
corresponding channel to let the error propagate, except the flt_end one.

When this happens while an analyser was yielding with the channel's
analyse_exp timer armed (e.g. an SPOE event or a Lua action waiting on an
external party), the stripped analysers can no longer manage nor reset this
timer. If it already expired, update_exp_and_leave() refuses to clear it as
long as the AN_*_FLT_END bit is kept for the two-channel filter
synchronisation, so t->expire is set to a past date, the task is requeued in
the past and process_stream() spins in a tight loop until the watchdog kills
the worker.

The request-side retry path (http_ana.c, http_req_retry) already resets
res->analyse_exp right after the same strip; do the same on both error
propagation paths of process_stream() so that an armed analyse timer can
never survive the analysers it was armed for.

This is the first commit of the series fixing github issue #3559. It must be
backported to all stable versions

src/stream.c

index 45b2c0946d978abe448f5808ffdfeb19d1e9f943..efa83e1f158316e6e511869268b0a4f414e4be73 100644 (file)
@@ -2385,7 +2385,7 @@ struct task *process_stream(struct task *t, void *context, unsigned int state)
                if ((scf->flags & SC_FL_ERROR) || req->flags & (CF_READ_TIMEOUT|CF_WRITE_TIMEOUT)) {
                        /* Report it if the client got an error or a read timeout expired */
                        req->analysers &= AN_REQ_FLT_END;
-                       channel_auto_close(req);
+                       req->analyse_exp = TICK_ETERNITY;
                        if (scf->flags & SC_FL_ERROR) {
                                if (s->be_tgcounters)
                                        _HA_ATOMIC_INC(&s->be_tgcounters->cli_aborts);
@@ -2440,6 +2440,7 @@ struct task *process_stream(struct task *t, void *context, unsigned int state)
                else if ((scb->flags & SC_FL_ERROR) || res->flags & (CF_READ_TIMEOUT|CF_WRITE_TIMEOUT)) {
                        /* Report it if the server got an error or a read timeout expired */
                        res->analysers &= AN_RES_FLT_END;
+                       res->analyse_exp = TICK_ETERNITY;
                        channel_auto_close(res);
                        if (scb->flags & SC_FL_ERROR) {
                                if (s->be_tgcounters)