comparison src/event/quic/ngx_event_quic_frames.c @ 8782:b3f6ad181df4 quic

QUIC: refactored CRYPTO and STREAM buffer ordering. Generic function ngx_quic_order_bufs() is introduced. This function creates and maintains a chain of buffers with holes. Holes are marked with b->sync flag. Several buffers and holes in this chain may share the same underlying memory buffer. When processing STREAM frames with this function, frame data is copied only once to the right place in the stream input chain. Previously data could be copied twice. First when buffering an out-of-order frame data, and then when filling stream buffer from ordered frame queue. Now there's only one data chain for both tasks.
author Roman Arutyunyan <arut@nginx.com>
date Tue, 25 May 2021 13:55:12 +0300
parents f52a2b77d406
children 6c213bec71c2
comparison
equal deleted inserted replaced
8781:81d491f0dc8c 8782:b3f6ad181df4
14 14
15 15
16 static ngx_chain_t *ngx_quic_split_bufs(ngx_connection_t *c, ngx_chain_t *in, 16 static ngx_chain_t *ngx_quic_split_bufs(ngx_connection_t *c, ngx_chain_t *in,
17 size_t len); 17 size_t len);
18 18
19 static ngx_int_t ngx_quic_buffer_frame(ngx_connection_t *c,
20 ngx_quic_frames_stream_t *stream, ngx_quic_frame_t *f);
21 static ngx_int_t ngx_quic_adjust_frame_offset(ngx_connection_t *c,
22 ngx_quic_frame_t *f, uint64_t offset_in);
23
24 19
25 ngx_quic_frame_t * 20 ngx_quic_frame_t *
26 ngx_quic_alloc_frame(ngx_connection_t *c) 21 ngx_quic_alloc_frame(ngx_connection_t *c)
27 { 22 {
28 ngx_queue_t *q; 23 ngx_queue_t *q;
78 73
79 #ifdef NGX_QUIC_DEBUG_ALLOC 74 #ifdef NGX_QUIC_DEBUG_ALLOC
80 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, 75 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
81 "quic free frame n:%ui", qc->nframes); 76 "quic free frame n:%ui", qc->nframes);
82 #endif 77 #endif
78 }
79
80
81 void
82 ngx_quic_trim_bufs(ngx_chain_t *in, size_t size)
83 {
84 size_t n;
85 ngx_buf_t *b;
86
87 while (in && size > 0) {
88 b = in->buf;
89 n = ngx_min((size_t) (b->last - b->pos), size);
90
91 b->pos += n;
92 size -= n;
93
94 if (b->pos == b->last) {
95 in = in->next;
96 }
97 }
83 } 98 }
84 99
85 100
86 void 101 void
87 ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in) 102 ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in)
467 return out; 482 return out;
468 } 483 }
469 484
470 485
471 ngx_int_t 486 ngx_int_t
472 ngx_quic_handle_ordered_frame(ngx_connection_t *c, ngx_quic_frames_stream_t *fs, 487 ngx_quic_order_bufs(ngx_connection_t *c, ngx_chain_t **out, ngx_chain_t *in,
473 ngx_quic_frame_t *frame, ngx_quic_frame_handler_pt handler, void *data) 488 size_t offset)
474 { 489 {
475 size_t full_len; 490 u_char *p;
476 ngx_int_t rc; 491 size_t n;
477 ngx_queue_t *q; 492 ngx_buf_t *b;
478 ngx_quic_ordered_frame_t *f; 493 ngx_chain_t *cl, *sl;
479 494
480 f = &frame->u.ord; 495 while (in) {
481 496 cl = *out;
482 if (f->offset > fs->received) { 497
483 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, 498 if (cl == NULL) {
484 "quic out-of-order frame: expecting:%uL got:%uL", 499 cl = ngx_quic_alloc_buf(c);
485 fs->received, f->offset); 500 if (cl == NULL) {
486 501 return NGX_ERROR;
487 return ngx_quic_buffer_frame(c, fs, frame); 502 }
488 } 503
489 504 cl->buf->last = cl->buf->end;
490 if (f->offset < fs->received) { 505 cl->buf->sync = 1; /* hole */
491 506 cl->next = NULL;
492 if (ngx_quic_adjust_frame_offset(c, frame, fs->received) 507 *out = cl;
493 == NGX_DONE) 508 }
494 { 509
495 /* old/duplicate data range */
496 return handler == ngx_quic_crypto_input ? NGX_DECLINED : NGX_OK;
497 }
498
499 /* intersecting data range, frame modified */
500 }
501
502 /* f->offset == fs->received */
503
504 rc = handler(c, frame, data);
505 if (rc == NGX_ERROR) {
506 return NGX_ERROR;
507
508 } else if (rc == NGX_DONE) {
509 /* handler destroyed stream, queue no longer exists */
510 return NGX_OK;
511 }
512
513 /* rc == NGX_OK */
514
515 fs->received += f->length;
516
517 /* now check the queue if we can continue with buffered frames */
518
519 do {
520 q = ngx_queue_head(&fs->frames);
521 if (q == ngx_queue_sentinel(&fs->frames)) {
522 break;
523 }
524
525 frame = ngx_queue_data(q, ngx_quic_frame_t, queue);
526 f = &frame->u.ord;
527
528 if (f->offset > fs->received) {
529 /* gap found, nothing more to do */
530 break;
531 }
532
533 full_len = f->length;
534
535 if (f->offset < fs->received) {
536
537 if (ngx_quic_adjust_frame_offset(c, frame, fs->received)
538 == NGX_DONE)
539 {
540 /* old/duplicate data range */
541 ngx_queue_remove(q);
542 fs->total -= f->length;
543
544 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
545 "quic skipped buffered frame, total:%ui",
546 fs->total);
547 ngx_quic_free_frame(c, frame);
548 continue;
549 }
550
551 /* frame was adjusted, proceed to input */
552 }
553
554 /* f->offset == fs->received */
555
556 rc = handler(c, frame, data);
557
558 if (rc == NGX_ERROR) {
559 return NGX_ERROR;
560
561 } else if (rc == NGX_DONE) {
562 /* handler destroyed stream, queue no longer exists */
563 return NGX_OK;
564 }
565
566 fs->received += f->length;
567 fs->total -= full_len;
568
569 ngx_queue_remove(q);
570
571 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
572 "quic consumed buffered frame, total:%ui", fs->total);
573
574 ngx_quic_free_frame(c, frame);
575
576 } while (1);
577
578 return NGX_OK;
579 }
580
581
582 static ngx_int_t
583 ngx_quic_adjust_frame_offset(ngx_connection_t *c, ngx_quic_frame_t *frame,
584 uint64_t offset_in)
585 {
586 size_t tail, n;
587 ngx_buf_t *b;
588 ngx_chain_t *cl;
589 ngx_quic_ordered_frame_t *f;
590
591 f = &frame->u.ord;
592
593 tail = offset_in - f->offset;
594
595 if (tail >= f->length) {
596 /* range preceeding already received data or duplicate, ignore */
597
598 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
599 "quic old or duplicate data in ordered frame, ignored");
600 return NGX_DONE;
601 }
602
603 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
604 "quic adjusted ordered frame data start to expected offset");
605
606 /* intersecting range: adjust data size */
607
608 f->offset += tail;
609 f->length -= tail;
610
611 for (cl = frame->data; cl; cl = cl->next) {
612 b = cl->buf; 510 b = cl->buf;
613 n = ngx_buf_size(b); 511 n = b->last - b->pos;
614 512
615 if (n >= tail) { 513 if (n <= offset) {
616 b->pos += tail; 514 offset -= n;
617 break; 515 out = &cl->next;
618 } 516 continue;
619 517 }
620 cl->buf->pos = cl->buf->last; 518
621 tail -= n; 519 if (b->sync && offset > 0) {
622 } 520 sl = ngx_quic_split_bufs(c, cl, offset);
623 521 if (sl == NGX_CHAIN_ERROR) {
624 return NGX_OK; 522 return NGX_ERROR;
625 } 523 }
626 524
627 525 cl->next = sl;
628 static ngx_int_t 526 continue;
629 ngx_quic_buffer_frame(ngx_connection_t *c, ngx_quic_frames_stream_t *fs, 527 }
630 ngx_quic_frame_t *frame) 528
631 { 529 for (p = b->pos + offset; p != b->last && in; /* void */ ) {
632 ngx_queue_t *q; 530 n = ngx_min(b->last - p, in->buf->last - in->buf->pos);
633 ngx_quic_frame_t *dst, *item; 531
634 ngx_quic_ordered_frame_t *f, *df; 532 if (b->sync) {
635 533 ngx_memcpy(p, in->buf->pos, n);
636 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, 534 }
637 "quic ngx_quic_buffer_frame"); 535
638 536 p += n;
639 f = &frame->u.ord; 537 in->buf->pos += n;
640 538 offset += n;
641 /* frame start offset is in the future, buffer it */ 539
642 540 if (in->buf->pos == in->buf->last) {
643 dst = ngx_quic_alloc_frame(c); 541 in = in->next;
644 if (dst == NULL) { 542 }
645 return NGX_ERROR; 543 }
646 } 544
647 545 if (b->sync && p != b->pos) {
648 ngx_memcpy(dst, frame, sizeof(ngx_quic_frame_t)); 546 sl = ngx_quic_split_bufs(c, cl, p - b->pos);
649 547 if (sl == NGX_CHAIN_ERROR) {
650 dst->data = ngx_quic_copy_chain(c, frame->data, 0); 548 return NGX_ERROR;
651 if (dst->data == NGX_CHAIN_ERROR) { 549 }
652 return NGX_ERROR; 550
653 } 551 cl->next = sl;
654 552 cl->buf->sync = 0;
655 df = &dst->u.ord; 553 }
656 554 }
657 fs->total += f->length;
658
659 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
660 "quic ordered frame with unexpected offset:"
661 " buffered total:%ui", fs->total);
662
663 if (ngx_queue_empty(&fs->frames)) {
664 ngx_queue_insert_after(&fs->frames, &dst->queue);
665 return NGX_OK;
666 }
667
668 for (q = ngx_queue_last(&fs->frames);
669 q != ngx_queue_sentinel(&fs->frames);
670 q = ngx_queue_prev(q))
671 {
672 item = ngx_queue_data(q, ngx_quic_frame_t, queue);
673 f = &item->u.ord;
674
675 if (f->offset < df->offset) {
676 ngx_queue_insert_after(q, &dst->queue);
677 return NGX_OK;
678 }
679 }
680
681 ngx_queue_insert_after(&fs->frames, &dst->queue);
682 555
683 return NGX_OK; 556 return NGX_OK;
684 } 557 }
685 558
686 559