changeset 8496:c5324bb3a704 quic

QUIC: limited the number of client-initiated streams. The limits on active bidi and uni client streams are maintained at their initial values initial_max_streams_bidi and initial_max_streams_uni by sending a MAX_STREAMS frame upon each client stream closure. Also, the following is changed for data arriving to non-existing streams: - if a stream was already closed, such data is ignored - when creating a new stream, all streams of the same type with lower ids are created too
author Roman Arutyunyan <arut@nginx.com>
date Mon, 27 Jul 2020 19:15:17 +0300
parents 455a8536eaa7
children 0596fe1aee16
files src/event/ngx_event_quic.c
diffstat 1 files changed, 93 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -48,6 +48,11 @@ typedef struct {
     uint64_t                          server_max_streams_bidi;
     uint64_t                          server_streams_uni;
     uint64_t                          server_streams_bidi;
+
+    uint64_t                          client_max_streams_uni;
+    uint64_t                          client_max_streams_bidi;
+    uint64_t                          client_streams_uni;
+    uint64_t                          client_streams_bidi;
 } ngx_quic_streams_t;
 
 
@@ -122,9 +127,6 @@ struct ngx_quic_connection_s {
     ngx_quic_congestion_t             congestion;
     size_t                            received;
 
-    uint64_t                          cur_streams;
-    uint64_t                          max_streams;
-
     ngx_uint_t                        error;
     enum ssl_encryption_level_t       error_level;
     ngx_uint_t                        error_ftype;
@@ -233,7 +235,6 @@ static ngx_int_t ngx_quic_handle_stream_
 static ngx_int_t ngx_quic_stream_input(ngx_connection_t *c,
     ngx_quic_frame_t *frame, void *data);
 
-static ngx_int_t ngx_quic_handle_max_streams(ngx_connection_t *c);
 static ngx_int_t ngx_quic_handle_max_data_frame(ngx_connection_t *c,
     ngx_quic_max_data_frame_t *f);
 static ngx_int_t ngx_quic_handle_streams_blocked_frame(ngx_connection_t *c,
@@ -722,6 +723,9 @@ ngx_quic_new_connection(ngx_connection_t
 
     qc->streams.recv_max_data = qc->tp.initial_max_data;
 
+    qc->streams.client_max_streams_uni = qc->tp.initial_max_streams_uni;
+    qc->streams.client_max_streams_bidi = qc->tp.initial_max_streams_bidi;
+
     qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
                                     ngx_max(2 * qc->tp.max_udp_payload_size,
                                             14720));
@@ -1182,8 +1186,6 @@ ngx_quic_init_connection(ngx_connection_
     }
 #endif
 
-    qc->max_streams = qc->tp.initial_max_streams_bidi;
-
     return NGX_OK;
 }
 
@@ -2885,6 +2887,7 @@ ngx_quic_handle_stream_frame(ngx_connect
     ngx_quic_frame_t *frame)
 {
     size_t                     n;
+    uint64_t                   id;
     ngx_buf_t                 *b;
     ngx_event_t               *rev;
     ngx_quic_stream_t         *sn;
@@ -2913,9 +2916,35 @@ ngx_quic_handle_stream_frame(ngx_connect
             return NGX_ERROR;
         }
 
-        n = (f->stream_id & NGX_QUIC_STREAM_UNIDIRECTIONAL)
-            ? qc->tp.initial_max_stream_data_uni
-            : qc->tp.initial_max_stream_data_bidi_remote;
+        if (f->stream_id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {
+            if ((f->stream_id >> 2) < qc->streams.client_streams_uni) {
+                return NGX_OK;
+            }
+
+            if ((f->stream_id >> 2) >= qc->streams.client_max_streams_uni) {
+                qc->error = NGX_QUIC_ERR_STREAM_LIMIT_ERROR;
+                return NGX_ERROR;
+            }
+
+            id = (qc->streams.client_streams_uni << 2)
+                 | NGX_QUIC_STREAM_UNIDIRECTIONAL;
+            qc->streams.client_streams_uni = (f->stream_id >> 2) + 1;
+            n = qc->tp.initial_max_stream_data_uni;
+
+        } else {
+            if ((f->stream_id >> 2) < qc->streams.client_streams_bidi) {
+                return NGX_OK;
+            }
+
+            if ((f->stream_id >> 2) >= qc->streams.client_max_streams_bidi) {
+                qc->error = NGX_QUIC_ERR_STREAM_LIMIT_ERROR;
+                return NGX_ERROR;
+            }
+
+            id = (qc->streams.client_streams_bidi << 2);
+            qc->streams.client_streams_bidi = (f->stream_id >> 2) + 1;
+            n = qc->tp.initial_max_stream_data_bidi_remote;
+        }
 
         if (n < NGX_QUIC_STREAM_BUFSIZE) {
             n = NGX_QUIC_STREAM_BUFSIZE;
@@ -2928,8 +2957,6 @@ ngx_quic_handle_stream_frame(ngx_connect
         }
 
         /*
-         *   TODO: check IDs are increasing ? create all lower-numbered?
-         *
          *   2.1.  Stream Types and Identifiers
          *
          *   Within each type, streams are created with numerically increasing
@@ -2937,36 +2964,31 @@ ngx_quic_handle_stream_frame(ngx_connect
          *   streams of that type with lower-numbered stream IDs also being
          *   opened.
          */
-        sn = ngx_quic_create_stream(c, f->stream_id, n);
-        if (sn == NULL) {
-            return NGX_ERROR;
+
+        for ( /* void */ ; id <= f->stream_id; id += 0x04) {
+
+            sn = ngx_quic_create_stream(c, id, n);
+            if (sn == NULL) {
+                return NGX_ERROR;
+            }
+
+            if (id == f->stream_id && f->offset == 0) {
+                b = sn->b;
+                b->last = ngx_cpymem(b->last, f->data, f->length);
+
+                sn->fs.received += f->length;
+
+                rev = sn->c->read;
+                rev->ready = 1;
+
+                if (f->fin) {
+                    rev->pending_eof = 1;
+                }
+            }
+
+            sn->c->listening->handler(sn->c);
         }
 
-        rev = sn->c->read;
-
-        if (f->offset == 0) {
-
-            b = sn->b;
-            b->last = ngx_cpymem(b->last, f->data, f->length);
-
-            sn->fs.received += f->length;
-
-            rev->ready = 1;
-
-            if (f->fin) {
-                rev->pending_eof = 1;
-            }
-
-        } else {
-            rev->ready = 0;
-        }
-
-        if ((f->stream_id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) {
-            ngx_quic_handle_max_streams(c);
-        }
-
-        sn->c->listening->handler(sn->c);
-
         if (f->offset == 0) {
             return NGX_OK;
         }
@@ -3033,43 +3055,6 @@ ngx_quic_stream_input(ngx_connection_t *
 
 
 static ngx_int_t
-ngx_quic_handle_max_streams(ngx_connection_t *c)
-{
-    ngx_quic_frame_t       *frame;
-    ngx_quic_connection_t  *qc;
-
-    qc = c->quic;
-    qc->cur_streams++;
-
-    if (qc->cur_streams + NGX_QUIC_STREAMS_INC / 2 < qc->max_streams) {
-        return NGX_OK;
-    }
-
-    frame = ngx_quic_alloc_frame(c, 0);
-    if (frame == NULL) {
-        return NGX_ERROR;
-    }
-
-    qc->max_streams = ngx_max(qc->max_streams + NGX_QUIC_STREAMS_INC,
-                              NGX_QUIC_STREAMS_LIMIT);
-
-    frame->level = ssl_encryption_application;
-    frame->type = NGX_QUIC_FT_MAX_STREAMS;
-    frame->u.max_streams.limit = qc->max_streams;
-    frame->u.max_streams.bidi = 1;
-
-    ngx_sprintf(frame->info, "MAX_STREAMS limit:%d bidi:%d level=%d",
-                (int) frame->u.max_streams.limit,
-                (int) frame->u.max_streams.bidi,
-                frame->level);
-
-    ngx_quic_queue_frame(qc, frame);
-
-    return NGX_OK;
-}
-
-
-static ngx_int_t
 ngx_quic_handle_max_data_frame(ngx_connection_t *c,
     ngx_quic_max_data_frame_t *f)
 {
@@ -3112,27 +3097,6 @@ static ngx_int_t
 ngx_quic_handle_streams_blocked_frame(ngx_connection_t *c,
     ngx_quic_header_t *pkt, ngx_quic_streams_blocked_frame_t *f)
 {
-    ngx_quic_frame_t  *frame;
-
-    frame = ngx_quic_alloc_frame(c, 0);
-    if (frame == NULL) {
-        return NGX_ERROR;
-    }
-
-    frame->level = pkt->level;
-    frame->type = NGX_QUIC_FT_MAX_STREAMS;
-    frame->u.max_streams.limit = ngx_max(f->limit * 2, NGX_QUIC_STREAMS_LIMIT);
-    frame->u.max_streams.bidi = f->bidi;
-
-    c->quic->max_streams = frame->u.max_streams.limit;
-
-    ngx_sprintf(frame->info, "MAX_STREAMS limit:%d bidi:%d level=%d",
-                (int) frame->u.max_streams.limit,
-                (int) frame->u.max_streams.bidi,
-                frame->level);
-
-    ngx_quic_queue_frame(c->quic, frame);
-
     return NGX_OK;
 }
 
@@ -3921,6 +3885,9 @@ ngx_quic_create_stream(ngx_connection_t 
     ngx_pool_cleanup_t     *cln;
     ngx_quic_connection_t  *qc;
 
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic stream id 0x%uL create", id);
+
     qc = c->quic;
 
     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, c->log);
@@ -4257,9 +4224,35 @@ ngx_quic_stream_cleanup_handler(void *da
         return;
     }
 
-    if ((qs->id & 0x03) == NGX_QUIC_STREAM_UNIDIRECTIONAL) {
-        /* do not send fin for client unidirectional streams */
-        return;
+    if ((qs->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0) {
+        frame = ngx_quic_alloc_frame(pc, 0);
+        if (frame == NULL) {
+            return;
+        }
+
+        frame->level = ssl_encryption_application;
+        frame->type = NGX_QUIC_FT_MAX_STREAMS;
+
+        if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {
+            frame->u.max_streams.limit = ++qc->streams.client_max_streams_uni;
+            frame->u.max_streams.bidi = 0;
+
+        } else {
+            frame->u.max_streams.limit = ++qc->streams.client_max_streams_bidi;
+            frame->u.max_streams.bidi = 1;
+        }
+
+        ngx_sprintf(frame->info, "MAX_STREAMS limit:%uL bidi:%ui level=%d",
+                    frame->u.max_streams.limit,
+                    frame->u.max_streams.bidi,
+                    (int) frame->level);
+
+        ngx_quic_queue_frame(qc, frame);
+
+        if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {
+            /* do not send fin for client unidirectional streams */
+            return;
+        }
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,