1 // Copyright 2021 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ 6 #define QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ 7 8 #include <memory> 9 #include <optional> 10 11 #include "absl/base/attributes.h" 12 #include "absl/container/flat_hash_set.h" 13 #include "absl/time/time.h" 14 #include "quiche/quic/core/http/quic_spdy_session.h" 15 #include "quiche/quic/core/http/web_transport_stream_adapter.h" 16 #include "quiche/quic/core/quic_error_codes.h" 17 #include "quiche/quic/core/quic_stream.h" 18 #include "quiche/quic/core/quic_types.h" 19 #include "quiche/quic/core/web_transport_interface.h" 20 #include "quiche/quic/core/web_transport_stats.h" 21 #include "quiche/common/platform/api/quiche_mem_slice.h" 22 #include "quiche/common/quiche_callbacks.h" 23 #include "quiche/web_transport/web_transport.h" 24 #include "quiche/spdy/core/http2_header_block.h" 25 26 namespace quic { 27 28 class QuicSpdySession; 29 class QuicSpdyStream; 30 31 enum class WebTransportHttp3RejectionReason { 32 kNone, 33 kNoStatusCode, 34 kWrongStatusCode, 35 kMissingDraftVersion, 36 kUnsupportedDraftVersion, 37 }; 38 39 // A session of WebTransport over HTTP/3. The session is owned by 40 // QuicSpdyStream object for the CONNECT stream that established it. 41 // 42 // WebTransport over HTTP/3 specification: 43 // <https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3> 44 class QUICHE_EXPORT WebTransportHttp3 45 : public WebTransportSession, 46 public QuicSpdyStream::Http3DatagramVisitor { 47 public: 48 WebTransportHttp3(QuicSpdySession* session, QuicSpdyStream* connect_stream, 49 WebTransportSessionId id); 50 51 void HeadersReceived(const spdy::Http2HeaderBlock& headers); SetVisitor(std::unique_ptr<WebTransportVisitor> visitor)52 void SetVisitor(std::unique_ptr<WebTransportVisitor> visitor) { 53 visitor_ = std::move(visitor); 54 } 55 id()56 WebTransportSessionId id() { return id_; } ready()57 bool ready() { return ready_; } 58 59 void AssociateStream(QuicStreamId stream_id); OnStreamClosed(QuicStreamId stream_id)60 void OnStreamClosed(QuicStreamId stream_id) { streams_.erase(stream_id); } 61 void OnConnectStreamClosing(); 62 NumberOfAssociatedStreams()63 size_t NumberOfAssociatedStreams() { return streams_.size(); } 64 65 void CloseSession(WebTransportSessionError error_code, 66 absl::string_view error_message) override; 67 void OnCloseReceived(WebTransportSessionError error_code, 68 absl::string_view error_message); 69 void OnConnectStreamFinReceived(); 70 71 // It is legal for WebTransport to be closed without a 72 // CLOSE_WEBTRANSPORT_SESSION capsule. We always send a capsule, but we still 73 // need to ensure we handle this case correctly. 74 void CloseSessionWithFinOnlyForTests(); 75 76 // Return the earliest incoming stream that has been received by the session 77 // but has not been accepted. Returns nullptr if there are no incoming 78 // streams. 79 WebTransportStream* AcceptIncomingBidirectionalStream() override; 80 WebTransportStream* AcceptIncomingUnidirectionalStream() override; 81 82 bool CanOpenNextOutgoingBidirectionalStream() override; 83 bool CanOpenNextOutgoingUnidirectionalStream() override; 84 WebTransportStream* OpenOutgoingBidirectionalStream() override; 85 WebTransportStream* OpenOutgoingUnidirectionalStream() override; 86 87 webtransport::Stream* GetStreamById(webtransport::StreamId id) override; 88 89 webtransport::DatagramStatus SendOrQueueDatagram( 90 absl::string_view datagram) override; 91 QuicByteCount GetMaxDatagramSize() const override; 92 void SetDatagramMaxTimeInQueue(absl::Duration max_time_in_queue) override; 93 GetDatagramStats()94 webtransport::DatagramStats GetDatagramStats() override { 95 return WebTransportDatagramStatsForQuicSession(*session_); 96 } GetSessionStats()97 webtransport::SessionStats GetSessionStats() override { 98 return WebTransportStatsForQuicSession(*session_); 99 } 100 101 void NotifySessionDraining() override; SetOnDraining(quiche::SingleUseCallback<void ()> callback)102 void SetOnDraining(quiche::SingleUseCallback<void()> callback) override { 103 drain_callback_ = std::move(callback); 104 } 105 106 // From QuicSpdyStream::Http3DatagramVisitor. 107 void OnHttp3Datagram(QuicStreamId stream_id, 108 absl::string_view payload) override; OnUnknownCapsule(QuicStreamId,const quiche::UnknownCapsule &)109 void OnUnknownCapsule(QuicStreamId /*stream_id*/, 110 const quiche::UnknownCapsule& /*capsule*/) override {} 111 close_received()112 bool close_received() const { return close_received_; } rejection_reason()113 WebTransportHttp3RejectionReason rejection_reason() const { 114 return rejection_reason_; 115 } 116 117 void OnGoAwayReceived(); 118 void OnDrainSessionReceived(); 119 120 private: 121 // Notifies the visitor that the connection has been closed. Ensures that the 122 // visitor is only ever called once. 123 void MaybeNotifyClose(); 124 125 QuicSpdySession* const session_; // Unowned. 126 QuicSpdyStream* const connect_stream_; // Unowned. 127 const WebTransportSessionId id_; 128 // |ready_| is set to true when the peer has seen both sets of headers. 129 bool ready_ = false; 130 std::unique_ptr<WebTransportVisitor> visitor_; 131 absl::flat_hash_set<QuicStreamId> streams_; 132 quiche::QuicheCircularDeque<QuicStreamId> incoming_bidirectional_streams_; 133 quiche::QuicheCircularDeque<QuicStreamId> incoming_unidirectional_streams_; 134 135 bool close_sent_ = false; 136 bool close_received_ = false; 137 bool close_notified_ = false; 138 139 quiche::SingleUseCallback<void()> drain_callback_ = nullptr; 140 141 WebTransportHttp3RejectionReason rejection_reason_ = 142 WebTransportHttp3RejectionReason::kNone; 143 bool drain_sent_ = false; 144 // Those are set to default values, which are used if the session is not 145 // closed cleanly using an appropriate capsule. 146 WebTransportSessionError error_code_ = 0; 147 std::string error_message_ = ""; 148 }; 149 150 class QUICHE_EXPORT WebTransportHttp3UnidirectionalStream : public QuicStream { 151 public: 152 // Incoming stream. 153 WebTransportHttp3UnidirectionalStream(PendingStream* pending, 154 QuicSpdySession* session); 155 // Outgoing stream. 156 WebTransportHttp3UnidirectionalStream(QuicStreamId id, 157 QuicSpdySession* session, 158 WebTransportSessionId session_id); 159 160 // Sends the stream type and the session ID on the stream. 161 void WritePreamble(); 162 163 // Implementation of QuicStream. 164 void OnDataAvailable() override; 165 void OnCanWriteNewData() override; 166 void OnClose() override; 167 void OnStreamReset(const QuicRstStreamFrame& frame) override; 168 bool OnStopSending(QuicResetStreamError error) override; 169 void OnWriteSideInDataRecvdState() override; 170 interface()171 WebTransportStream* interface() { return &adapter_; } SetUnblocked()172 void SetUnblocked() { sequencer()->SetUnblocked(); } 173 174 private: 175 QuicSpdySession* session_; 176 WebTransportStreamAdapter adapter_; 177 std::optional<WebTransportSessionId> session_id_; 178 bool needs_to_send_preamble_; 179 180 bool ReadSessionId(); 181 // Closes the stream if all of the data has been received. 182 void MaybeCloseIncompleteStream(); 183 }; 184 185 // Remaps HTTP/3 error code into a WebTransport error code. Returns nullopt if 186 // the provided code is outside of valid range. 187 QUICHE_EXPORT std::optional<WebTransportStreamError> Http3ErrorToWebTransport( 188 uint64_t http3_error_code); 189 190 // Same as above, but returns default error value (zero) when none could be 191 // mapped. 192 QUICHE_EXPORT WebTransportStreamError 193 Http3ErrorToWebTransportOrDefault(uint64_t http3_error_code); 194 195 // Remaps WebTransport error code into an HTTP/3 error code. 196 QUICHE_EXPORT uint64_t 197 WebTransportErrorToHttp3(WebTransportStreamError webtransport_error_code); 198 199 } // namespace quic 200 201 #endif // QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ 202