blob: 542b9418a7367487637705d7e872d98504d964d5 [file] [log] [blame]
// Copyright 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.
#ifndef QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_
#define QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_
#include <memory>
#include "absl/base/attributes.h"
#include "absl/container/flat_hash_set.h"
#include "absl/types/optional.h"
#include "quic/core/http/quic_spdy_session.h"
#include "quic/core/http/web_transport_stream_adapter.h"
#include "quic/core/quic_error_codes.h"
#include "quic/core/quic_stream.h"
#include "quic/core/quic_types.h"
#include "quic/core/web_transport_interface.h"
#include "spdy/core/spdy_header_block.h"
namespace quic {
class QuicSpdySession;
class QuicSpdyStream;
enum class WebTransportHttp3RejectionReason {
kNone,
kNoStatusCode,
kWrongStatusCode,
kMissingDraftVersion,
kUnsupportedDraftVersion,
};
// A session of WebTransport over HTTP/3. The session is owned by
// QuicSpdyStream object for the CONNECT stream that established it.
//
// WebTransport over HTTP/3 specification:
// <https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3>
class QUIC_EXPORT_PRIVATE WebTransportHttp3
: public WebTransportSession,
public QuicSpdyStream::Http3DatagramRegistrationVisitor,
public QuicSpdyStream::Http3DatagramVisitor {
public:
WebTransportHttp3(QuicSpdySession* session, QuicSpdyStream* connect_stream,
WebTransportSessionId id,
bool attempt_to_use_datagram_contexts);
void HeadersReceived(const spdy::SpdyHeaderBlock& headers);
void SetVisitor(std::unique_ptr<WebTransportVisitor> visitor) {
visitor_ = std::move(visitor);
}
WebTransportSessionId id() { return id_; }
bool ready() { return ready_; }
absl::optional<QuicDatagramContextId> context_id() const {
return context_id_;
}
void AssociateStream(QuicStreamId stream_id);
void OnStreamClosed(QuicStreamId stream_id) { streams_.erase(stream_id); }
void OnConnectStreamClosing();
size_t NumberOfAssociatedStreams() { return streams_.size(); }
void CloseSession(WebTransportSessionError error_code,
absl::string_view error_message) override;
void OnCloseReceived(WebTransportSessionError error_code,
absl::string_view error_message);
void OnConnectStreamFinReceived();
// It is legal for WebTransport to be closed without a
// CLOSE_WEBTRANSPORT_SESSION capsule. We always send a capsule, but we still
// need to ensure we handle this case correctly.
void CloseSessionWithFinOnlyForTests();
// 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.
WebTransportStream* AcceptIncomingBidirectionalStream() override;
WebTransportStream* AcceptIncomingUnidirectionalStream() override;
bool CanOpenNextOutgoingBidirectionalStream() override;
bool CanOpenNextOutgoingUnidirectionalStream() override;
WebTransportStream* OpenOutgoingBidirectionalStream() override;
WebTransportStream* OpenOutgoingUnidirectionalStream() override;
MessageStatus SendOrQueueDatagram(QuicMemSlice datagram) override;
QuicByteCount GetMaxDatagramSize() const override;
void SetDatagramMaxTimeInQueue(QuicTime::Delta max_time_in_queue) override;
// From QuicSpdyStream::Http3DatagramVisitor.
void OnHttp3Datagram(QuicStreamId stream_id,
absl::optional<QuicDatagramContextId> context_id,
absl::string_view payload) override;
// From QuicSpdyStream::Http3DatagramRegistrationVisitor.
void OnContextReceived(QuicStreamId stream_id,
absl::optional<QuicDatagramContextId> context_id,
DatagramFormatType format_type,
absl::string_view format_additional_data) override;
void OnContextClosed(QuicStreamId stream_id,
absl::optional<QuicDatagramContextId> context_id,
ContextCloseCode close_code,
absl::string_view close_details) override;
bool close_received() const { return close_received_; }
WebTransportHttp3RejectionReason rejection_reason() const {
return rejection_reason_;
}
private:
// Notifies the visitor that the connection has been closed. Ensures that the
// visitor is only ever called once.
void MaybeNotifyClose();
QuicSpdySession* const session_; // Unowned.
QuicSpdyStream* const connect_stream_; // Unowned.
const WebTransportSessionId id_;
absl::optional<QuicDatagramContextId> context_id_;
// |ready_| is set to true when the peer has seen both sets of headers.
bool ready_ = false;
// Whether we know which |context_id_| to use. On the client this is always
// true, and on the server it becomes true when we receive a context
// registration capsule.
bool context_is_known_ = false;
// Whether |context_id_| is currently registered with |connect_stream_|.
bool context_currently_registered_ = false;
std::unique_ptr<WebTransportVisitor> visitor_;
absl::flat_hash_set<QuicStreamId> streams_;
quiche::QuicheCircularDeque<QuicStreamId> incoming_bidirectional_streams_;
quiche::QuicheCircularDeque<QuicStreamId> incoming_unidirectional_streams_;
bool close_sent_ = false;
bool close_received_ = false;
bool close_notified_ = false;
WebTransportHttp3RejectionReason rejection_reason_ =
WebTransportHttp3RejectionReason::kNone;
// Those are set to default values, which are used if the session is not
// closed cleanly using an appropriate capsule.
WebTransportSessionError error_code_ = 0;
std::string error_message_ = "";
};
class QUIC_EXPORT_PRIVATE WebTransportHttp3UnidirectionalStream
: public QuicStream {
public:
// Incoming stream.
WebTransportHttp3UnidirectionalStream(PendingStream* pending,
QuicSpdySession* session);
// Outgoing stream.
WebTransportHttp3UnidirectionalStream(QuicStreamId id,
QuicSpdySession* session,
WebTransportSessionId session_id);
// Sends the stream type and the session ID on the stream.
void WritePreamble();
// Implementation of QuicStream.
void OnDataAvailable() override;
void OnCanWriteNewData() override;
void OnClose() override;
void OnStreamReset(const QuicRstStreamFrame& frame) override;
bool OnStopSending(QuicResetStreamError error) override;
void OnWriteSideInDataRecvdState() override;
WebTransportStream* interface() { return &adapter_; }
void SetUnblocked() { sequencer()->SetUnblocked(); }
private:
QuicSpdySession* session_;
WebTransportStreamAdapter adapter_;
absl::optional<WebTransportSessionId> session_id_;
bool needs_to_send_preamble_;
bool ReadSessionId();
// Closes the stream if all of the data has been received.
void MaybeCloseIncompleteStream();
};
// Remaps HTTP/3 error code into a WebTransport error code. Returns nullopt if
// the provided code is outside of valid range.
QUIC_EXPORT_PRIVATE absl::optional<WebTransportStreamError>
Http3ErrorToWebTransport(uint64_t http3_error_code);
// Same as above, but returns default error value (zero) when none could be
// mapped.
QUIC_EXPORT_PRIVATE WebTransportStreamError
Http3ErrorToWebTransportOrDefault(uint64_t http3_error_code);
// Remaps WebTransport error code into an HTTP/3 error code.
QUIC_EXPORT_PRIVATE uint64_t
WebTransportErrorToHttp3(WebTransportStreamError webtransport_error_code);
} // namespace quic
#endif // QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_