# HG changeset patch # User Roman Arutyunyan # Date 1606849861 0 # Node ID 2dfc5ef29973d8d3f491defd66c613b495fd12cb # Parent 43f3574b3e6f07a7f894f223598ef46c40b0d043 QUIC: introduced QUIC buffers. Buffers are used to hold frame data. They have a fixed size and are reused after being freed. diff --git a/README b/README --- a/README +++ b/README @@ -237,7 +237,7 @@ 5. Troubleshooting #define NGX_QUIC_DEBUG_PACKETS #define NGX_QUIC_DEBUG_FRAMES - #define NGX_QUIC_DEBUG_FRAMES_ALLOC + #define NGX_QUIC_DEBUG_ALLOC #define NGX_QUIC_DEBUG_CRYPTO 6. Contributing 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 @@ -143,7 +143,6 @@ typedef struct { ngx_event_t push; ngx_event_t pto; ngx_event_t close; - ngx_queue_t free_frames; ngx_msec_t last_cc; ngx_msec_t latest_rtt; @@ -153,8 +152,12 @@ typedef struct { ngx_uint_t pto_count; -#if (NGX_DEBUG) + ngx_queue_t free_frames; + ngx_chain_t *free_bufs; + +#ifdef NGX_QUIC_DEBUG_ALLOC ngx_uint_t nframes; + ngx_uint_t nbufs; #endif ngx_quic_streams_t streams; @@ -265,7 +268,7 @@ static ngx_int_t ngx_quic_send_cc(ngx_co static ngx_int_t ngx_quic_send_new_token(ngx_connection_t *c); static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c, - ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *f); + ngx_quic_header_t *pkt, ngx_quic_frame_t *f); static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max, ngx_msec_t *send_time); @@ -361,7 +364,7 @@ static ngx_chain_t *ngx_quic_stream_send static size_t ngx_quic_max_stream_frame(ngx_quic_connection_t *qc); static size_t ngx_quic_max_stream_flow(ngx_connection_t *c); static void ngx_quic_stream_cleanup_handler(void *data); -static ngx_quic_frame_t *ngx_quic_alloc_frame(ngx_connection_t *c, size_t size); +static ngx_quic_frame_t *ngx_quic_alloc_frame(ngx_connection_t *c); static void ngx_quic_free_frame(ngx_connection_t *c, ngx_quic_frame_t *frame); static void ngx_quic_congestion_ack(ngx_connection_t *c, @@ -369,6 +372,13 @@ static void ngx_quic_congestion_ack(ngx_ static void ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *frame); +static ngx_chain_t *ngx_quic_alloc_buf(ngx_connection_t *c); +static void ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in); +static ngx_chain_t *ngx_quic_copy_buf(ngx_connection_t *c, u_char *data, + size_t len); +static ngx_chain_t *ngx_quic_copy_chain(ngx_connection_t *c, ngx_chain_t *in, + size_t limit); + static SSL_QUIC_METHOD quic_method = { #if BORINGSSL_API_VERSION >= 10 @@ -414,8 +424,14 @@ ngx_quic_log_frame(ngx_log_t *log, ngx_q p = ngx_slprintf(p, last, "ACK n:%ui delay:%uL ", f->u.ack.range_count, f->u.ack.delay); - pos = f->u.ack.ranges_start; - end = f->u.ack.ranges_end; + if (f->data) { + pos = f->data->buf->pos; + end = f->data->buf->end; + + } else { + pos = NULL; + end = NULL; + } largest = f->u.ack.largest; smallest = f->u.ack.largest - f->u.ack.first_range; @@ -507,8 +523,16 @@ ngx_quic_log_frame(ngx_log_t *log, ngx_q } #ifdef NGX_QUIC_DEBUG_FRAMES - p = ngx_slprintf(p, last, " data len:%uL %*xs", f->u.stream.length, - (size_t) f->u.stream.length, f->u.stream.data); + { + ngx_chain_t *cl; + + p = ngx_slprintf(p, last, " data:"); + + for (cl = f->data; cl; cl = cl->next) { + p = ngx_slprintf(p, last, "%*xs", + cl->buf->last - cl->buf->pos, cl->buf->pos); + } + } #endif break; @@ -885,18 +909,20 @@ ngx_quic_add_handshake_data(ngx_ssl_conn fsize = ngx_min(limit, (size_t) (end - p)); - frame = ngx_quic_alloc_frame(c, fsize); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return 0; } - ngx_memcpy(frame->data, p, fsize); + frame->data = ngx_quic_copy_buf(c, p, fsize); + if (frame->data == NGX_CHAIN_ERROR) { + return 0; + } frame->level = level; frame->type = NGX_QUIC_FT_CRYPTO; frame->u.crypto.offset = fs->sent; frame->u.crypto.length = fsize; - frame->u.crypto.data = frame->data; fs->sent += fsize; p += fsize; @@ -1870,15 +1896,6 @@ ngx_quic_close_quic(ngx_connection_t *c, ngx_delete_posted_event(&qc->push); } - for (i = 0; i < NGX_QUIC_ENCRYPTION_LAST; i++) { - ngx_quic_free_frames(c, &qc->crypto[i].frames); - } - - for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { - ngx_quic_free_frames(c, &qc->send_ctx[i].frames); - ngx_quic_free_frames(c, &qc->send_ctx[i].sent); - } - while (!ngx_queue_empty(&qc->server_ids)) { q = ngx_queue_head(&qc->server_ids); sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); @@ -2438,7 +2455,9 @@ ngx_quic_payload_handler(ngx_connection_ { u_char *end, *p; ssize_t len; + ngx_buf_t buf; ngx_uint_t do_close; + ngx_chain_t chain; ngx_quic_frame_t frame; ngx_quic_connection_t *qc; @@ -2472,6 +2491,12 @@ ngx_quic_payload_handler(ngx_connection_ c->log->action = "parsing frames"; + ngx_memzero(&buf, sizeof(ngx_buf_t)); + + chain.buf = &buf; + chain.next = NULL; + frame.data = &chain; + len = ngx_quic_parse_frame(pkt, p, end, &frame); if (len < 0) { @@ -2488,7 +2513,7 @@ ngx_quic_payload_handler(ngx_connection_ switch (frame.type) { case NGX_QUIC_FT_ACK: - if (ngx_quic_handle_ack_frame(c, pkt, &frame.u.ack) != NGX_OK) { + if (ngx_quic_handle_ack_frame(c, pkt, &frame) != NGX_OK) { return NGX_ERROR; } @@ -2922,7 +2947,7 @@ ngx_quic_send_ack_range(ngx_connection_t { ngx_quic_frame_t *frame; - frame = ngx_quic_alloc_frame(c, 0); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return NGX_ERROR; } @@ -2999,10 +3024,11 @@ ngx_quic_drop_ack_ranges(ngx_connection_ static ngx_int_t ngx_quic_send_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx) { - u_char *p; - size_t ranges_len; + size_t len, left; uint64_t ack_delay; + ngx_buf_t *b; ngx_uint_t i; + ngx_chain_t *cl, **ll; ngx_quic_frame_t *frame; ngx_quic_connection_t *qc; @@ -3017,24 +3043,44 @@ ngx_quic_send_ack(ngx_connection_t *c, n ack_delay = 0; } - ranges_len = 0; + frame = ngx_quic_alloc_frame(c); + if (frame == NULL) { + return NGX_ERROR; + } + + ll = &frame->data; + b = NULL; for (i = 0; i < ctx->nranges; i++) { - ranges_len += ngx_quic_create_ack_range(NULL, ctx->ranges[i].gap, - ctx->ranges[i].range); - } - - frame = ngx_quic_alloc_frame(c, ranges_len); - if (frame == NULL) { - return NGX_ERROR; - } - - p = frame->data; - - for (i = 0; i < ctx->nranges; i++) { - p += ngx_quic_create_ack_range(p, ctx->ranges[i].gap, - ctx->ranges[i].range); - } + len = ngx_quic_create_ack_range(NULL, ctx->ranges[i].gap, + ctx->ranges[i].range); + + left = b ? b->end - b->last : 0; + + if (left < len) { + cl = ngx_quic_alloc_buf(c); + if (cl == NULL) { + return NGX_ERROR; + } + + *ll = cl; + ll = &cl->next; + + b = cl->buf; + left = b->end - b->last; + + if (left < len) { + return NGX_ERROR; + } + } + + b->last += ngx_quic_create_ack_range(b->last, ctx->ranges[i].gap, + ctx->ranges[i].range); + + frame->u.ack.ranges_length += len; + } + + *ll = NULL; frame->level = ctx->level; frame->type = NGX_QUIC_FT_ACK; @@ -3042,8 +3088,6 @@ ngx_quic_send_ack(ngx_connection_t *c, n frame->u.ack.delay = ack_delay; frame->u.ack.range_count = ctx->nranges; frame->u.ack.first_range = ctx->first_range; - frame->u.ack.ranges_start = frame->data; - frame->u.ack.ranges_end = frame->data + ranges_len; ngx_quic_queue_frame(qc, frame); @@ -3077,7 +3121,7 @@ ngx_quic_send_cc(ngx_connection_t *c) return NGX_OK; } - frame = ngx_quic_alloc_frame(c, 0); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return NGX_ERROR; } @@ -3118,7 +3162,7 @@ ngx_quic_send_new_token(ngx_connection_t return NGX_ERROR; } - frame = ngx_quic_alloc_frame(c, 0); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return NGX_ERROR; } @@ -3136,7 +3180,7 @@ ngx_quic_send_new_token(ngx_connection_t static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, - ngx_quic_ack_frame_t *ack) + ngx_quic_frame_t *f) { ssize_t n; u_char *pos, *end; @@ -3144,6 +3188,7 @@ ngx_quic_handle_ack_frame(ngx_connection ngx_msec_t send_time; ngx_uint_t i; ngx_quic_send_ctx_t *ctx; + ngx_quic_ack_frame_t *ack; ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); @@ -3153,6 +3198,8 @@ ngx_quic_handle_ack_frame(ngx_connection ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic ngx_quic_handle_ack_frame level:%d", pkt->level); + ack = &f->u.ack; + /* * If any computed packet number is negative, an endpoint MUST * generate a connection error of type FRAME_ENCODING_ERROR. @@ -3194,8 +3241,14 @@ ngx_quic_handle_ack_frame(ngx_connection } } - pos = ack->ranges_start; - end = ack->ranges_end; + if (f->data) { + pos = f->data->buf->pos; + end = f->data->buf->last; + + } else { + pos = NULL; + end = NULL; + } for (i = 0; i < ack->range_count; i++) { @@ -3537,7 +3590,9 @@ static ngx_int_t ngx_quic_adjust_frame_offset(ngx_connection_t *c, ngx_quic_frame_t *frame, uint64_t offset_in) { - size_t tail; + size_t tail, n; + ngx_buf_t *b; + ngx_chain_t *cl; ngx_quic_ordered_frame_t *f; f = &frame->u.ord; @@ -3558,9 +3613,21 @@ ngx_quic_adjust_frame_offset(ngx_connect /* intersecting range: adjust data size */ f->offset += tail; - f->data += tail; f->length -= tail; + for (cl = frame->data; cl; cl = cl->next) { + b = cl->buf; + n = ngx_buf_size(b); + + if (n >= tail) { + b->pos += tail; + break; + } + + cl->buf->pos = cl->buf->last; + tail -= n; + } + return NGX_OK; } @@ -3569,7 +3636,6 @@ static ngx_int_t ngx_quic_buffer_frame(ngx_connection_t *c, ngx_quic_frames_stream_t *fs, ngx_quic_frame_t *frame) { - u_char *data; ngx_queue_t *q; ngx_quic_frame_t *dst, *item; ngx_quic_ordered_frame_t *f, *df; @@ -3581,19 +3647,19 @@ ngx_quic_buffer_frame(ngx_connection_t * /* frame start offset is in the future, buffer it */ - dst = ngx_quic_alloc_frame(c, f->length); + dst = ngx_quic_alloc_frame(c); if (dst == NULL) { return NGX_ERROR; } - data = dst->data; ngx_memcpy(dst, frame, sizeof(ngx_quic_frame_t)); - dst->data = data; - - ngx_memcpy(dst->data, f->data, f->length); + + dst->data = ngx_quic_copy_chain(c, frame->data, 0); + if (dst->data == NGX_CHAIN_ERROR) { + return NGX_ERROR; + } df = &dst->u.ord; - df->data = dst->data; fs->total += f->length; @@ -3671,15 +3737,14 @@ ngx_quic_handle_crypto_frame(ngx_connect static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_quic_frame_t *frame, void *data) { - int n, sslerr; - ngx_ssl_conn_t *ssl_conn; - ngx_quic_connection_t *qc; - ngx_quic_crypto_frame_t *f; + int n, sslerr; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_ssl_conn_t *ssl_conn; + ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); - f = &frame->u.crypto; - ssl_conn = c->ssl->connection; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -3687,12 +3752,16 @@ ngx_quic_crypto_input(ngx_connection_t * (int) SSL_quic_read_level(ssl_conn), (int) SSL_quic_write_level(ssl_conn)); - if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn), - f->data, f->length)) - { - ngx_ssl_error(NGX_LOG_INFO, c->log, 0, - "SSL_provide_quic_data() failed"); - return NGX_ERROR; + for (cl = frame->data; cl; cl = cl->next) { + b = cl->buf; + + if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn), + b->pos, b->last - b->pos)) + { + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, + "SSL_provide_quic_data() failed"); + return NGX_ERROR; + } } n = SSL_do_handshake(ssl_conn); @@ -3730,7 +3799,7 @@ ngx_quic_crypto_input(ngx_connection_t * c->ssl->handshaked = 1; - frame = ngx_quic_alloc_frame(c, 0); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return NGX_ERROR; } @@ -3858,6 +3927,7 @@ ngx_quic_stream_input(ngx_connection_t * uint64_t id; ngx_buf_t *b; ngx_event_t *rev; + ngx_chain_t *cl; ngx_quic_stream_t *sn; ngx_quic_connection_t *qc; ngx_quic_stream_frame_t *f; @@ -3881,7 +3951,10 @@ ngx_quic_stream_input(ngx_connection_t * b->pos = b->start; } - b->last = ngx_cpymem(b->last, f->data, f->length); + for (cl = frame->data; cl; cl = cl->next) { + b->last = ngx_cpymem(b->last, cl->buf->pos, + cl->buf->last - cl->buf->pos); + } rev = sn->c->read; rev->ready = 1; @@ -3992,7 +4065,7 @@ ngx_quic_handle_stream_data_blocked_fram n = sn->fs.received + (b->pos - b->start) + (b->end - b->last); } - frame = ngx_quic_alloc_frame(c, 0); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return NGX_ERROR; } @@ -4215,7 +4288,7 @@ ngx_quic_handle_path_challenge_frame(ngx qc = ngx_quic_get_connection(c); - frame = ngx_quic_alloc_frame(c, 0); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return NGX_ERROR; } @@ -4381,7 +4454,7 @@ ngx_quic_retire_connection_id(ngx_connec qc = ngx_quic_get_connection(c); - frame = ngx_quic_alloc_frame(c, 0); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return NGX_ERROR; } @@ -4455,7 +4528,7 @@ ngx_quic_issue_server_ids(ngx_connection return NGX_ERROR; } - frame = ngx_quic_alloc_frame(c, 0); + frame = ngx_quic_alloc_frame(c); if (frame == NULL) { return NGX_ERROR; } @@ -5634,7 +5707,7 @@ ngx_quic_stream_recv(ngx_connection_t *c qs->id, len, size); if (!rev->pending_eof) { - frame = ngx_quic_alloc_frame(pc, 0); + frame = ngx_quic_alloc_frame(pc); if (frame == NULL) { return NGX_ERROR; } @@ -5650,7 +5723,7 @@ ngx_quic_stream_recv(ngx_connection_t *c if ((qc->streams.recv_max_data / 2) < qc->streams.received) { - frame = ngx_quic_alloc_frame(pc, 0); + frame = ngx_quic_alloc_frame(pc); if (frame == NULL) { return NGX_ERROR; @@ -5703,13 +5776,9 @@ ngx_quic_stream_send(ngx_connection_t *c static ngx_chain_t * ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { - u_char *p; - size_t n, max, max_frame, max_flow, max_limit, len; + size_t n, max, max_frame, max_flow, max_limit; #if (NGX_DEBUG) size_t sent; -#endif - ngx_buf_t *b; -#if (NGX_DEBUG) ngx_uint_t nframes; #endif ngx_event_t *wev; @@ -5763,7 +5832,7 @@ ngx_quic_stream_send_chain(ngx_connectio break; } - frame = ngx_quic_alloc_frame(pc, n); + frame = ngx_quic_alloc_frame(pc); if (frame == NULL) { return NGX_CHAIN_ERROR; } @@ -5778,7 +5847,6 @@ ngx_quic_stream_send_chain(ngx_connectio frame->u.stream.stream_id = qs->id; frame->u.stream.offset = c->sent; frame->u.stream.length = n; - frame->u.stream.data = frame->data; c->sent += n; qc->streams.sent += n; @@ -5793,18 +5861,9 @@ ngx_quic_stream_send_chain(ngx_connectio nframes++; #endif - for (p = frame->data; n > 0; cl = cl->next) { - b = cl->buf; - - if (!ngx_buf_in_memory(b)) { - continue; - } - - len = ngx_min(n, (size_t) (b->last - b->pos)); - p = ngx_cpymem(p, b->pos, len); - - b->pos += len; - n -= len; + frame->data = ngx_quic_copy_chain(pc, cl, n); + if (frame->data == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; } ngx_quic_queue_frame(qc, frame); @@ -5916,7 +5975,7 @@ ngx_quic_stream_cleanup_handler(void *da || (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { if (!c->read->pending_eof && !c->read->error) { - frame = ngx_quic_alloc_frame(pc, 0); + frame = ngx_quic_alloc_frame(pc); if (frame == NULL) { return; } @@ -5931,7 +5990,7 @@ ngx_quic_stream_cleanup_handler(void *da } if ((qs->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0) { - frame = ngx_quic_alloc_frame(pc, 0); + frame = ngx_quic_alloc_frame(pc); if (frame == NULL) { return; } @@ -5959,7 +6018,7 @@ ngx_quic_stream_cleanup_handler(void *da ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic stream id:0x%xL send fin", qs->id); - frame = ngx_quic_alloc_frame(pc, 0); + frame = ngx_quic_alloc_frame(pc); if (frame == NULL) { return; } @@ -5974,7 +6033,6 @@ ngx_quic_stream_cleanup_handler(void *da frame->u.stream.stream_id = qs->id; frame->u.stream.offset = c->sent; frame->u.stream.length = 0; - frame->u.stream.data = NULL; ngx_quic_queue_frame(qc, frame); @@ -5983,23 +6041,12 @@ ngx_quic_stream_cleanup_handler(void *da static ngx_quic_frame_t * -ngx_quic_alloc_frame(ngx_connection_t *c, size_t size) +ngx_quic_alloc_frame(ngx_connection_t *c) { - u_char *p; ngx_queue_t *q; ngx_quic_frame_t *frame; ngx_quic_connection_t *qc; - if (size) { - p = ngx_alloc(size, c->log); - if (p == NULL) { - return NULL; - } - - } else { - p = NULL; - } - qc = ngx_quic_get_connection(c); if (!ngx_queue_empty(&qc->free_frames)) { @@ -6009,7 +6056,7 @@ ngx_quic_alloc_frame(ngx_connection_t *c ngx_queue_remove(&frame->queue); -#ifdef NGX_QUIC_DEBUG_FRAMES_ALLOC +#ifdef NGX_QUIC_DEBUG_ALLOC ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic reuse frame n:%ui", qc->nframes); #endif @@ -6017,15 +6064,12 @@ ngx_quic_alloc_frame(ngx_connection_t *c } else { frame = ngx_pcalloc(c->pool, sizeof(ngx_quic_frame_t)); if (frame == NULL) { - ngx_free(p); return NULL; } -#if (NGX_DEBUG) +#ifdef NGX_QUIC_DEBUG_ALLOC ++qc->nframes; -#endif - -#ifdef NGX_QUIC_DEBUG_FRAMES_ALLOC + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic alloc frame n:%ui", qc->nframes); #endif @@ -6033,8 +6077,6 @@ ngx_quic_alloc_frame(ngx_connection_t *c ngx_memzero(frame, sizeof(ngx_quic_frame_t)); - frame->data = p; - return frame; } @@ -6140,13 +6182,12 @@ ngx_quic_free_frame(ngx_connection_t *c, qc = ngx_quic_get_connection(c); if (frame->data) { - ngx_free(frame->data); - frame->data = NULL; + ngx_quic_free_bufs(c, frame->data); } ngx_queue_insert_head(&qc->free_frames, &frame->queue); -#ifdef NGX_QUIC_DEBUG_FRAMES_ALLOC +#ifdef NGX_QUIC_DEBUG_ALLOC ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic free frame n:%ui", qc->nframes); #endif @@ -6165,3 +6206,170 @@ ngx_quic_version(ngx_connection_t *c) return (version & 0xff000000) == 0xff000000 ? version & 0xff : version; } + + +static ngx_chain_t * +ngx_quic_alloc_buf(ngx_connection_t *c) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + + if (qc->free_bufs) { + cl = qc->free_bufs; + qc->free_bufs = cl->next; + + b = cl->buf; + b->pos = b->start; + b->last = b->start; + +#ifdef NGX_QUIC_DEBUG_ALLOC + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic reuse buffer n:%ui", qc->nbufs); +#endif + + return cl; + } + + cl = ngx_alloc_chain_link(c->pool); + if (cl == NULL) { + return NULL; + } + + b = ngx_create_temp_buf(c->pool, NGX_QUIC_BUFFER_SIZE); + if (b == NULL) { + return NULL; + } + + b->tag = (ngx_buf_tag_t) &ngx_quic_alloc_buf; + + cl->buf = b; + +#ifdef NGX_QUIC_DEBUG_ALLOC + ++qc->nbufs; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic alloc buffer n:%ui", qc->nbufs); +#endif + + return cl; +} + + +static void +ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in) +{ + ngx_chain_t *cl; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + + while (in) { +#ifdef NGX_QUIC_DEBUG_ALLOC + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic free buffer n:%ui", qc->nbufs); +#endif + + cl = in; + in = in->next; + + cl->next = qc->free_bufs; + qc->free_bufs = cl; + } +} + + +static ngx_chain_t * +ngx_quic_copy_buf(ngx_connection_t *c, u_char *data, size_t len) +{ + size_t n; + ngx_buf_t *b; + ngx_chain_t *cl, *out, **ll; + + out = NULL; + ll = &out; + + while (len) { + cl = ngx_quic_alloc_buf(c); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + b = cl->buf; + n = ngx_min((size_t) (b->end - b->last), len); + + b->last = ngx_cpymem(b->last, data, n); + + data += n; + len -= n; + + *ll = cl; + ll = &cl->next; + } + + *ll = NULL; + + return out; +} + + +static ngx_chain_t * +ngx_quic_copy_chain(ngx_connection_t *c, ngx_chain_t *in, size_t limit) +{ + size_t n; + ngx_buf_t *b; + ngx_chain_t *cl, *out, **ll; + + out = NULL; + ll = &out; + + while (in) { + if (!ngx_buf_in_memory(in->buf) || ngx_buf_size(in->buf) == 0) { + in = in->next; + continue; + } + + cl = ngx_quic_alloc_buf(c); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + *ll = cl; + ll = &cl->next; + + b = cl->buf; + + while (in && b->last != b->end) { + + n = ngx_min(in->buf->last - in->buf->pos, b->end - b->last); + + if (limit > 0 && n > limit) { + n = limit; + } + + b->last = ngx_cpymem(b->last, in->buf->pos, n); + + in->buf->pos += n; + if (in->buf->pos == in->buf->last) { + in = in->next; + } + + if (limit > 0) { + if (limit == n) { + goto done; + } + + limit -= n; + } + } + + } + +done: + + *ll = NULL; + + return out; +} diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h --- a/src/event/ngx_event_quic.h +++ b/src/event/ngx_event_quic.h @@ -61,6 +61,8 @@ #define NGX_QUIC_MAX_SERVER_IDS 8 +#define NGX_QUIC_BUFFER_SIZE 4096 + #define ngx_quic_get_connection(c) ((ngx_quic_connection_t *)(c)->udp) @@ -135,7 +137,7 @@ ngx_int_t ngx_quic_get_packet_dcid(ngx_l /* #define NGX_QUIC_DEBUG_PACKETS */ /* dump packet contents */ /* #define NGX_QUIC_DEBUG_FRAMES */ /* dump frames contents */ -/* #define NGX_QUIC_DEBUG_FRAMES_ALLOC */ /* log frames alloc/reuse/free */ +/* #define NGX_QUIC_DEBUG_ALLOC */ /* log frames and bufs alloc */ /* #define NGX_QUIC_DEBUG_CRYPTO */ #endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */ diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c --- a/src/event/ngx_event_quic_transport.c +++ b/src/event/ngx_event_quic_transport.c @@ -87,15 +87,17 @@ static size_t ngx_quic_create_short_head static ngx_int_t ngx_quic_frame_allowed(ngx_quic_header_t *pkt, ngx_uint_t frame_type); -static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack); +static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack, + ngx_chain_t *ranges); static size_t ngx_quic_create_stop_sending(u_char *p, ngx_quic_stop_sending_frame_t *ss); static size_t ngx_quic_create_crypto(u_char *p, - ngx_quic_crypto_frame_t *crypto); + ngx_quic_crypto_frame_t *crypto, ngx_chain_t *data); static size_t ngx_quic_create_hs_done(u_char *p); static size_t ngx_quic_create_new_token(u_char *p, ngx_quic_new_token_frame_t *token); -static size_t ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf); +static size_t ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf, + ngx_chain_t *data); static size_t ngx_quic_create_max_streams(u_char *p, ngx_quic_max_streams_frame_t *ms); static size_t ngx_quic_create_max_stream_data(u_char *p, @@ -703,8 +705,11 @@ ngx_quic_parse_frame(ngx_quic_header_t * { u_char *p; uint64_t varint; + ngx_buf_t *b; ngx_uint_t i; + b = f->data->buf; + p = start; p = ngx_quic_parse_int(p, end, &varint); @@ -736,11 +741,13 @@ ngx_quic_parse_frame(ngx_quic_header_t * goto error; } - p = ngx_quic_read_bytes(p, end, f->u.crypto.length, &f->u.crypto.data); + p = ngx_quic_read_bytes(p, end, f->u.crypto.length, &b->pos); if (p == NULL) { goto error; } + b->last = p; + break; case NGX_QUIC_FT_PADDING: @@ -762,7 +769,7 @@ ngx_quic_parse_frame(ngx_quic_header_t * goto error; } - f->u.ack.ranges_start = p; + b->pos = p; /* process all ranges to get bounds, values are ignored */ for (i = 0; i < f->u.ack.range_count; i++) { @@ -777,7 +784,9 @@ ngx_quic_parse_frame(ngx_quic_header_t * } } - f->u.ack.ranges_end = p; + b->last = p; + + f->u.ack.ranges_length = b->last - b->pos; if (f->type == NGX_QUIC_FT_ACK_ECN) { @@ -914,12 +923,12 @@ ngx_quic_parse_frame(ngx_quic_header_t * f->u.stream.length = end - p; /* up to packet end */ } - p = ngx_quic_read_bytes(p, end, f->u.stream.length, - &f->u.stream.data); + p = ngx_quic_read_bytes(p, end, f->u.stream.length, &b->pos); if (p == NULL) { goto error; } + b->last = p; break; case NGX_QUIC_FT_MAX_DATA: @@ -1192,13 +1201,13 @@ ngx_quic_create_frame(u_char *p, ngx_qui switch (f->type) { case NGX_QUIC_FT_ACK: f->need_ack = 0; - return ngx_quic_create_ack(p, &f->u.ack); + return ngx_quic_create_ack(p, &f->u.ack, f->data); case NGX_QUIC_FT_STOP_SENDING: return ngx_quic_create_stop_sending(p, &f->u.stop_sending); case NGX_QUIC_FT_CRYPTO: - return ngx_quic_create_crypto(p, &f->u.crypto); + return ngx_quic_create_crypto(p, &f->u.crypto, f->data); case NGX_QUIC_FT_HANDSHAKE_DONE: return ngx_quic_create_hs_done(p); @@ -1214,7 +1223,7 @@ ngx_quic_create_frame(u_char *p, ngx_qui case NGX_QUIC_FT_STREAM5: case NGX_QUIC_FT_STREAM6: case NGX_QUIC_FT_STREAM7: - return ngx_quic_create_stream(p, &f->u.stream); + return ngx_quic_create_stream(p, &f->u.stream, f->data); case NGX_QUIC_FT_CONNECTION_CLOSE: case NGX_QUIC_FT_CONNECTION_CLOSE_APP: @@ -1247,10 +1256,11 @@ ngx_quic_create_frame(u_char *p, ngx_qui static size_t -ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack) +ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack, ngx_chain_t *ranges) { - size_t len; - u_char *start; + size_t len; + u_char *start; + ngx_buf_t *b; if (p == NULL) { len = ngx_quic_varint_len(NGX_QUIC_FT_ACK); @@ -1258,7 +1268,7 @@ ngx_quic_create_ack(u_char *p, ngx_quic_ len += ngx_quic_varint_len(ack->delay); len += ngx_quic_varint_len(ack->range_count); len += ngx_quic_varint_len(ack->first_range); - len += ack->ranges_end - ack->ranges_start; + len += ack->ranges_length; return len; } @@ -1270,7 +1280,12 @@ ngx_quic_create_ack(u_char *p, ngx_quic_ ngx_quic_build_int(&p, ack->delay); ngx_quic_build_int(&p, ack->range_count); ngx_quic_build_int(&p, ack->first_range); - p = ngx_cpymem(p, ack->ranges_start, ack->ranges_end - ack->ranges_start); + + while (ranges) { + b = ranges->buf; + p = ngx_cpymem(p, b->pos, b->last - b->pos); + ranges = ranges->next; + } return p - start; } @@ -1300,10 +1315,12 @@ ngx_quic_create_stop_sending(u_char *p, static size_t -ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto) +ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto, + ngx_chain_t *data) { - size_t len; - u_char *start; + size_t len; + u_char *start; + ngx_buf_t *b; if (p == NULL) { len = ngx_quic_varint_len(NGX_QUIC_FT_CRYPTO); @@ -1319,7 +1336,12 @@ ngx_quic_create_crypto(u_char *p, ngx_qu ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO); ngx_quic_build_int(&p, crypto->offset); ngx_quic_build_int(&p, crypto->length); - p = ngx_cpymem(p, crypto->data, crypto->length); + + while (data) { + b = data->buf; + p = ngx_cpymem(p, b->pos, b->last - b->pos); + data = data->next; + } return p - start; } @@ -1367,10 +1389,12 @@ ngx_quic_create_new_token(u_char *p, ngx static size_t -ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf) +ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf, + ngx_chain_t *data) { - size_t len; - u_char *start; + size_t len; + u_char *start; + ngx_buf_t *b; if (p == NULL) { len = ngx_quic_varint_len(sf->type); @@ -1401,7 +1425,11 @@ ngx_quic_create_stream(u_char *p, ngx_qu /* length is always present in generated frames */ ngx_quic_build_int(&p, sf->length); - p = ngx_cpymem(p, sf->data, sf->length); + while (data) { + b = data->buf; + p = ngx_cpymem(p, b->pos, b->last - b->pos); + data = data->next; + } return p - start; } diff --git a/src/event/ngx_event_quic_transport.h b/src/event/ngx_event_quic_transport.h --- a/src/event/ngx_event_quic_transport.h +++ b/src/event/ngx_event_quic_transport.h @@ -144,8 +144,7 @@ typedef struct { uint64_t ect0; uint64_t ect1; uint64_t ce; - u_char *ranges_start; - u_char *ranges_end; + uint64_t ranges_length; } ngx_quic_ack_frame_t; @@ -171,7 +170,6 @@ typedef struct { typedef struct { uint64_t offset; uint64_t length; - u_char *data; } ngx_quic_ordered_frame_t; typedef ngx_quic_ordered_frame_t ngx_quic_crypto_frame_t; @@ -181,7 +179,6 @@ typedef struct { /* initial fields same as in ngx_quic_ordered_frame_t */ uint64_t offset; uint64_t length; - u_char *data; uint8_t type; uint64_t stream_id; @@ -270,7 +267,7 @@ struct ngx_quic_frame_s { ngx_uint_t need_ack; /* unsigned need_ack:1; */ - u_char *data; + ngx_chain_t *data; union { ngx_quic_ack_frame_t ack; ngx_quic_crypto_frame_t crypto;