[PATCH 04 of 10] QUIC: reusable mode for main connection

Roman Arutyunyan arut at nginx.com
Thu Sep 8 09:06:31 UTC 2022


# HG changeset patch
# User Roman Arutyunyan <arut at nginx.com>
# Date 1662478181 -14400
#      Tue Sep 06 19:29:41 2022 +0400
# Branch quic
# Node ID 0a5de8e68cb8b238a1fb82da93ce583c0fa1a6a1
# Parent  b4662fc66f1e8388f54d988777b741964892cec2
QUIC: reusable mode for main connection.

The connection is automatically switched to this mode by transport layer when
there are no non-cancelable streams.  Currently, cancelable streams are
HTTP/3 encoder/decoder/control streams.

diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -341,6 +341,8 @@ ngx_quic_new_connection(ngx_connection_t
         return NULL;
     }
 
+    ngx_reusable_connection(c, 1);
+
     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "quic connection created");
 
@@ -420,7 +422,7 @@ ngx_quic_input_handler(ngx_event_t *rev)
     if (c->close) {
         qc->error = NGX_QUIC_ERR_NO_ERROR;
         qc->error_reason = "graceful shutdown";
-        ngx_quic_close_connection(c, NGX_OK);
+        ngx_quic_close_connection(c, NGX_ERROR);
         return;
     }
 
@@ -603,12 +605,17 @@ ngx_quic_finalize_connection(ngx_connect
     ngx_quic_connection_t  *qc;
 
     qc = ngx_quic_get_connection(c);
+
+    if (qc->closing) {
+        return;
+    }
+
     qc->error = err;
     qc->error_reason = reason;
     qc->error_app = 1;
     qc->error_ftype = 0;
 
-    ngx_quic_close_connection(c, NGX_OK);
+    ngx_post_event(&qc->close, &ngx_posted_events);
 }
 
 
@@ -630,20 +637,13 @@ ngx_quic_shutdown_connection(ngx_connect
 static void
 ngx_quic_close_handler(ngx_event_t *ev)
 {
-    ngx_connection_t       *c;
-    ngx_quic_connection_t  *qc;
+    ngx_connection_t  *c;
 
     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic close handler");
 
     c = ev->data;
-    qc = ngx_quic_get_connection(c);
 
-    if (qc->closing) {
-        ngx_quic_close_connection(c, NGX_OK);
-
-    } else if (qc->shutdown) {
-        ngx_quic_shutdown_quic(c);
-    }
+    ngx_quic_close_connection(c, NGX_OK);
 }
 
 
@@ -1428,31 +1428,10 @@ ngx_quic_push_handler(ngx_event_t *ev)
 void
 ngx_quic_shutdown_quic(ngx_connection_t *c)
 {
-    ngx_rbtree_t           *tree;
-    ngx_rbtree_node_t      *node;
-    ngx_quic_stream_t      *qs;
     ngx_quic_connection_t  *qc;
 
-    qc = ngx_quic_get_connection(c);
-
-    if (qc->closing) {
-        return;
+    if (c->reusable) {
+        qc = ngx_quic_get_connection(c);
+        ngx_quic_finalize_connection(c, qc->shutdown_code, qc->shutdown_reason);
     }
-
-    tree = &qc->streams.tree;
-
-    if (tree->root != tree->sentinel) {
-        for (node = ngx_rbtree_min(tree->root, tree->sentinel);
-             node;
-             node = ngx_rbtree_next(tree, node))
-        {
-            qs = (ngx_quic_stream_t *) node;
-
-            if (!qs->cancelable) {
-                return;
-            }
-        }
-    }
-
-    ngx_quic_finalize_connection(c, qc->shutdown_code, qc->shutdown_reason);
 }
diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h
--- a/src/event/quic/ngx_event_quic.h
+++ b/src/event/quic/ngx_event_quic.h
@@ -113,6 +113,7 @@ void ngx_quic_shutdown_connection(ngx_co
     const char *reason);
 ngx_int_t ngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err);
 ngx_int_t ngx_quic_shutdown_stream(ngx_connection_t *c, int how);
+void ngx_quic_cancelable_stream(ngx_connection_t *c);
 ngx_int_t ngx_quic_handle_read_event(ngx_event_t *rev, ngx_uint_t flags);
 ngx_int_t ngx_quic_handle_write_event(ngx_event_t *wev, size_t lowat);
 ngx_int_t ngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t len,
diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c
--- a/src/event/quic/ngx_event_quic_streams.c
+++ b/src/event/quic/ngx_event_quic_streams.c
@@ -33,6 +33,7 @@ static ngx_chain_t *ngx_quic_stream_send
 static ngx_int_t ngx_quic_stream_flush(ngx_quic_stream_t *qs);
 static void ngx_quic_stream_cleanup_handler(void *data);
 static ngx_int_t ngx_quic_close_stream(ngx_quic_stream_t *qs);
+static ngx_int_t ngx_quic_can_shutdown(ngx_connection_t *c);
 static ngx_int_t ngx_quic_control_flow(ngx_quic_stream_t *qs, uint64_t last);
 static ngx_int_t ngx_quic_update_flow(ngx_quic_stream_t *qs, uint64_t last);
 static ngx_int_t ngx_quic_update_max_stream_data(ngx_quic_stream_t *qs);
@@ -51,6 +52,10 @@ ngx_quic_open_stream(ngx_connection_t *c
     pc = c->quic ? c->quic->parent : c;
     qc = ngx_quic_get_connection(pc);
 
+    if (qc->shutdown || qc->closing) {
+        return NULL;
+    }
+
     if (bidi) {
         if (qc->streams.server_streams_bidi
             >= qc->streams.server_max_streams_bidi)
@@ -161,13 +166,10 @@ ngx_quic_close_streams(ngx_connection_t 
     ngx_pool_t         *pool;
     ngx_queue_t        *q;
     ngx_rbtree_t       *tree;
+    ngx_connection_t   *sc;
     ngx_rbtree_node_t  *node;
     ngx_quic_stream_t  *qs;
 
-#if (NGX_DEBUG)
-    ngx_uint_t          ns;
-#endif
-
     while (!ngx_queue_empty(&qc->streams.uninitialized)) {
         q = ngx_queue_head(&qc->streams.uninitialized);
         ngx_queue_remove(q);
@@ -185,34 +187,34 @@ ngx_quic_close_streams(ngx_connection_t 
         return NGX_OK;
     }
 
-#if (NGX_DEBUG)
-    ns = 0;
-#endif
-
     node = ngx_rbtree_min(tree->root, tree->sentinel);
 
     while (node) {
         qs = (ngx_quic_stream_t *) node;
         node = ngx_rbtree_next(tree, node);
+        sc = qs->connection;
 
         qs->recv_state = NGX_QUIC_STREAM_RECV_RESET_RECVD;
         qs->send_state = NGX_QUIC_STREAM_SEND_RESET_SENT;
 
-        if (qs->connection == NULL) {
+        if (sc == NULL) {
             ngx_quic_close_stream(qs);
             continue;
         }
 
-        ngx_quic_set_event(qs->connection->read);
-        ngx_quic_set_event(qs->connection->write);
+        ngx_quic_set_event(sc->read);
+        ngx_quic_set_event(sc->write);
 
-#if (NGX_DEBUG)
-        ns++;
-#endif
+        sc->close = 1;
+        sc->read->handler(sc->read);
     }
 
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic connection has %ui active streams", ns);
+    if (tree->root == tree->sentinel) {
+        return NGX_OK;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic connection has active streams");
 
     return NGX_AGAIN;
 }
@@ -587,6 +589,7 @@ ngx_quic_create_stream(ngx_connection_t 
 {
     ngx_log_t              *log;
     ngx_pool_t             *pool;
+    ngx_uint_t              reusable;
     ngx_queue_t            *q;
     ngx_connection_t       *sc;
     ngx_quic_stream_t      *qs;
@@ -639,10 +642,14 @@ ngx_quic_create_stream(ngx_connection_t 
     *log = *c->log;
     pool->log = log;
 
+    reusable = c->reusable;
+    ngx_reusable_connection(c, 0);
+
     sc = ngx_get_connection(c->fd, log);
     if (sc == NULL) {
         ngx_destroy_pool(pool);
         ngx_queue_insert_tail(&qc->streams.free, &qs->queue);
+        ngx_reusable_connection(c, reusable);
         return NULL;
     }
 
@@ -712,6 +719,7 @@ ngx_quic_create_stream(ngx_connection_t 
         ngx_close_connection(sc);
         ngx_destroy_pool(pool);
         ngx_queue_insert_tail(&qc->streams.free, &qs->queue);
+        ngx_reusable_connection(c, reusable);
         return NULL;
     }
 
@@ -724,6 +732,31 @@ ngx_quic_create_stream(ngx_connection_t 
 }
 
 
+void
+ngx_quic_cancelable_stream(ngx_connection_t *c)
+{
+    ngx_connection_t       *pc;
+    ngx_quic_stream_t      *qs;
+    ngx_quic_connection_t  *qc;
+
+    qs = c->quic;
+    pc = qs->parent;
+    qc = ngx_quic_get_connection(pc);
+
+    if (!qs->cancelable) {
+        qs->cancelable = 1;
+
+        if (ngx_quic_can_shutdown(pc) == NGX_OK) {
+            ngx_reusable_connection(pc, 1);
+
+            if (qc->shutdown) {
+                ngx_quic_shutdown_quic(pc);
+            }
+        }
+    }
+}
+
+
 static void
 ngx_quic_empty_handler(ngx_event_t *ev)
 {
@@ -1056,14 +1089,47 @@ ngx_quic_close_stream(ngx_quic_stream_t 
         ngx_quic_queue_frame(qc, frame);
     }
 
+    if (!pc->reusable && ngx_quic_can_shutdown(pc) == NGX_OK) {
+        ngx_reusable_connection(pc, 1);
+    }
+
     if (qc->shutdown) {
-        ngx_post_event(&qc->close, &ngx_posted_events);
+        ngx_quic_shutdown_quic(pc);
     }
 
     return NGX_OK;
 }
 
 
+static ngx_int_t
+ngx_quic_can_shutdown(ngx_connection_t *c)
+{
+    ngx_rbtree_t           *tree;
+    ngx_rbtree_node_t      *node;
+    ngx_quic_stream_t      *qs;
+    ngx_quic_connection_t  *qc;
+
+    qc = ngx_quic_get_connection(c);
+
+    tree = &qc->streams.tree;
+
+    if (tree->root != tree->sentinel) {
+        for (node = ngx_rbtree_min(tree->root, tree->sentinel);
+             node;
+             node = ngx_rbtree_next(tree, node))
+        {
+            qs = (ngx_quic_stream_t *) node;
+
+            if (!qs->cancelable) {
+                return NGX_DECLINED;
+            }
+        }
+     }
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_quic_handle_stream_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
     ngx_quic_frame_t *frame)
diff --git a/src/http/v3/ngx_http_v3_uni.c b/src/http/v3/ngx_http_v3_uni.c
--- a/src/http/v3/ngx_http_v3_uni.c
+++ b/src/http/v3/ngx_http_v3_uni.c
@@ -52,7 +52,7 @@ ngx_http_v3_init_uni_stream(ngx_connecti
         return;
     }
 
-    c->quic->cancelable = 1;
+    ngx_quic_cancelable_stream(c);
 
     us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t));
     if (us == NULL) {
@@ -182,6 +182,11 @@ ngx_http_v3_uni_read_handler(ngx_event_t
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler");
 
+    if (c->close) {
+        ngx_http_v3_close_uni_stream(c);
+        return;
+    }
+
     ngx_memzero(&b, sizeof(ngx_buf_t));
 
     while (rev->ready) {
@@ -262,6 +267,11 @@ ngx_http_v3_uni_dummy_read_handler(ngx_e
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy read handler");
 
+    if (c->close) {
+        ngx_http_v3_close_uni_stream(c);
+        return;
+    }
+
     if (rev->ready) {
         if (c->recv(c, &ch, 1) != 0) {
             ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, NULL);
@@ -404,7 +414,7 @@ ngx_http_v3_get_uni_stream(ngx_connectio
         goto failed;
     }
 
-    sc->quic->cancelable = 1;
+    ngx_quic_cancelable_stream(sc);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http3 create uni stream, type:%ui", type);



More information about the nginx-devel mailing list