]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stconn: Merge all .shutdown() callback functions in sc_shutdown()
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 5 Mar 2026 19:59:40 +0000 (20:59 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 10 Mar 2026 14:10:34 +0000 (15:10 +0100)
sc_shutdown() is no longer relying on .shutdown() callback functions.
Everything was merged in sc_shutdown() with a test on the app type.

include/haproxy/sc_strm.h
src/stconn.c

index 84f85e6eb140d46c7de6b91fe6604019ca0a6e17..3b442684cdd05575a05b94e5f61f788d46a76e31 100644 (file)
@@ -37,6 +37,7 @@ void sc_update_rx(struct stconn *sc);
 void sc_update_tx(struct stconn *sc);
 
 void sc_abort(struct stconn *sc);
+void sc_shutdown(struct stconn *sc);
 void sc_chk_rcv(struct stconn *sc);
 
 struct task *sc_conn_io_cb(struct task *t, void *ctx, unsigned int state);
@@ -513,11 +514,4 @@ static inline void sc_schedule_shutdown(struct stconn *sc)
        sc->flags |= SC_FL_SHUT_WANTED;
 }
 
-/* Shutdown the SC and notify the endpoint using the data layer */
-static inline void sc_shutdown(struct stconn *sc)
-{
-       if (likely(sc->app_ops->shutdown))
-               sc->app_ops->shutdown(sc);
-}
-
 #endif /* _HAPROXY_SC_STRM_H */
index 7ac9ea1003de8b0fb476ad587122dd9ee16e2bd4..83785b03c0ee7e0a8b3a6328c150f68f3ce05908 100644 (file)
@@ -661,6 +661,84 @@ void sc_abort(struct stconn *sc)
        }
 }
 
+/*
+ * This function performs a shutdown-write on a detached stream connector in a
+ * connected or init state (it does nothing for other states). It either shuts
+ * the write side or marks itself as closed. The buffer flags are updated to
+ * reflect the new state. It does also close everything if the SC was marked as
+ * being in error state. The owner task is woken up if it exists.
+ */
+void sc_shutdown(struct stconn *sc)
+{
+
+       struct channel *ic = sc_ic(sc);
+       struct channel *oc = sc_oc(sc);
+
+       BUG_ON(!sc_strm(sc));
+
+       sc->flags &= ~SC_FL_SHUT_WANTED;
+       if (sc->flags & SC_FL_SHUT_DONE)
+               return;
+       sc->flags |= SC_FL_SHUT_DONE;
+       oc->flags |= CF_WRITE_EVENT;
+       sc_set_hcto(sc);
+       sc_report_term_evt(sc, strm_tevt_type_shutw);
+
+       switch (sc->state) {
+       case SC_ST_RDY:
+       case SC_ST_EST:
+               /* we have to shut before closing, otherwise some short messages
+                * may never leave the system, especially when there are remaining
+                * unread data in the socket input buffer, or when nolinger is set.
+                * However, if SC_FL_NOLINGER is explicitly set, we know there is
+                * no risk so we close both sides immediately.
+                */
+               if (!(sc->flags & (SC_FL_ERROR|SC_FL_NOLINGER|SC_FL_EOS|SC_FL_ABRT_DONE)) &&
+                   !(ic->flags & CF_DONT_READ)) {
+                       if (sc_ep_test(sc, SE_FL_T_MUX|SE_FL_T_APPLET))
+                               se_shutdown(sc->sedesc, SE_SHW_NORMAL);
+                       return;
+               }
+
+               if (sc_ep_test(sc, SE_FL_T_MUX))
+                       se_shutdown(sc->sedesc, SE_SHR_RESET|((sc->flags & SC_FL_NOLINGER) ? SE_SHW_SILENT : SE_SHW_NORMAL));
+               else if (sc_ep_test(sc, SE_FL_T_APPLET))
+                       se_shutdown(sc->sedesc, SE_SHR_RESET|SE_SHW_NORMAL);
+
+               sc->state = SC_ST_DIS;
+               break;
+
+       case SC_ST_CON:
+               if (sc_ep_test(sc, SE_FL_T_MUX)) {
+                       /* we may have to close a pending connection, and mark the
+                        * response buffer as abort
+                        */
+                       se_shutdown(sc->sedesc, SE_SHR_RESET|SE_SHW_SILENT);
+               }
+               __fallthrough;
+       case SC_ST_CER:
+       case SC_ST_QUE:
+       case SC_ST_TAR:
+               /* Note that none of these states may happen with applets */
+               sc->state = SC_ST_DIS;
+               break;
+       default:
+               break;
+       }
+
+       sc->flags &= ~SC_FL_NOLINGER;
+       if (!(sc->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)))
+               sc->flags |= SC_FL_ABRT_DONE;
+       if (sc->flags & SC_FL_ISBACK)
+               __sc_strm(sc)->conn_exp = TICK_ETERNITY;
+
+       if (!sc_ep_test(sc, SE_FL_T_MUX|SE_FL_T_APPLET)) {
+               /* note that if the task exists, it must unregister itself once it runs */
+               if (!(sc->flags & SC_FL_DONT_WAKE))
+                       task_wakeup(sc_strm_task(sc), TASK_WOKEN_IO);
+       }
+}
+
 /* This is to be used after making some room available in a channel. It will
  * return without doing anything if the stream connector's RX path is blocked.
  * It will automatically mark the stream connector as busy processing the end