]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: applet: Reenable reads in applet context if requesting a connection
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 10 Jul 2026 09:53:33 +0000 (11:53 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 10 Jul 2026 10:03:18 +0000 (12:03 +0200)
When a applet is waiting for a connection, like Socket applet in lua, reads
are blocked. When the connection is finally available , a read activity is
reported and reads are reenabled (flag SE_FL_APPLET_NEED_CONN is removed).

However, most of time (always ?), this operation is performed in the context
of the connection and the applet is woken up. It means the task expiration
date is not updated where the read activity is reported. It is an issue for
small timeouts because the task may be queued in past, triggering a BUG_ON
in sc_notify(). In fact, a read activity must never be reported in another
context of the endpoint itself or the upper stream, specifically to properly
set the task expiration date.

To fix the issue, in sc_chk_rcv(), the applet is only woken up in that case
and the reads are re-enabled when the applet is executed. For applets using
the new API, it is performed in sc_appet_sync_recv(). For legacy applets, it
is directly performed in task_run_applet().

This patch is related to #3442. It must be backported as far as 2.8. On the
2.8, only task_run_applet must be fixed because sc_applet_sync_recv() does
not exist.

src/applet.c
src/stconn.c

index 63f2654c07406474e6a66045b8c0b980e4f339ed..59969fc1c79e32a6dee475d3b0eec4312feb4dfc 100644 (file)
@@ -826,6 +826,12 @@ struct task *task_run_applet(struct task *t, void *context, unsigned int state)
        applet_need_more_data(app);
        applet_have_no_more_data(app);
 
+       if (sc_ep_test(sc, SE_FL_APPLET_NEED_CONN) &&
+           sc_state_in(sco->state, SC_SB_RDY|SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) {
+               sc_ep_clr(sc, SE_FL_APPLET_NEED_CONN);
+               sc_ep_report_read_activity(sc);
+       }
+
        /* Now we'll try to allocate the input buffer. We wake up the applet in
         * all cases. So this is the applet's responsibility to check if this
         * buffer was allocated or not. This leaves a chance for applets to do
index 6e30b38514f465fa6755f33481f9fb7556be3d9d..8dcdd49700acce62aa99088b3c7cfda43cd03635 100644 (file)
@@ -717,8 +717,11 @@ void sc_chk_rcv(struct stconn *sc)
 
        if (sc_ep_test(sc, SE_FL_APPLET_NEED_CONN) &&
            sc_state_in(sc_opposite(sc)->state, SC_SB_RDY|SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) {
-               sc_ep_clr(sc, SE_FL_APPLET_NEED_CONN);
-               sc_ep_report_read_activity(sc);
+               /* connection available (or closed), so wake applet up to handle
+                * the event. here we are not in the applet context (but most
+                * probably in the connection context).
+                */
+               appctx_wakeup(__sc_appctx(sc));
        }
 
        if (!sc_is_recv_allowed(sc))
@@ -2024,6 +2027,12 @@ int sc_applet_sync_recv(struct stconn *sc)
        if (!appctx_app_test(__sc_appctx(sc), APPLET_FL_NEW_API))
                return 0;
 
+       if (sc_ep_test(sc, SE_FL_APPLET_NEED_CONN) &&
+           sc_state_in(sc_opposite(sc)->state, SC_SB_RDY|SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) {
+               sc_ep_clr(sc, SE_FL_APPLET_NEED_CONN);
+               sc_ep_report_read_activity(sc);
+       }
+
        if (!sc_state_in(sc->state, SC_SB_RDY|SC_SB_EST))
                return 0;