blob: 2b767cac8e0e128246a3012eb84e70711ae6847a [file] [log] [blame]
// Copyright 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.
#include "quiche/quic/core/quic_generic_session.h"
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include "quiche/quic/core/http/web_transport_stream_adapter.h"
#include "quiche/quic/core/quic_crypto_client_stream.h"
#include "quiche/quic/core/quic_session.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/common/simple_buffer_allocator.h"
#include "quiche/web_transport/web_transport.h"
namespace quic {
namespace {
class NoOpProofHandler : public QuicCryptoClientStream::ProofHandler {
public:
void OnProofValid(const QuicCryptoClientConfig::CachedState&) override {}
void OnProofVerifyDetailsAvailable(const ProofVerifyDetails&) override {}
};
class NoOpServerCryptoHelper : public QuicCryptoServerStreamBase::Helper {
public:
bool CanAcceptClientHello(const CryptoHandshakeMessage& /*message*/,
const QuicSocketAddress& /*client_address*/,
const QuicSocketAddress& /*peer_address*/,
const QuicSocketAddress& /*self_address*/,
std::string* /*error_details*/) const override {
return true;
}
};
} // namespace
ParsedQuicVersionVector GetQuicVersionsForGenericSession() {
return {ParsedQuicVersion::RFCv1()};
}
// QuicGenericStream is a stream that provides a general-purpose implementation
// of a webtransport::Stream interface.
class QUICHE_EXPORT QuicGenericStream : public QuicStream {
public:
QuicGenericStream(QuicStreamId id, QuicSession* session)
: QuicStream(id, session, /*is_static=*/false,
QuicUtils::GetStreamType(
id, session->connection()->perspective(),
session->IsIncomingStream(id), session->version())),
adapter_(session, this, sequencer()) {}
WebTransportStreamAdapter* adapter() { return &adapter_; }
// QuicSession method implementations.
void OnDataAvailable() override { adapter_.OnDataAvailable(); }
void OnCanWriteNewData() override { adapter_.OnCanWriteNewData(); }
private:
WebTransportStreamAdapter adapter_;
};
QuicGenericSessionBase::QuicGenericSessionBase(
QuicConnection* connection, bool owns_connection, Visitor* owner,
const QuicConfig& config, std::string alpn, WebTransportVisitor* visitor,
bool owns_visitor,
std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer)
: QuicSession(connection, owner, config, GetQuicVersionsForGenericSession(),
/*num_expected_unidirectional_static_streams=*/0,
std::move(datagram_observer)),
alpn_(std::move(alpn)),
visitor_(visitor),
owns_connection_(owns_connection),
owns_visitor_(owns_visitor) {}
QuicGenericSessionBase::~QuicGenericSessionBase() {
if (owns_connection_) {
DeleteConnection();
}
if (owns_visitor_) {
delete visitor_;
visitor_ = nullptr;
}
}
QuicStream* QuicGenericSessionBase::CreateIncomingStream(QuicStreamId id) {
QUIC_DVLOG(1) << "Creating incoming QuicGenricStream " << id;
QuicGenericStream* stream = CreateStream(id);
if (stream->type() == BIDIRECTIONAL) {
incoming_bidirectional_streams_.push_back(id);
visitor_->OnIncomingBidirectionalStreamAvailable();
} else {
incoming_unidirectional_streams_.push_back(id);
visitor_->OnIncomingUnidirectionalStreamAvailable();
}
return stream;
}
void QuicGenericSessionBase::OnTlsHandshakeComplete() {
QuicSession::OnTlsHandshakeComplete();
visitor_->OnSessionReady();
}
webtransport::Stream*
QuicGenericSessionBase::AcceptIncomingBidirectionalStream() {
while (!incoming_bidirectional_streams_.empty()) {
webtransport::Stream* stream =
GetStreamById(incoming_bidirectional_streams_.front());
incoming_bidirectional_streams_.pop_front();
if (stream != nullptr) {
return stream;
}
}
return nullptr;
}
webtransport::Stream*
QuicGenericSessionBase::AcceptIncomingUnidirectionalStream() {
while (!incoming_unidirectional_streams_.empty()) {
webtransport::Stream* stream =
GetStreamById(incoming_unidirectional_streams_.front());
incoming_unidirectional_streams_.pop_front();
if (stream != nullptr) {
return stream;
}
}
return nullptr;
}
webtransport::Stream*
QuicGenericSessionBase::OpenOutgoingBidirectionalStream() {
if (!CanOpenNextOutgoingBidirectionalStream()) {
QUIC_BUG(QuicGenericSessionBase_flow_control_violation_bidi)
<< "Attempted to open a stream in violation of flow control";
return nullptr;
}
return CreateStream(GetNextOutgoingBidirectionalStreamId())->adapter();
}
webtransport::Stream*
QuicGenericSessionBase::OpenOutgoingUnidirectionalStream() {
if (!CanOpenNextOutgoingUnidirectionalStream()) {
QUIC_BUG(QuicGenericSessionBase_flow_control_violation_unidi)
<< "Attempted to open a stream in violation of flow control";
return nullptr;
}
return CreateStream(GetNextOutgoingUnidirectionalStreamId())->adapter();
}
QuicGenericStream* QuicGenericSessionBase::CreateStream(QuicStreamId id) {
auto stream = std::make_unique<QuicGenericStream>(id, this);
QuicGenericStream* stream_ptr = stream.get();
ActivateStream(std::move(stream));
return stream_ptr;
}
void QuicGenericSessionBase::OnMessageReceived(absl::string_view message) {
visitor_->OnDatagramReceived(message);
}
void QuicGenericSessionBase::OnCanCreateNewOutgoingStream(bool unidirectional) {
if (unidirectional) {
visitor_->OnCanCreateNewOutgoingUnidirectionalStream();
} else {
visitor_->OnCanCreateNewOutgoingBidirectionalStream();
}
}
webtransport::Stream* QuicGenericSessionBase::GetStreamById(
webtransport::StreamId id) {
QuicStream* stream = GetActiveStream(id);
if (stream == nullptr) {
return nullptr;
}
return static_cast<QuicGenericStream*>(stream)->adapter();
}
webtransport::DatagramStatus QuicGenericSessionBase::SendOrQueueDatagram(
absl::string_view datagram) {
quiche::QuicheBuffer buffer = quiche::QuicheBuffer::Copy(
quiche::SimpleBufferAllocator::Get(), datagram);
return MessageStatusToWebTransportStatus(
datagram_queue()->SendOrQueueDatagram(
quiche::QuicheMemSlice(std::move(buffer))));
}
void QuicGenericSessionBase::OnConnectionClosed(
const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) {
QuicSession::OnConnectionClosed(frame, source);
visitor_->OnSessionClosed(static_cast<webtransport::SessionErrorCode>(
frame.transport_close_frame_type),
frame.error_details);
}
QuicGenericClientSession::QuicGenericClientSession(
QuicConnection* connection, bool owns_connection, Visitor* owner,
const QuicConfig& config, std::string host, uint16_t port, std::string alpn,
webtransport::SessionVisitor* visitor, bool owns_visitor,
std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer,
QuicCryptoClientConfig* crypto_config)
: QuicGenericSessionBase(connection, owns_connection, owner, config,
std::move(alpn), visitor, owns_visitor,
std::move(datagram_observer)) {
static NoOpProofHandler* handler = new NoOpProofHandler();
crypto_stream_ = std::make_unique<QuicCryptoClientStream>(
QuicServerId(std::move(host), port), this,
crypto_config->proof_verifier()->CreateDefaultContext(), crypto_config,
/*proof_handler=*/handler, /*has_application_state=*/false);
}
QuicGenericClientSession::QuicGenericClientSession(
QuicConnection* connection, bool owns_connection, Visitor* owner,
const QuicConfig& config, std::string host, uint16_t port, std::string alpn,
CreateWebTransportSessionVisitorCallback create_visitor_callback,
std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer,
QuicCryptoClientConfig* crypto_config)
: QuicGenericClientSession(
connection, owns_connection, owner, config, std::move(host), port,
std::move(alpn), std::move(create_visitor_callback)(*this).release(),
/*owns_visitor=*/true, std::move(datagram_observer), crypto_config) {}
QuicGenericServerSession::QuicGenericServerSession(
QuicConnection* connection, bool owns_connection, Visitor* owner,
const QuicConfig& config, std::string alpn,
webtransport::SessionVisitor* visitor, bool owns_visitor,
std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer,
const QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache)
: QuicGenericSessionBase(connection, owns_connection, owner, config,
std::move(alpn), visitor, owns_visitor,
std::move(datagram_observer)) {
static NoOpServerCryptoHelper* helper = new NoOpServerCryptoHelper();
crypto_stream_ = CreateCryptoServerStream(
crypto_config, compressed_certs_cache, this, helper);
}
QuicGenericServerSession::QuicGenericServerSession(
QuicConnection* connection, bool owns_connection, Visitor* owner,
const QuicConfig& config, std::string alpn,
CreateWebTransportSessionVisitorCallback create_visitor_callback,
std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer,
const QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache)
: QuicGenericServerSession(
connection, owns_connection, owner, config, std::move(alpn),
std::move(create_visitor_callback)(*this).release(),
/*owns_visitor=*/true, std::move(datagram_observer), crypto_config,
compressed_certs_cache) {}
} // namespace quic