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 <cstdint> 13 #include <memory> 14 #include <string> 15 16 // The dependencies of this API should be kept minimal and independent of 17 // specific transport implementations. 18 #include "absl/strings/string_view.h" 19 #include "absl/time/time.h" 20 #include "absl/types/span.h" 21 #include "quiche/common/platform/api/quiche_export.h" 22 #include "quiche/common/quiche_callbacks.h" 23 #include "quiche/common/quiche_stream.h" 24 25 namespace webtransport { 26 27 enum class Perspective { kClient, kServer }; 28 29 // A numeric ID uniquely identifying a WebTransport stream. Note that by design, 30 // those IDs are not available in the Web API, and the IDs do not necessarily 31 // match between client and server perspective, since there may be a proxy 32 // between them. 33 using StreamId = uint32_t; 34 // Application-specific error code used for resetting either the read or the 35 // write half of the stream. 36 using StreamErrorCode = uint32_t; 37 // Application-specific error code used for closing a WebTransport session. 38 using SessionErrorCode = uint32_t; 39 40 // WebTransport priority as defined in 41 // https://w3c.github.io/webtransport/#webtransportsendstream-write 42 // The rules are as follows: 43 // - Streams with the same priority are handled in FIFO order. 44 // - Streams with the same group_id but different send_order are handled 45 // strictly in order. 46 // - Different group_ids are handled in the FIFO order. 47 using SendGroupId = uint32_t; 48 using SendOrder = int64_t; 49 struct QUICHE_EXPORT StreamPriority { 50 SendGroupId send_group_id = 0; 51 SendOrder send_order = 0; 52 53 bool operator==(const StreamPriority& other) const { 54 return send_group_id == other.send_group_id && 55 send_order == other.send_order; 56 } 57 }; 58 59 // An outcome of a datagram send call. 60 enum class DatagramStatusCode { 61 // Datagram has been successfully sent or placed into the datagram queue. 62 kSuccess, 63 // Datagram has not been sent since the underlying QUIC connection is blocked 64 // by the congestion control. Note that this can only happen if the queue is 65 // full. 66 kBlocked, 67 // Datagram has not been sent since it is too large to fit into a single 68 // UDP packet. 69 kTooBig, 70 // An unspecified internal error. 71 kInternalError, 72 }; 73 74 // An outcome of a datagram send call, in both enum and human-readable form. 75 struct QUICHE_EXPORT DatagramStatus { DatagramStatusDatagramStatus76 explicit DatagramStatus(DatagramStatusCode code, std::string error_message) 77 : code(code), error_message(std::move(error_message)) {} 78 79 DatagramStatusCode code; 80 std::string error_message; 81 }; 82 83 enum class StreamType { 84 kUnidirectional, 85 kBidirectional, 86 }; 87 88 // Based on 89 // https://w3c.github.io/webtransport/#dictdef-webtransportdatagramstats. 90 struct QUICHE_EXPORT DatagramStats { 91 uint64_t expired_outgoing; 92 uint64_t lost_outgoing; 93 94 // droppedIncoming is not present, since in the C++ API, we immediately 95 // deliver datagrams via callback, meaning there is no queue where things 96 // would be dropped. 97 }; 98 99 // Based on https://w3c.github.io/webtransport/#web-transport-stats 100 // Note that this is currently not a complete implementation of that API, as 101 // some of those still need to be clarified in 102 // https://github.com/w3c/webtransport/issues/537 103 struct QUICHE_EXPORT SessionStats { 104 absl::Duration min_rtt; 105 absl::Duration smoothed_rtt; 106 absl::Duration rtt_variation; 107 108 uint64_t estimated_send_rate_bps; // In bits per second. 109 110 DatagramStats datagram_stats; 111 }; 112 113 // The stream visitor is an application-provided object that gets notified about 114 // events related to a WebTransport stream. The visitor object is owned by the 115 // stream itself, meaning that if the stream is ever fully closed, the visitor 116 // will be garbage-collected. 117 class QUICHE_EXPORT StreamVisitor : public quiche::ReadStreamVisitor, 118 public quiche::WriteStreamVisitor { 119 public: ~StreamVisitor()120 virtual ~StreamVisitor() {} 121 122 // Called when RESET_STREAM is received for the stream. 123 virtual void OnResetStreamReceived(StreamErrorCode error) = 0; 124 // Called when STOP_SENDING is received for the stream. 125 virtual void OnStopSendingReceived(StreamErrorCode error) = 0; 126 // Called when the write side of the stream is closed and all of the data sent 127 // has been acknowledged ("Data Recvd" state of RFC 9000). Primarily used by 128 // the state machine of the Web API. 129 virtual void OnWriteSideInDataRecvdState() = 0; 130 }; 131 132 // A stream (either bidirectional or unidirectional) that is contained within a 133 // WebTransport session. 134 class QUICHE_EXPORT Stream : public quiche::ReadStream, 135 public quiche::WriteStream, 136 public quiche::TerminableStream { 137 public: ~Stream()138 virtual ~Stream() {} 139 140 // An ID that is unique within the session. Those are not exposed to the user 141 // via the web API, but can be used internally for bookkeeping and 142 // diagnostics. 143 virtual StreamId GetStreamId() const = 0; 144 145 // Resets the read or the write side of the stream with the specified error 146 // code. 147 virtual void ResetWithUserCode(StreamErrorCode error) = 0; 148 virtual void SendStopSending(StreamErrorCode error) = 0; 149 150 // A general-purpose stream reset method that may be used when a specific 151 // error code is not available. 152 virtual void ResetDueToInternalError() = 0; 153 // If the stream has not been already reset, reset the stream. This is 154 // primarily used in the JavaScript API when the stream object has been 155 // garbage collected. 156 virtual void MaybeResetDueToStreamObjectGone() = 0; 157 158 virtual StreamVisitor* visitor() = 0; 159 virtual void SetVisitor(std::unique_ptr<StreamVisitor> visitor) = 0; 160 }; 161 162 // Visitor that gets notified about events related to a WebTransport session. 163 class QUICHE_EXPORT SessionVisitor { 164 public: ~SessionVisitor()165 virtual ~SessionVisitor() {} 166 167 // Notifies the visitor when the session is ready to exchange application 168 // data. 169 virtual void OnSessionReady() = 0; 170 171 // Notifies the visitor when the session has been closed. 172 virtual void OnSessionClosed(SessionErrorCode error_code, 173 const std::string& error_message) = 0; 174 175 // Notifies the visitor when a new stream has been received. The stream in 176 // question can be retrieved using AcceptIncomingBidirectionalStream() or 177 // AcceptIncomingUnidirectionalStream(). 178 virtual void OnIncomingBidirectionalStreamAvailable() = 0; 179 virtual void OnIncomingUnidirectionalStreamAvailable() = 0; 180 181 // Notifies the visitor when a new datagram has been received. 182 virtual void OnDatagramReceived(absl::string_view datagram) = 0; 183 184 // Notifies the visitor that a new outgoing stream can now be created. 185 virtual void OnCanCreateNewOutgoingBidirectionalStream() = 0; 186 virtual void OnCanCreateNewOutgoingUnidirectionalStream() = 0; 187 }; 188 189 // An abstract interface for a WebTransport session. 190 // 191 // *** AN IMPORTANT NOTE ABOUT STREAM LIFETIMES *** 192 // Stream objects are managed internally by the underlying QUIC stack, and can 193 // go away at any time due to the peer resetting the stream. Because of that, 194 // any pointers to the stream objects returned by this class MUST NEVER be 195 // retained long-term, except inside the stream visitor (the stream visitor is 196 // owned by the stream object). If you need to store a reference to a stream, 197 // consider one of the two following options: 198 // (1) store a stream ID, 199 // (2) store a weak pointer to the stream visitor, and then access the stream 200 // via the said visitor (the visitor is guaranteed to be alive as long as 201 // the stream is alive). 202 class QUICHE_EXPORT Session { 203 public: ~Session()204 virtual ~Session() {} 205 206 // Closes the WebTransport session in question with the specified |error_code| 207 // and |error_message|. 208 virtual void CloseSession(SessionErrorCode error_code, 209 absl::string_view error_message) = 0; 210 211 // Return the earliest incoming stream that has been received by the session 212 // but has not been accepted. Returns nullptr if there are no incoming 213 // streams. See the class note regarding the lifetime of the returned stream 214 // object. 215 virtual Stream* AcceptIncomingBidirectionalStream() = 0; 216 virtual Stream* AcceptIncomingUnidirectionalStream() = 0; 217 218 // Returns true if flow control allows opening a new stream. 219 // 220 // IMPORTANT: See the class note regarding the lifetime of the returned stream 221 // object. 222 virtual bool CanOpenNextOutgoingBidirectionalStream() = 0; 223 virtual bool CanOpenNextOutgoingUnidirectionalStream() = 0; 224 225 // Opens a new WebTransport stream, or returns nullptr if that is not possible 226 // due to flow control. See the class note regarding the lifetime of the 227 // returned stream object. 228 // 229 // IMPORTANT: See the class note regarding the lifetime of the returned stream 230 // object. 231 virtual Stream* OpenOutgoingBidirectionalStream() = 0; 232 virtual Stream* OpenOutgoingUnidirectionalStream() = 0; 233 234 // Returns the WebTransport stream with the corresponding ID. 235 // 236 // IMPORTANT: See the class note regarding the lifetime of the returned stream 237 // object. 238 virtual Stream* GetStreamById(StreamId id) = 0; 239 240 virtual DatagramStatus SendOrQueueDatagram(absl::string_view datagram) = 0; 241 // Returns a conservative estimate of the largest datagram size that the 242 // session would be able to send. 243 virtual uint64_t GetMaxDatagramSize() const = 0; 244 // Sets the largest duration that a datagram can spend in the queue before 245 // being silently dropped. 246 virtual void SetDatagramMaxTimeInQueue(absl::Duration max_time_in_queue) = 0; 247 248 // Returns stats that generally follow the semantics of W3C WebTransport API. 249 virtual DatagramStats GetDatagramStats() = 0; 250 virtual SessionStats GetSessionStats() = 0; 251 252 // Sends a DRAIN_WEBTRANSPORT_SESSION capsule or an equivalent signal to the 253 // peer indicating that the session is draining. 254 virtual void NotifySessionDraining() = 0; 255 // Notifies that either the session itself (DRAIN_WEBTRANSPORT_SESSION 256 // capsule), or the underlying connection (HTTP GOAWAY) is being drained by 257 // the peer. 258 virtual void SetOnDraining(quiche::SingleUseCallback<void()> callback) = 0; 259 }; 260 261 } // namespace webtransport 262 263 #endif // QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_ 264