blob: c008801a97db6f1c480e18734b25148c13a9ba90 [file] [log] [blame]
// 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;
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_