| // Copyright (c) 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. | 
 |  | 
 | // This header contains interfaces that abstract away different backing | 
 | // protocols for WebTransport. | 
 |  | 
 | #ifndef QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_ | 
 | #define QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_ | 
 |  | 
 | #include <cstddef> | 
 | #include <cstdint> | 
 | #include <memory> | 
 | #include <optional> | 
 | #include <string> | 
 |  | 
 | // The dependencies of this API should be kept minimal and independent of | 
 | // specific transport implementations. | 
 | #include "absl/strings/string_view.h" | 
 | #include "absl/time/time.h" | 
 | #include "absl/types/span.h" | 
 | #include "quiche/common/platform/api/quiche_export.h" | 
 | #include "quiche/common/quiche_callbacks.h" | 
 | #include "quiche/common/quiche_stream.h" | 
 |  | 
 | namespace webtransport { | 
 |  | 
 | enum class Perspective { kClient, kServer }; | 
 |  | 
 | // A numeric ID uniquely identifying a WebTransport stream. Note that by design, | 
 | // those IDs are not available in the Web API, and the IDs do not necessarily | 
 | // match between client and server perspective, since there may be a proxy | 
 | // between them. | 
 | using StreamId = uint32_t; | 
 | // Application-specific error code used for resetting either the read or the | 
 | // write half of the stream. | 
 | using StreamErrorCode = uint32_t; | 
 | // Application-specific error code used for closing a WebTransport session. | 
 | using SessionErrorCode = uint32_t; | 
 |  | 
 | // WebTransport priority as defined in | 
 | // https://w3c.github.io/webtransport/#webtransportsendstream-write | 
 | // The rules are as follows: | 
 | // - Streams with the same priority are handled in FIFO order. | 
 | // - Streams with the same group_id but different send_order are handled | 
 | //   strictly in order. | 
 | // - Different group_ids are handled in the FIFO order. | 
 | using SendGroupId = uint32_t; | 
 | using SendOrder = int64_t; | 
 | struct QUICHE_EXPORT StreamPriority { | 
 |   SendGroupId send_group_id = 0; | 
 |   SendOrder send_order = 0; | 
 |  | 
 |   bool operator==(const StreamPriority& other) const { | 
 |     return send_group_id == other.send_group_id && | 
 |            send_order == other.send_order; | 
 |   } | 
 | }; | 
 |  | 
 | // An outcome of a datagram send call. | 
 | enum class DatagramStatusCode { | 
 |   // Datagram has been successfully sent or placed into the datagram queue. | 
 |   kSuccess, | 
 |   // Datagram has not been sent since the underlying QUIC connection is blocked | 
 |   // by the congestion control.  Note that this can only happen if the queue is | 
 |   // full. | 
 |   kBlocked, | 
 |   // Datagram has not been sent since it is too large to fit into a single | 
 |   // UDP packet. | 
 |   kTooBig, | 
 |   // An unspecified internal error. | 
 |   kInternalError, | 
 | }; | 
 |  | 
 | // An outcome of a datagram send call, in both enum and human-readable form. | 
 | struct QUICHE_EXPORT DatagramStatus { | 
 |   explicit DatagramStatus(DatagramStatusCode code, std::string error_message) | 
 |       : code(code), error_message(std::move(error_message)) {} | 
 |  | 
 |   DatagramStatusCode code; | 
 |   std::string error_message; | 
 | }; | 
 |  | 
 | enum class StreamType { | 
 |   kUnidirectional, | 
 |   kBidirectional, | 
 | }; | 
 |  | 
 | // Based on | 
 | // https://w3c.github.io/webtransport/#dictdef-webtransportdatagramstats. | 
 | struct QUICHE_EXPORT DatagramStats { | 
 |   uint64_t expired_outgoing; | 
 |   uint64_t lost_outgoing; | 
 |  | 
 |   // droppedIncoming is not present, since in the C++ API, we immediately | 
 |   // deliver datagrams via callback, meaning there is no queue where things | 
 |   // would be dropped. | 
 | }; | 
 |  | 
 | // Based on https://w3c.github.io/webtransport/#web-transport-stats | 
 | // Note that this is currently not a complete implementation of that API, as | 
 | // some of those still need to be clarified in | 
 | // https://github.com/w3c/webtransport/issues/537 | 
 | struct QUICHE_EXPORT SessionStats { | 
 |   absl::Duration min_rtt; | 
 |   absl::Duration smoothed_rtt; | 
 |   absl::Duration rtt_variation; | 
 |  | 
 |   uint64_t estimated_send_rate_bps;  // In bits per second. | 
 |  | 
 |   DatagramStats datagram_stats; | 
 | }; | 
 |  | 
 | // The stream visitor is an application-provided object that gets notified about | 
 | // events related to a WebTransport stream.  The visitor object is owned by the | 
 | // stream itself, meaning that if the stream is ever fully closed, the visitor | 
 | // will be garbage-collected. | 
 | class QUICHE_EXPORT StreamVisitor : public quiche::ReadStreamVisitor, | 
 |                                     public quiche::WriteStreamVisitor { | 
 |  public: | 
 |   virtual ~StreamVisitor() {} | 
 |  | 
 |   // Called when RESET_STREAM is received for the stream. | 
 |   virtual void OnResetStreamReceived(StreamErrorCode error) = 0; | 
 |   // Called when STOP_SENDING is received for the stream. | 
 |   virtual void OnStopSendingReceived(StreamErrorCode error) = 0; | 
 |   // Called when the write side of the stream is closed and all of the data sent | 
 |   // has been acknowledged ("Data Recvd" state of RFC 9000).  Primarily used by | 
 |   // the state machine of the Web API. | 
 |   virtual void OnWriteSideInDataRecvdState() = 0; | 
 | }; | 
 |  | 
 | // A stream (either bidirectional or unidirectional) that is contained within a | 
 | // WebTransport session. | 
 | class QUICHE_EXPORT Stream : public quiche::ReadStream, | 
 |                              public quiche::WriteStream, | 
 |                              public quiche::TerminableStream { | 
 |  public: | 
 |   virtual ~Stream() {} | 
 |  | 
 |   // An ID that is unique within the session.  Those are not exposed to the user | 
 |   // via the web API, but can be used internally for bookkeeping and | 
 |   // diagnostics. | 
 |   virtual StreamId GetStreamId() const = 0; | 
 |  | 
 |   // Resets the read or the write side of the stream with the specified error | 
 |   // code. | 
 |   virtual void ResetWithUserCode(StreamErrorCode error) = 0; | 
 |   virtual void SendStopSending(StreamErrorCode error) = 0; | 
 |  | 
 |   // A general-purpose stream reset method that may be used when a specific | 
 |   // error code is not available. | 
 |   virtual void ResetDueToInternalError() = 0; | 
 |   // If the stream has not been already reset, reset the stream. This is | 
 |   // primarily used in the JavaScript API when the stream object has been | 
 |   // garbage collected. | 
 |   virtual void MaybeResetDueToStreamObjectGone() = 0; | 
 |  | 
 |   // Sets the send group and the send order of the stream as defined in | 
 |   // https://w3c.github.io/webtransport/#dictdef-webtransportsendstreamoptions | 
 |   virtual void SetPriority(const StreamPriority& priority) = 0; | 
 |  | 
 |   virtual StreamVisitor* visitor() = 0; | 
 |   virtual void SetVisitor(std::unique_ptr<StreamVisitor> visitor) = 0; | 
 | }; | 
 |  | 
 | // Visitor that gets notified about events related to a WebTransport session. | 
 | class QUICHE_EXPORT SessionVisitor { | 
 |  public: | 
 |   virtual ~SessionVisitor() {} | 
 |  | 
 |   // Notifies the visitor when the session is ready to exchange application | 
 |   // data. | 
 |   virtual void OnSessionReady() = 0; | 
 |  | 
 |   // Notifies the visitor when the session has been closed. | 
 |   virtual void OnSessionClosed(SessionErrorCode error_code, | 
 |                                const std::string& error_message) = 0; | 
 |  | 
 |   // Notifies the visitor when a new stream has been received.  The stream in | 
 |   // question can be retrieved using AcceptIncomingBidirectionalStream() or | 
 |   // AcceptIncomingUnidirectionalStream(). | 
 |   virtual void OnIncomingBidirectionalStreamAvailable() = 0; | 
 |   virtual void OnIncomingUnidirectionalStreamAvailable() = 0; | 
 |  | 
 |   // Notifies the visitor when a new datagram has been received. | 
 |   virtual void OnDatagramReceived(absl::string_view datagram) = 0; | 
 |  | 
 |   // Notifies the visitor that a new outgoing stream can now be created. | 
 |   virtual void OnCanCreateNewOutgoingBidirectionalStream() = 0; | 
 |   virtual void OnCanCreateNewOutgoingUnidirectionalStream() = 0; | 
 | }; | 
 |  | 
 | // An abstract interface for a WebTransport session. | 
 | // | 
 | // *** AN IMPORTANT NOTE ABOUT STREAM LIFETIMES *** | 
 | // Stream objects are managed internally by the underlying QUIC stack, and can | 
 | // go away at any time due to the peer resetting the stream. Because of that, | 
 | // any pointers to the stream objects returned by this class MUST NEVER be | 
 | // retained long-term, except inside the stream visitor (the stream visitor is | 
 | // owned by the stream object). If you need to store a reference to a stream, | 
 | // consider one of the two following options: | 
 | //   (1) store a stream ID, | 
 | //   (2) store a weak pointer to the stream visitor, and then access the stream | 
 | //       via the said visitor (the visitor is guaranteed to be alive as long as | 
 | //       the stream is alive). | 
 | class QUICHE_EXPORT Session { | 
 |  public: | 
 |   virtual ~Session() {} | 
 |  | 
 |   // Closes the WebTransport session in question with the specified |error_code| | 
 |   // and |error_message|. | 
 |   virtual void CloseSession(SessionErrorCode error_code, | 
 |                             absl::string_view error_message) = 0; | 
 |  | 
 |   // 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.  See the class note regarding the lifetime of the returned stream | 
 |   // object. | 
 |   virtual Stream* AcceptIncomingBidirectionalStream() = 0; | 
 |   virtual Stream* AcceptIncomingUnidirectionalStream() = 0; | 
 |  | 
 |   // Returns true if flow control allows opening a new stream. | 
 |   // | 
 |   // IMPORTANT: See the class note regarding the lifetime of the returned stream | 
 |   // object. | 
 |   virtual bool CanOpenNextOutgoingBidirectionalStream() = 0; | 
 |   virtual bool CanOpenNextOutgoingUnidirectionalStream() = 0; | 
 |  | 
 |   // Opens a new WebTransport stream, or returns nullptr if that is not possible | 
 |   // due to flow control.  See the class note regarding the lifetime of the | 
 |   // returned stream object. | 
 |   // | 
 |   // IMPORTANT: See the class note regarding the lifetime of the returned stream | 
 |   // object. | 
 |   virtual Stream* OpenOutgoingBidirectionalStream() = 0; | 
 |   virtual Stream* OpenOutgoingUnidirectionalStream() = 0; | 
 |  | 
 |   // Returns the WebTransport stream with the corresponding ID. | 
 |   // | 
 |   // IMPORTANT: See the class note regarding the lifetime of the returned stream | 
 |   // object. | 
 |   virtual Stream* GetStreamById(StreamId id) = 0; | 
 |  | 
 |   virtual DatagramStatus SendOrQueueDatagram(absl::string_view datagram) = 0; | 
 |   // Returns a conservative estimate of the largest datagram size that the | 
 |   // session would be able to send. | 
 |   virtual uint64_t GetMaxDatagramSize() const = 0; | 
 |   // Sets the largest duration that a datagram can spend in the queue before | 
 |   // being silently dropped. | 
 |   virtual void SetDatagramMaxTimeInQueue(absl::Duration max_time_in_queue) = 0; | 
 |  | 
 |   // Returns stats that generally follow the semantics of W3C WebTransport API. | 
 |   virtual DatagramStats GetDatagramStats() = 0; | 
 |   virtual SessionStats GetSessionStats() = 0; | 
 |  | 
 |   // Sends a DRAIN_WEBTRANSPORT_SESSION capsule or an equivalent signal to the | 
 |   // peer indicating that the session is draining. | 
 |   virtual void NotifySessionDraining() = 0; | 
 |   // Notifies that either the session itself (DRAIN_WEBTRANSPORT_SESSION | 
 |   // capsule), or the underlying connection (HTTP GOAWAY) is being drained by | 
 |   // the peer. | 
 |   virtual void SetOnDraining(quiche::SingleUseCallback<void()> callback) = 0; | 
 |  | 
 |   // Returns the negotiated subprotocol, or std::nullopt, if none was | 
 |   // negotiated. | 
 |   virtual std::optional<std::string> GetNegotiatedSubprotocol() const = 0; | 
 | }; | 
 |  | 
 | }  // namespace webtransport | 
 |  | 
 | #endif  // QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_ |