1 // Copyright (c) 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 // This header contains interfaces that abstract away different backing 6 // protocols for WebTransport. 7 8 #ifndef QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_ 9 #define QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_ 10 11 #include <cstddef> 12 #include <memory> 13 #include <string> 14 15 // The dependencies of this API should be kept minimal and independent of 16 // specific transport implementations. 17 #include "absl/strings/string_view.h" 18 #include "absl/time/time.h" 19 #include "absl/types/span.h" 20 #include "quiche/common/platform/api/quiche_export.h" 21 #include "quiche/common/quiche_stream.h" 22 #include "quiche/spdy/core/http2_header_block.h" 23 24 namespace webtransport { 25 26 // A numeric ID uniquely identifying a WebTransport stream. Note that by design, 27 // those IDs are not available in the Web API, and the IDs do not necessarily 28 // match between client and server perspective, since there may be a proxy 29 // between them. 30 using StreamId = uint32_t; 31 // Application-specific error code used for resetting either the read or the 32 // write half of the stream. 33 using StreamErrorCode = uint8_t; 34 // Application-specific error code used for closing a WebTransport session. 35 using SessionErrorCode = uint32_t; 36 37 // An outcome of a datagram send call. 38 enum class DatagramStatusCode { 39 // Datagram has been successfully sent or placed into the datagram queue. 40 kSuccess, 41 // Datagram has not been sent since the underlying QUIC connection is blocked 42 // by the congestion control. Note that this can only happen if the queue is 43 // full. 44 kBlocked, 45 // Datagram has not been sent since it is too large to fit into a single 46 // UDP packet. 47 kTooBig, 48 // An unspecified internal error. 49 kInternalError, 50 }; 51 52 // An outcome of a datagram send call, in both enum and human-readable form. 53 struct QUICHE_EXPORT DatagramStatus { DatagramStatusDatagramStatus54 explicit DatagramStatus(DatagramStatusCode code, std::string error_message) 55 : code(code), error_message(std::move(error_message)) {} 56 57 DatagramStatusCode code; 58 std::string error_message; 59 }; 60 61 enum class StreamType { 62 kUnidirectional, 63 kBidirectional, 64 }; 65 66 // The stream visitor is an application-provided object that gets notified about 67 // events related to a WebTransport stream. The visitor object is owned by the 68 // stream itself, meaning that if the stream is ever fully closed, the visitor 69 // will be garbage-collected. 70 class QUICHE_EXPORT StreamVisitor : public quiche::WriteStreamVisitor { 71 public: ~StreamVisitor()72 virtual ~StreamVisitor() {} 73 74 // Called whenever the stream has readable data available. 75 virtual void OnCanRead() = 0; 76 77 // Called when RESET_STREAM is received for the stream. 78 virtual void OnResetStreamReceived(StreamErrorCode error) = 0; 79 // Called when STOP_SENDING is received for the stream. 80 virtual void OnStopSendingReceived(StreamErrorCode error) = 0; 81 // Called when the write side of the stream is closed and all of the data sent 82 // has been acknowledged ("Data Recvd" state of RFC 9000). Primarily used by 83 // the state machine of the Web API. 84 virtual void OnWriteSideInDataRecvdState() = 0; 85 }; 86 87 // A stream (either bidirectional or unidirectional) that is contained within a 88 // WebTransport session. 89 class QUICHE_EXPORT Stream : public quiche::WriteStream { 90 public: 91 struct QUICHE_EXPORT ReadResult { 92 // Number of bytes actually read. 93 size_t bytes_read; 94 // Whether the FIN has been received; if true, no further data will arrive 95 // on the stream, and the stream object can be soon potentially garbage 96 // collected. 97 bool fin; 98 }; 99 ~Stream()100 virtual ~Stream() {} 101 102 // Reads at most |buffer.size()| bytes into |buffer|. 103 [[nodiscard]] virtual ReadResult Read(absl::Span<char> buffer) = 0; 104 // Reads all available data and appends it to the end of |output|. 105 [[nodiscard]] virtual ReadResult Read(std::string* output) = 0; 106 107 // Indicates the number of bytes that can be read from the stream. 108 virtual size_t ReadableBytes() const = 0; 109 110 // An ID that is unique within the session. Those are not exposed to the user 111 // via the web API, but can be used internally for bookkeeping and 112 // diagnostics. 113 virtual StreamId GetStreamId() const = 0; 114 115 // Resets the read or the write side of the stream with the specified error 116 // code. 117 virtual void ResetWithUserCode(StreamErrorCode error) = 0; 118 virtual void SendStopSending(StreamErrorCode error) = 0; 119 120 // A general-purpose stream reset method that may be used when a specific 121 // error code is not available. 122 virtual void ResetDueToInternalError() = 0; 123 // If the stream has not been already reset, reset the stream. This is 124 // primarily used in the JavaScript API when the stream object has been 125 // garbage collected. 126 virtual void MaybeResetDueToStreamObjectGone() = 0; 127 128 virtual StreamVisitor* visitor() = 0; 129 virtual void SetVisitor(std::unique_ptr<StreamVisitor> visitor) = 0; 130 }; 131 132 // Visitor that gets notified about events related to a WebTransport session. 133 class QUICHE_EXPORT SessionVisitor { 134 public: ~SessionVisitor()135 virtual ~SessionVisitor() {} 136 137 // Notifies the visitor when the session is ready to exchange application 138 // data. 139 virtual void OnSessionReady(const spdy::Http2HeaderBlock& headers) = 0; 140 141 // Notifies the visitor when the session has been closed. 142 virtual void OnSessionClosed(SessionErrorCode error_code, 143 const std::string& error_message) = 0; 144 145 // Notifies the visitor when a new stream has been received. The stream in 146 // question can be retrieved using AcceptIncomingBidirectionalStream() or 147 // AcceptIncomingUnidirectionalStream(). 148 virtual void OnIncomingBidirectionalStreamAvailable() = 0; 149 virtual void OnIncomingUnidirectionalStreamAvailable() = 0; 150 151 // Notifies the visitor when a new datagram has been received. 152 virtual void OnDatagramReceived(absl::string_view datagram) = 0; 153 154 // Notifies the visitor that a new outgoing stream can now be created. 155 virtual void OnCanCreateNewOutgoingBidirectionalStream() = 0; 156 virtual void OnCanCreateNewOutgoingUnidirectionalStream() = 0; 157 }; 158 159 // An abstract interface for a WebTransport session. 160 // 161 // *** AN IMPORTANT NOTE ABOUT STREAM LIFETIMES *** 162 // Stream objects are managed internally by the underlying QUIC stack, and can 163 // go away at any time due to the peer resetting the stream. Because of that, 164 // any pointers to the stream objects returned by this class MUST NEVER be 165 // retained long-term, except inside the stream visitor (the stream visitor is 166 // owned by the stream object). If you need to store a reference to a stream, 167 // consider one of the two following options: 168 // (1) store a stream ID, 169 // (2) store a weak pointer to the stream visitor, and then access the stream 170 // via the said visitor (the visitor is guaranteed to be alive as long as 171 // the stream is alive). 172 class QUICHE_EXPORT Session { 173 public: ~Session()174 virtual ~Session() {} 175 176 // Closes the WebTransport session in question with the specified |error_code| 177 // and |error_message|. 178 virtual void CloseSession(SessionErrorCode error_code, 179 absl::string_view error_message) = 0; 180 181 // Return the earliest incoming stream that has been received by the session 182 // but has not been accepted. Returns nullptr if there are no incoming 183 // streams. See the class note regarding the lifetime of the returned stream 184 // object. 185 virtual Stream* AcceptIncomingBidirectionalStream() = 0; 186 virtual Stream* AcceptIncomingUnidirectionalStream() = 0; 187 188 // Returns true if flow control allows opening a new stream. 189 // 190 // IMPORTANT: See the class note regarding the lifetime of the returned stream 191 // object. 192 virtual bool CanOpenNextOutgoingBidirectionalStream() = 0; 193 virtual bool CanOpenNextOutgoingUnidirectionalStream() = 0; 194 195 // Opens a new WebTransport stream, or returns nullptr if that is not possible 196 // due to flow control. See the class note regarding the lifetime of the 197 // returned stream object. 198 // 199 // IMPORTANT: See the class note regarding the lifetime of the returned stream 200 // object. 201 virtual Stream* OpenOutgoingBidirectionalStream() = 0; 202 virtual Stream* OpenOutgoingUnidirectionalStream() = 0; 203 204 // Returns the WebTransport stream with the corresponding ID. 205 // 206 // IMPORTANT: See the class note regarding the lifetime of the returned stream 207 // object. 208 virtual Stream* GetStreamById(StreamId id) = 0; 209 210 virtual DatagramStatus SendOrQueueDatagram(absl::string_view datagram) = 0; 211 // Returns a conservative estimate of the largest datagram size that the 212 // session would be able to send. 213 virtual uint64_t GetMaxDatagramSize() const = 0; 214 // Sets the largest duration that a datagram can spend in the queue before 215 // being silently dropped. 216 virtual void SetDatagramMaxTimeInQueue(absl::Duration max_time_in_queue) = 0; 217 }; 218 219 } // namespace webtransport 220 221 #endif // QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_ 222