changeset 8495:455a8536eaa7 quic

QUIC: limited the number of server-initiated streams. Also, ngx_quic_create_uni_stream() is replaced with ngx_quic_open_stream() which is capable of creating a bidi stream.
author Roman Arutyunyan <arut@nginx.com>
date Mon, 27 Jul 2020 18:51:42 +0300
parents e334ca1b23ba
children c5324bb3a704
files src/event/ngx_event_quic.c src/event/ngx_event_quic.h src/http/v3/ngx_http_v3_streams.c
diffstat 3 files changed, 114 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -39,12 +39,15 @@ typedef struct {
     ngx_rbtree_t                      tree;
     ngx_rbtree_node_t                 sentinel;
 
-    ngx_uint_t                        id_counter;
-
     uint64_t                          received;
     uint64_t                          sent;
     uint64_t                          recv_max_data;
     uint64_t                          send_max_data;
+
+    uint64_t                          server_max_streams_uni;
+    uint64_t                          server_max_streams_bidi;
+    uint64_t                          server_streams_uni;
+    uint64_t                          server_streams_bidi;
 } ngx_quic_streams_t;
 
 
@@ -243,6 +246,8 @@ static ngx_int_t ngx_quic_handle_reset_s
     ngx_quic_header_t *pkt, ngx_quic_reset_stream_frame_t *f);
 static ngx_int_t ngx_quic_handle_stop_sending_frame(ngx_connection_t *c,
     ngx_quic_header_t *pkt, ngx_quic_stop_sending_frame_t *f);
+static ngx_int_t ngx_quic_handle_max_streams_frame(ngx_connection_t *c,
+    ngx_quic_header_t *pkt, ngx_quic_max_streams_frame_t *f);
 
 static void ngx_quic_queue_frame(ngx_quic_connection_t *qc,
     ngx_quic_frame_t *frame);
@@ -494,6 +499,9 @@ ngx_quic_add_handshake_data(ngx_ssl_conn
         }
 #endif
 
+        qc->streams.server_max_streams_bidi = qc->ctp.initial_max_streams_bidi;
+        qc->streams.server_max_streams_uni = qc->ctp.initial_max_streams_uni;
+
         qc->client_tp_done = 1;
     }
 
@@ -2100,6 +2108,17 @@ ngx_quic_payload_handler(ngx_connection_
 
             break;
 
+        case NGX_QUIC_FT_MAX_STREAMS:
+        case NGX_QUIC_FT_MAX_STREAMS2:
+
+            if (ngx_quic_handle_max_streams_frame(c, pkt, &frame.u.max_streams)
+                != NGX_OK)
+            {
+                return NGX_ERROR;
+            }
+
+            break;
+
         case NGX_QUIC_FT_NEW_CONNECTION_ID:
         case NGX_QUIC_FT_RETIRE_CONNECTION_ID:
         case NGX_QUIC_FT_PATH_CHALLENGE:
@@ -3273,6 +3292,35 @@ ngx_quic_handle_stop_sending_frame(ngx_c
 }
 
 
+static ngx_int_t
+ngx_quic_handle_max_streams_frame(ngx_connection_t *c,
+    ngx_quic_header_t *pkt, ngx_quic_max_streams_frame_t *f)
+{
+    ngx_quic_connection_t  *qc;
+
+    qc = c->quic;
+
+    if (f->bidi) {
+        if (qc->streams.server_max_streams_bidi < f->limit) {
+            qc->streams.server_max_streams_bidi = f->limit;
+
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "quic max_streams_bidi:%uL", f->limit);
+        }
+
+    } else {
+        if (qc->streams.server_max_streams_uni < f->limit) {
+            qc->streams.server_max_streams_uni = f->limit;
+
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "quic max_streams_uni:%uL", f->limit);
+        }
+    }
+
+    return NGX_OK;
+}
+
+
 static void
 ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame)
 {
@@ -3750,26 +3798,61 @@ ngx_quic_detect_lost(ngx_connection_t *c
 
 
 ngx_connection_t *
-ngx_quic_create_uni_stream(ngx_connection_t *c)
+ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi)
 {
-    ngx_uint_t              id;
+    size_t                  rcvbuf_size;
+    uint64_t                id;
     ngx_quic_stream_t      *qs, *sn;
     ngx_quic_connection_t  *qc;
 
     qs = c->qs;
     qc = qs->parent->quic;
 
-    id = (qc->streams.id_counter << 2)
-         | NGX_QUIC_STREAM_SERVER_INITIATED
-         | NGX_QUIC_STREAM_UNIDIRECTIONAL;
-
-    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic creating server uni stream #%ui id %ui",
-                   qc->streams.id_counter, id);
-
-    qc->streams.id_counter++;
-
-    sn = ngx_quic_create_stream(qs->parent, id, 0);
+    if (bidi) {
+        if (qc->streams.server_streams_bidi
+            >= qc->streams.server_max_streams_bidi)
+        {
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "quic too many server bidi streams: %uL",
+                           qc->streams.server_streams_bidi);
+            return NULL;
+        }
+
+        id = (qc->streams.server_streams_bidi << 2)
+             | NGX_QUIC_STREAM_SERVER_INITIATED;
+
+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "quic creating server bidi stream %uL/%uL id:%uL",
+                       qc->streams.server_streams_bidi,
+                       qc->streams.server_max_streams_bidi, id);
+
+        qc->streams.server_streams_bidi++;
+        rcvbuf_size = qc->tp.initial_max_stream_data_bidi_local;
+
+    } else {
+        if (qc->streams.server_streams_uni
+            >= qc->streams.server_max_streams_uni)
+        {
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "quic too many server uni streams: %uL",
+                           qc->streams.server_streams_uni);
+            return NULL;
+        }
+
+        id = (qc->streams.server_streams_uni << 2)
+             | NGX_QUIC_STREAM_SERVER_INITIATED
+             | NGX_QUIC_STREAM_UNIDIRECTIONAL;
+
+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "quic creating server uni stream %uL/%uL id:%uL",
+                       qc->streams.server_streams_uni,
+                       qc->streams.server_max_streams_uni, id);
+
+        qc->streams.server_streams_uni++;
+        rcvbuf_size = 0;
+    }
+
+    sn = ngx_quic_create_stream(qs->parent, id, rcvbuf_size);
     if (sn == NULL) {
         return NULL;
     }
--- a/src/event/ngx_event_quic.h
+++ b/src/event/ngx_event_quic.h
@@ -113,7 +113,7 @@ struct ngx_quic_stream_s {
 
 
 void ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_conf_t *conf);
-ngx_connection_t *ngx_quic_create_uni_stream(ngx_connection_t *c);
+ngx_connection_t *ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi);
 void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err,
     const char *reason);
 
--- a/src/http/v3/ngx_http_v3_streams.c
+++ b/src/http/v3/ngx_http_v3_streams.c
@@ -55,16 +55,8 @@ ngx_http_v3_init_connection(ngx_connecti
         return NGX_OK;
     }
 
-    h3c = c->qs->parent->data;
-
-    if (!h3c->settings_sent) {
-        h3c->settings_sent = 1;
-
-        if (ngx_http_v3_send_settings(c) != NGX_OK) {
-            ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR,
-                                            "could not send settings");
-            return NGX_ERROR;
-        }
+    if (ngx_http_v3_send_settings(c) == NGX_ERROR) {
+        return NGX_ERROR;
     }
 
     if ((c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) {
@@ -361,7 +353,7 @@ ngx_http_v3_get_uni_stream(ngx_connectio
         }
     }
 
-    sc = ngx_quic_create_uni_stream(c);
+    sc = ngx_quic_open_stream(c, 0);
     if (sc == NULL) {
         return NULL;
     }
@@ -410,14 +402,19 @@ ngx_http_v3_send_settings(ngx_connection
     ngx_http_v3_srv_conf_t    *h3scf;
     ngx_http_v3_connection_t  *h3c;
 
+    h3c = c->qs->parent->data;
+
+    if (h3c->settings_sent) {
+        return NGX_OK;
+    }
+
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings");
 
     cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL);
     if (cc == NULL) {
-        return NGX_ERROR;
+        return NGX_DECLINED;
     }
 
-    h3c = c->qs->parent->data;
     h3scf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module);
 
     n = ngx_http_v3_encode_varlen_int(NULL,
@@ -441,12 +438,17 @@ ngx_http_v3_send_settings(ngx_connection
         goto failed;
     }
 
+    h3c->settings_sent = 1;
+
     return NGX_OK;
 
 failed:
 
     ngx_http_v3_close_uni_stream(cc);
 
+    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR,
+                                    "could not send settings");
+
     return NGX_ERROR;
 }