]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-quic: implement http-keep-alive timeout
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 25 Jul 2022 09:53:18 +0000 (11:53 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 1 Aug 2022 13:00:13 +0000 (15:00 +0200)
Complete QUIC MUX timeout refresh function by using http-keep-alive
timeout. It is used when the connection is idle after having handle at
least one request.

To implement this a new member <idle_start> has been defined in qcc
structure. This is used as timestamp for when the connection became idle
and is used as base time for http keep-alive timeout

include/haproxy/mux_quic-t.h
src/mux_quic.c

index f7e538bafa864ef138f35e93d9659fbafbfe82a0..38d2af46aa38a43d65d9bb6a7d41b1634eb2c20a 100644 (file)
@@ -97,6 +97,7 @@ struct qcc {
        /* haproxy timeout management */
        struct task *task;
        int timeout;
+       int idle_start; /* base time for http-keep-alive timeout */
 
        const struct qcc_app_ops *app_ops;
        void *ctx; /* Application layer context */
index 549bcb0552275bc797bb3dd7b4c82e871eeaa1f4..d4e69d079ff140970d1574f06c7c1306e95469a3 100644 (file)
@@ -229,11 +229,23 @@ static forceinline struct stconn *qcs_sc(const struct qcs *qcs)
        return qcs->sd ? qcs->sd->sc : NULL;
 }
 
+/* Reset the <qcc> inactivity timeout for http-keep-alive timeout. */
+static forceinline void qcc_reset_idle_start(struct qcc *qcc)
+{
+       qcc->idle_start = now_ms;
+}
+
 /* Decrement <qcc> sc. */
 static forceinline void qcc_rm_sc(struct qcc *qcc)
 {
        BUG_ON_HOT(!qcc->nb_sc);
        --qcc->nb_sc;
+
+       /* Reset qcc idle start for http-keep-alive timeout. Timeout will be
+        * refreshed after this on stream detach.
+        */
+       if (!qcc->nb_sc && !qcc->nb_hreq)
+               qcc_reset_idle_start(qcc);
 }
 
 /* Decrement <qcc> hreq. */
@@ -241,6 +253,12 @@ static forceinline void qcc_rm_hreq(struct qcc *qcc)
 {
        BUG_ON_HOT(!qcc->nb_hreq);
        --qcc->nb_hreq;
+
+       /* Reset qcc idle start for http-keep-alive timeout. Timeout will be
+        * refreshed after this on I/O handler.
+        */
+       if (!qcc->nb_sc && !qcc->nb_hreq)
+               qcc_reset_idle_start(qcc);
 }
 
 /* Mark a stream as open if it was idle. This can be used on every
@@ -1036,6 +1054,8 @@ static inline int qcc_may_expire(struct qcc *qcc)
 /* Refresh the timeout on <qcc> if needed depending on its state. */
 static void qcc_refresh_timeout(struct qcc *qcc)
 {
+       const struct proxy *px = qcc->proxy;
+
        TRACE_ENTER(QMUX_EV_QCC_WAKE, qcc->conn);
 
        if (!qcc->task)
@@ -1044,12 +1064,28 @@ static void qcc_refresh_timeout(struct qcc *qcc)
        /* Calculate the timeout. */
        if (qcc_may_expire(qcc)) {
                /* TODO implement specific timeouts
-                * - http-keep-alive if connection is idle with no stream
                 * - http-requset for waiting on incomplete streams
                 * - client-fin for graceful shutdown
                 */
-               TRACE_DEVEL("applying default timeout", QMUX_EV_QCC_WAKE, qcc->conn);
-               qcc->task->expire = tick_add_ifset(now_ms, qcc->timeout);
+               if (qcc->nb_hreq) {
+                       /* detached streams left. */
+                       TRACE_DEVEL("one or more requests still in progress", QMUX_EV_QCC_WAKE, qcc->conn);
+                       qcc->task->expire = tick_add_ifset(now_ms, qcc->timeout);
+               }
+               else if (!conn_is_back(qcc->conn) && qcc->largest_bidi_r > 0x00) {
+                       int timeout = tick_isset(px->timeout.httpka) ?
+                                     px->timeout.httpka : px->timeout.httpreq;
+
+                       TRACE_DEVEL("at least one request achieved but none currently in progress", QMUX_EV_QCC_WAKE, qcc->conn);
+                       qcc->task->expire = tick_add_ifset(qcc->idle_start,
+                                                          timeout);
+               }
+
+               /* fallback to default timeout if none set. */
+               if (!tick_isset(qcc->task->expire)) {
+                       TRACE_DEVEL("fallback to default timeout", QMUX_EV_QCC_WAKE, qcc->conn);
+                       qcc->task->expire = tick_add_ifset(now_ms, qcc->timeout);
+               }
 
                /* TODO if connection is idle on frontend and proxy is
                 * disabled, remove it with global close_spread delay applied.
@@ -1881,6 +1917,7 @@ static int qc_init(struct connection *conn, struct proxy *prx,
                qcc->task->context = qcc;
                qcc->task->expire = tick_add(now_ms, qcc->timeout);
        }
+       qcc_reset_idle_start(qcc);
 
        if (!conn_is_back(conn)) {
                if (!LIST_INLIST(&conn->stopping_list)) {