# HG changeset patch # User Roman Arutyunyan # Date 1595866517 -10800 # Node ID c5324bb3a7044a40ffa918f90791d05eac07caf3 # Parent 455a8536eaa7ce22f2dae82b1d216a473960e23e 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 diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- 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,