| // 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 <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; |
| }; |
| |
| } // namespace webtransport |
| |
| #endif // QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_ |