| // Copyright 2021 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ | |
| #define QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ | |
| #include <memory> | |
| #include "absl/base/attributes.h" | |
| #include "absl/container/flat_hash_set.h" | |
| #include "absl/types/optional.h" | |
| #include "quic/core/http/quic_spdy_session.h" | |
| #include "quic/core/http/web_transport_stream_adapter.h" | |
| #include "quic/core/quic_error_codes.h" | |
| #include "quic/core/quic_stream.h" | |
| #include "quic/core/quic_types.h" | |
| #include "quic/core/web_transport_interface.h" | |
| #include "spdy/core/spdy_header_block.h" | |
| namespace quic { | |
| class QuicSpdySession; | |
| class QuicSpdyStream; | |
| enum class WebTransportHttp3RejectionReason { | |
| kNone, | |
| kNoStatusCode, | |
| kWrongStatusCode, | |
| kMissingDraftVersion, | |
| kUnsupportedDraftVersion, | |
| }; | |
| // A session of WebTransport over HTTP/3. The session is owned by | |
| // QuicSpdyStream object for the CONNECT stream that established it. | |
| // | |
| // WebTransport over HTTP/3 specification: | |
| // <https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3> | |
| class QUIC_EXPORT_PRIVATE WebTransportHttp3 | |
| : public WebTransportSession, | |
| public QuicSpdyStream::Http3DatagramRegistrationVisitor, | |
| public QuicSpdyStream::Http3DatagramVisitor { | |
| public: | |
| WebTransportHttp3(QuicSpdySession* session, QuicSpdyStream* connect_stream, | |
| WebTransportSessionId id, | |
| bool attempt_to_use_datagram_contexts); | |
| void HeadersReceived(const spdy::SpdyHeaderBlock& headers); | |
| void SetVisitor(std::unique_ptr<WebTransportVisitor> visitor) { | |
| visitor_ = std::move(visitor); | |
| } | |
| WebTransportSessionId id() { return id_; } | |
| bool ready() { return ready_; } | |
| absl::optional<QuicDatagramContextId> context_id() const { | |
| return context_id_; | |
| } | |
| void AssociateStream(QuicStreamId stream_id); | |
| void OnStreamClosed(QuicStreamId stream_id) { streams_.erase(stream_id); } | |
| void OnConnectStreamClosing(); | |
| size_t NumberOfAssociatedStreams() { return streams_.size(); } | |
| void CloseSession(WebTransportSessionError error_code, | |
| absl::string_view error_message) override; | |
| void OnCloseReceived(WebTransportSessionError error_code, | |
| absl::string_view error_message); | |
| void OnConnectStreamFinReceived(); | |
| // It is legal for WebTransport to be closed without a | |
| // CLOSE_WEBTRANSPORT_SESSION capsule. We always send a capsule, but we still | |
| // need to ensure we handle this case correctly. | |
| void CloseSessionWithFinOnlyForTests(); | |
| // Return the earliest incoming stream that has been received by the session | |
| // but has not been accepted. Returns nullptr if there are no incoming | |
| // streams. | |
| WebTransportStream* AcceptIncomingBidirectionalStream() override; | |
| WebTransportStream* AcceptIncomingUnidirectionalStream() override; | |
| bool CanOpenNextOutgoingBidirectionalStream() override; | |
| bool CanOpenNextOutgoingUnidirectionalStream() override; | |
| WebTransportStream* OpenOutgoingBidirectionalStream() override; | |
| WebTransportStream* OpenOutgoingUnidirectionalStream() override; | |
| MessageStatus SendOrQueueDatagram(QuicMemSlice datagram) override; | |
| QuicByteCount GetMaxDatagramSize() const override; | |
| void SetDatagramMaxTimeInQueue(QuicTime::Delta max_time_in_queue) override; | |
| // From QuicSpdyStream::Http3DatagramVisitor. | |
| void OnHttp3Datagram(QuicStreamId stream_id, | |
| absl::optional<QuicDatagramContextId> context_id, | |
| absl::string_view payload) override; | |
| // From QuicSpdyStream::Http3DatagramRegistrationVisitor. | |
| void OnContextReceived(QuicStreamId stream_id, | |
| absl::optional<QuicDatagramContextId> context_id, | |
| DatagramFormatType format_type, | |
| absl::string_view format_additional_data) override; | |
| void OnContextClosed(QuicStreamId stream_id, | |
| absl::optional<QuicDatagramContextId> context_id, | |
| ContextCloseCode close_code, | |
| absl::string_view close_details) override; | |
| bool close_received() const { return close_received_; } | |
| WebTransportHttp3RejectionReason rejection_reason() const { | |
| return rejection_reason_; | |
| } | |
| private: | |
| // Notifies the visitor that the connection has been closed. Ensures that the | |
| // visitor is only ever called once. | |
| void MaybeNotifyClose(); | |
| QuicSpdySession* const session_; // Unowned. | |
| QuicSpdyStream* const connect_stream_; // Unowned. | |
| const WebTransportSessionId id_; | |
| absl::optional<QuicDatagramContextId> context_id_; | |
| // |ready_| is set to true when the peer has seen both sets of headers. | |
| bool ready_ = false; | |
| // Whether we know which |context_id_| to use. On the client this is always | |
| // true, and on the server it becomes true when we receive a context | |
| // registration capsule. | |
| bool context_is_known_ = false; | |
| // Whether |context_id_| is currently registered with |connect_stream_|. | |
| bool context_currently_registered_ = false; | |
| std::unique_ptr<WebTransportVisitor> visitor_; | |
| absl::flat_hash_set<QuicStreamId> streams_; | |
| quiche::QuicheCircularDeque<QuicStreamId> incoming_bidirectional_streams_; | |
| quiche::QuicheCircularDeque<QuicStreamId> incoming_unidirectional_streams_; | |
| bool close_sent_ = false; | |
| bool close_received_ = false; | |
| bool close_notified_ = false; | |
| WebTransportHttp3RejectionReason rejection_reason_ = | |
| WebTransportHttp3RejectionReason::kNone; | |
| // Those are set to default values, which are used if the session is not | |
| // closed cleanly using an appropriate capsule. | |
| WebTransportSessionError error_code_ = 0; | |
| std::string error_message_ = ""; | |
| }; | |
| class QUIC_EXPORT_PRIVATE WebTransportHttp3UnidirectionalStream | |
| : public QuicStream { | |
| public: | |
| // Incoming stream. | |
| WebTransportHttp3UnidirectionalStream(PendingStream* pending, | |
| QuicSpdySession* session); | |
| // Outgoing stream. | |
| WebTransportHttp3UnidirectionalStream(QuicStreamId id, | |
| QuicSpdySession* session, | |
| WebTransportSessionId session_id); | |
| // Sends the stream type and the session ID on the stream. | |
| void WritePreamble(); | |
| // Implementation of QuicStream. | |
| void OnDataAvailable() override; | |
| void OnCanWriteNewData() override; | |
| void OnClose() override; | |
| void OnStreamReset(const QuicRstStreamFrame& frame) override; | |
| bool OnStopSending(QuicResetStreamError error) override; | |
| void OnWriteSideInDataRecvdState() override; | |
| WebTransportStream* interface() { return &adapter_; } | |
| void SetUnblocked() { sequencer()->SetUnblocked(); } | |
| private: | |
| QuicSpdySession* session_; | |
| WebTransportStreamAdapter adapter_; | |
| absl::optional<WebTransportSessionId> session_id_; | |
| bool needs_to_send_preamble_; | |
| bool ReadSessionId(); | |
| // Closes the stream if all of the data has been received. | |
| void MaybeCloseIncompleteStream(); | |
| }; | |
| // Remaps HTTP/3 error code into a WebTransport error code. Returns nullopt if | |
| // the provided code is outside of valid range. | |
| QUIC_EXPORT_PRIVATE absl::optional<WebTransportStreamError> | |
| Http3ErrorToWebTransport(uint64_t http3_error_code); | |
| // Same as above, but returns default error value (zero) when none could be | |
| // mapped. | |
| QUIC_EXPORT_PRIVATE WebTransportStreamError | |
| Http3ErrorToWebTransportOrDefault(uint64_t http3_error_code); | |
| // Remaps WebTransport error code into an HTTP/3 error code. | |
| QUIC_EXPORT_PRIVATE uint64_t | |
| WebTransportErrorToHttp3(WebTransportStreamError webtransport_error_code); | |
| } // namespace quic | |
| #endif // QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ |