blob: 85c14c93e577aa42423f941790427680d7b3cc88 [file] [log] [blame]
// Copyright (c) 2023 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_WEB_TRANSPORT_ENCAPSULATED_ENCAPSULATED_WEB_TRANSPORT_H_
#define QUICHE_WEB_TRANSPORT_ENCAPSULATED_ENCAPSULATED_WEB_TRANSPORT_H_
#include <cstdint>
#include <memory>
#include <string>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "quiche/common/capsule.h"
#include "quiche/common/http/http_header_block.h"
#include "quiche/common/platform/api/quiche_export.h"
#include "quiche/common/quiche_buffer_allocator.h"
#include "quiche/common/quiche_callbacks.h"
#include "quiche/common/quiche_circular_deque.h"
#include "quiche/common/quiche_stream.h"
#include "quiche/common/simple_buffer_allocator.h"
#include "quiche/web_transport/web_transport.h"
namespace webtransport {
using FatalErrorCallback = quiche::SingleUseCallback<void(absl::string_view)>;
// Implementation of the WebTransport over HTTP/2 protocol; works over any
// arbitrary bidirectional bytestream that can be prefixed with HTTP headers.
// Specification: https://datatracker.ietf.org/doc/draft-ietf-webtrans-http2/
class QUICHE_EXPORT EncapsulatedSession
: public webtransport::Session,
public quiche::WriteStreamVisitor,
public quiche::ReadStreamVisitor,
public quiche::CapsuleParser::Visitor {
public:
// The state machine of the transport.
enum State {
// The transport object has been created, but
// InitializeClient/InitializeServer has not been called yet.
kUninitialized,
// The client has sent its own headers, but haven't received a response yet.
kWaitingForHeaders,
// Both the client and the server headers have been processed.
kSessionOpen,
// The session close has been requested, but the CLOSE capsule hasn't been
// sent yet.
kSessionClosing,
// The session has been closed; no further data will be exchanged.
kSessionClosed,
};
// The `fatal_error_callback` implies that any state related to the session
// should be torn down after it's been called.
EncapsulatedSession(Perspective perspective,
FatalErrorCallback fatal_error_callback);
// WebTransport uses HTTP headers in a similar way to how QUIC uses SETTINGS;
// thus, the headers are necessary to initialize the session.
void InitializeClient(std::unique_ptr<SessionVisitor> visitor,
quiche::HttpHeaderBlock& outgoing_headers,
quiche::WriteStream* writer,
quiche::ReadStream* reader);
void InitializeServer(std::unique_ptr<SessionVisitor> visitor,
const quiche::HttpHeaderBlock& incoming_headers,
quiche::HttpHeaderBlock& outgoing_headers,
quiche::WriteStream* writer,
quiche::ReadStream* reader);
void ProcessIncomingServerHeaders(const quiche::HttpHeaderBlock& headers);
// webtransport::Session implementation.
void CloseSession(SessionErrorCode error_code,
absl::string_view error_message) override;
Stream* AcceptIncomingBidirectionalStream() override;
Stream* AcceptIncomingUnidirectionalStream() override;
bool CanOpenNextOutgoingBidirectionalStream() override;
bool CanOpenNextOutgoingUnidirectionalStream() override;
Stream* OpenOutgoingBidirectionalStream() override;
Stream* OpenOutgoingUnidirectionalStream() override;
DatagramStatus SendOrQueueDatagram(absl::string_view datagram) override;
uint64_t GetMaxDatagramSize() const override;
void SetDatagramMaxTimeInQueue(absl::Duration max_time_in_queue) override;
Stream* GetStreamById(StreamId id) override;
DatagramStats GetDatagramStats() override;
SessionStats GetSessionStats() override;
void NotifySessionDraining() override;
void SetOnDraining(quiche::SingleUseCallback<void()> callback) override;
// quiche::WriteStreamVisitor implementation.
void OnCanWrite() override;
// quiche::ReadStreamVisitor implementation.
void OnCanRead() override;
// quiche::CapsuleParser::Visitor implementation.
bool OnCapsule(const quiche::Capsule& capsule) override;
void OnCapsuleParseFailure(absl::string_view error_message) override;
State state() const { return state_; }
private:
struct BufferedClose {
SessionErrorCode error_code = 0;
std::string error_message;
};
Perspective perspective_;
State state_ = kUninitialized;
std::unique_ptr<SessionVisitor> visitor_ = nullptr;
FatalErrorCallback fatal_error_callback_;
quiche::SingleUseCallback<void()> draining_callback_;
quiche::WriteStream* writer_ = nullptr; // Not owned.
quiche::ReadStream* reader_ = nullptr; // Not owned.
quiche::QuicheBufferAllocator* allocator_ =
quiche::SimpleBufferAllocator::Get();
quiche::CapsuleParser capsule_parser_;
bool session_close_notified_ = false;
bool fin_sent_ = false;
BufferedClose buffered_session_close_;
quiche::QuicheCircularDeque<quiche::QuicheBuffer> control_capsule_queue_;
void OpenSession();
absl::Status SendFin(absl::string_view data);
void OnSessionClosed(SessionErrorCode error_code,
const std::string& error_message);
void OnFatalError(absl::string_view error_message);
void OnWriteError(absl::Status error);
};
} // namespace webtransport
#endif // QUICHE_WEB_TRANSPORT_ENCAPSULATED_ENCAPSULATED_WEB_TRANSPORT_H_