@@ -319,14 +319,20 @@ class Http3ApplicationImpl final : public Session::Application { |
319 | 319 |
|
320 | 320 | void CollectSessionTicketAppData( |
321 | 321 | SessionTicket::AppData* app_data) const override { |
322 | | - // TODO(@jasnell): There's currently nothing to store but there may be |
323 | | - // later. |
| 322 | + // TODO(@jasnell): When HTTP/3 settings become dynamic or |
| 323 | + // configurable per-connection, store them here so they can be |
| 324 | + // validated on 0-RTT resumption. Candidates include: |
| 325 | + // max_field_section_size, qpack_max_dtable_capacity, |
| 326 | + // qpack_encoder_max_dtable_capacity, qpack_blocked_streams, |
| 327 | + // enable_connect_protocol, and enable_datagrams. On extraction, |
| 328 | + // compare stored values against current settings and return |
| 329 | + // TICKET_IGNORE_RENEW if incompatible. |
324 | 330 | } |
325 | 331 |
|
326 | 332 | SessionTicket::AppData::Status ExtractSessionTicketAppData( |
327 | 333 | const SessionTicket::AppData& app_data, |
328 | 334 | SessionTicket::AppData::Source::Flag flag) override { |
329 | | - // There's currently nothing stored here but we might do so later. |
| 335 | + // See CollectSessionTicketAppData above. |
330 | 336 | return flag == SessionTicket::AppData::Source::Flag::STATUS_RENEW |
331 | 337 | ? SessionTicket::AppData::Status::TICKET_USE_RENEW |
332 | 338 | : SessionTicket::AppData::Status::TICKET_USE; |
@@ -448,10 +454,42 @@ class Http3ApplicationImpl final : public Session::Application { |
448 | 454 | return false; |
449 | 455 | } |
450 | 456 |
|
| 457 | + void SetStreamPriority(const Stream& stream, |
| 458 | + StreamPriority priority, |
| 459 | + StreamPriorityFlags flags) override { |
| 460 | + nghttp3_pri pri; |
| 461 | + pri.inc = (flags == StreamPriorityFlags::NON_INCREMENTAL) ? 0 : 1; |
| 462 | + switch (priority) { |
| 463 | + case StreamPriority::HIGH: |
| 464 | + pri.urgency = NGHTTP3_URGENCY_HIGH; |
| 465 | + break; |
| 466 | + case StreamPriority::LOW: |
| 467 | + pri.urgency = NGHTTP3_URGENCY_LOW; |
| 468 | + break; |
| 469 | + default: |
| 470 | + pri.urgency = NGHTTP3_DEFAULT_URGENCY; |
| 471 | + break; |
| 472 | + } |
| 473 | + if (session().is_server()) { |
| 474 | + nghttp3_conn_set_server_stream_priority( |
| 475 | + *this, stream.id(), &pri); |
| 476 | + } |
| 477 | + // Client-side priority is set at request submission time via |
| 478 | + // nghttp3_conn_submit_request and is not typically changed |
| 479 | + // after the fact. The client API takes a serialized RFC 9218 |
| 480 | + // field value rather than an nghttp3_pri struct. |
| 481 | + } |
| 482 | + |
451 | 483 | StreamPriority GetStreamPriority(const Stream& stream) override { |
452 | 484 | nghttp3_pri pri; |
453 | 485 | if (nghttp3_conn_get_stream_priority(*this, &pri, stream.id()) == 0) { |
454 | | - // TODO(@jasnell): Support the incremental flag |
| 486 | + // TODO(@jasnell): The nghttp3_pri.inc (incremental) flag is |
| 487 | + // not yet exposed. When priority-based stream scheduling is |
| 488 | + // implemented, GetStreamPriority should return both urgency |
| 489 | + // and the incremental flag (making get/set symmetrical). |
| 490 | + // The inc flag determines whether the server should interleave |
| 491 | + // data from this stream with others of the same urgency |
| 492 | + // (inc=1) or complete it first (inc=0). |
455 | 493 | switch (pri.urgency) { |
456 | 494 | case NGHTTP3_URGENCY_HIGH: |
457 | 495 | return StreamPriority::HIGH; |
@@ -673,22 +711,23 @@ class Http3ApplicationImpl final : public Session::Application { |
673 | 711 | stream->ReceiveStreamReset(0, QuicError::ForApplication(app_error_code)); |
674 | 712 | } |
675 | 713 |
|
676 | | - void OnShutdown() { |
677 | | - // This callback is invoked when we receive a request to gracefully shutdown |
678 | | - // the http3 connection. For client, the id is the stream id of a client |
679 | | - // initiated stream. For server, the id is the stream id of a server |
680 | | - // initiated stream. Once received, the other side is guaranteed not to |
681 | | - // process any more data. |
682 | | - |
683 | | - // On the client side, if id is equal to NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID, |
684 | | - // or on the server if the id is equal to NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID, |
685 | | - // then this is a request to begin a graceful shutdown. |
686 | | - |
687 | | - // This can be called multiple times but the id can only stay the same or |
688 | | - // *decrease*. |
689 | | - |
690 | | - // TODO(@jasnell): Need to determine exactly how to handle. |
691 | | - Debug(&session(), "HTTP/3 application received shutdown notice"); |
| 714 | + void OnShutdown(int64_t id) { |
| 715 | + // The peer has sent a GOAWAY frame initiating a graceful shutdown. |
| 716 | + // For a client, id is the stream ID beyond which the server will |
| 717 | + // not process requests. For a server, id is a push ID (server |
| 718 | + // push is not implemented). Streams/pushes with IDs >= id will |
| 719 | + // not be processed by the peer. |
| 720 | + // |
| 721 | + // When id equals NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID (client) or |
| 722 | + // NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID (server), this is a notice of |
| 723 | + // intent to shut down rather than an immediate refusal. |
| 724 | + // |
| 725 | + // This can be called multiple times with a decreasing id as the |
| 726 | + // peer progressively reduces the set of streams it will process. |
| 727 | + Debug(&session(), |
| 728 | + "HTTP/3 received GOAWAY (id=%" PRIi64 ")", |
| 729 | + id); |
| 730 | + session().Close(Session::CloseMethod::GRACEFUL); |
692 | 731 | } |
693 | 732 |
|
694 | 733 | void OnReceiveSettings(const nghttp3_settings* settings) { |
@@ -747,9 +786,55 @@ class Http3ApplicationImpl final : public Session::Application { |
747 | 786 | uint32_t* pflags, |
748 | 787 | void* conn_user_data, |
749 | 788 | void* stream_user_data) { |
750 | | - return NGTCP2_SUCCESS; |
| 789 | + auto ptr = From(conn, conn_user_data); |
| 790 | + CHECK_NOT_NULL(ptr); |
| 791 | + auto& app = *ptr; |
| 792 | + NgHttp3CallbackScope scope(app.env()); |
| 793 | + |
| 794 | + auto stream = app.session().FindStream(stream_id); |
| 795 | + if (!stream) return NGHTTP3_ERR_CALLBACK_FAILURE; |
| 796 | + |
| 797 | + if (stream->is_eos()) { |
| 798 | + *pflags |= NGHTTP3_DATA_FLAG_EOF; |
| 799 | + return 0; |
| 800 | + } |
| 801 | + |
| 802 | + size_t max_count = std::min(veccnt, |
| 803 | + static_cast<size_t>(kMaxVectorCount)); |
| 804 | + nghttp3_ssize result = 0; |
| 805 | + |
| 806 | + auto next = [&](int status, |
| 807 | + const ngtcp2_vec* data, |
| 808 | + size_t count, |
| 809 | + bob::Done done) { |
| 810 | + switch (status) { |
| 811 | + case bob::Status::STATUS_BLOCK: |
| 812 | + case bob::Status::STATUS_WAIT: |
| 813 | + result = NGHTTP3_ERR_WOULDBLOCK; |
| 814 | + return; |
| 815 | + case bob::Status::STATUS_EOS: |
| 816 | + *pflags |= NGHTTP3_DATA_FLAG_EOF; |
| 817 | + break; |
| 818 | + } |
| 819 | + count = std::min(count, max_count); |
| 820 | + for (size_t n = 0; n < count; n++) { |
| 821 | + vec[n].base = data[n].base; |
| 822 | + vec[n].len = data[n].len; |
| 823 | + } |
| 824 | + result = static_cast<nghttp3_ssize>(count); |
| 825 | + }; |
| 826 | + |
| 827 | + ngtcp2_vec data[kMaxVectorCount]; |
| 828 | + stream->Pull(std::move(next), |
| 829 | + bob::Options::OPTIONS_SYNC, |
| 830 | + data, |
| 831 | + max_count, |
| 832 | + max_count); |
| 833 | + |
| 834 | + return result; |
751 | 835 | } |
752 | 836 |
|
| 837 | + |
753 | 838 | static int on_acked_stream_data(nghttp3_conn* conn, |
754 | 839 | int64_t stream_id, |
755 | 840 | uint64_t datalen, |
@@ -934,7 +1019,7 @@ class Http3ApplicationImpl final : public Session::Application { |
934 | 1019 |
|
935 | 1020 | static int on_shutdown(nghttp3_conn* conn, int64_t id, void* conn_user_data) { |
936 | 1021 | NGHTTP3_CALLBACK_SCOPE(app); |
937 | | - app.OnShutdown(); |
| 1022 | + app.OnShutdown(id); |
938 | 1023 | return NGTCP2_SUCCESS; |
939 | 1024 | } |
940 | 1025 |
|
@@ -950,14 +1035,14 @@ class Http3ApplicationImpl final : public Session::Application { |
950 | 1035 | const uint8_t* origin, |
951 | 1036 | size_t originlen, |
952 | 1037 | void* conn_user_data) { |
953 | | - // TODO(@jasnell): Handle the origin callback. This is called |
954 | | - // when a single origin in an ORIGIN frame is received. |
| 1038 | + // ORIGIN frames (RFC 8336) are used for connection coalescing |
| 1039 | + // across multiple origins. Not yet implemented u2014 requires |
| 1040 | + // connection pooling and multi-origin reuse support. |
955 | 1041 | return NGTCP2_SUCCESS; |
956 | 1042 | } |
957 | 1043 |
|
958 | 1044 | static int on_end_origin(nghttp3_conn* conn, void* conn_user_data) { |
959 | | - // TODO(@jasnell): Handle the end of origin callback. This is called |
960 | | - // when the end of an ORIGIN frame is received. |
| 1045 | + // See on_receive_origin above. |
961 | 1046 | return NGTCP2_SUCCESS; |
962 | 1047 | } |
963 | 1048 |
|
|