Mercurial > hg > nginx
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 |