blob: 223c1bce8f1a8558c08297533db69eb69e47bd86 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/quic/qbone/test_tools/qbone_basic_quic_server_handler.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/nullability.h"
#include "absl/status/status.h"
#include "absl/types/span.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_stream_sequencer.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/qbone/qbone_constants.h"
#include "quiche/quic/qbone/test_tools/basic_quic_server.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_status_utils.h"
namespace quic::test {
class QboneBasicQuicServerHandler::SequencerReader {
public:
explicit SequencerReader(const QuicStreamSequencer* absl_nonnull sequencer
ABSL_ATTRIBUTE_LIFETIME_BOUND)
: sequencer_(sequencer), offset_(sequencer->NumBytesConsumed()) {}
bool Read(void* buffer, int num_bytes) {
if (sequencer_->ReadableBytes() <
offset_ - sequencer_->NumBytesConsumed() + num_bytes) {
return false;
}
QuicStreamOffset new_offset = offset_;
QuicStreamOffset end_offset = offset_ + num_bytes;
iovec iov;
while (new_offset < end_offset) {
QUICHE_CHECK(sequencer_->PeekRegion(new_offset, &iov));
QUICHE_CHECK_LE(iov.iov_len, std::numeric_limits<int>::max());
int bytes_to_copy = std::min<int>(end_offset - new_offset, iov.iov_len);
memcpy(reinterpret_cast<std::byte*>(buffer) + (new_offset - offset_),
iov.iov_base, bytes_to_copy);
new_offset += bytes_to_copy;
}
QUICHE_CHECK_EQ(new_offset, end_offset);
offset_ = new_offset;
return true;
}
const QuicStreamSequencer* absl_nonnull sequencer_;
QuicStreamOffset offset_;
};
QboneBasicQuicServerHandler::QboneBasicQuicServerHandler(
BasicQuicServer* absl_nonnull server)
: server_(server) {}
// Lots of unnecessary copying here, but control messages are not performance-
// sensitive.
absl::Status QboneBasicQuicServerHandler::SendControlMessage(
QuicConnectionId server_connection_id, const QboneClientRequest& request) {
QUICHE_CHECK(connections_.contains(server_connection_id))
<< "No control stream for connection " << server_connection_id;
QUICHE_CHECK_LE(request.ByteSizeLong(), std::numeric_limits<uint16_t>::max());
uint16_t serialized_size = static_cast<uint16_t>(request.ByteSizeLong());
std::vector<std::byte> buffer(sizeof(serialized_size) +
request.ByteSizeLong());
// Size-prefix the serialized message. (Not network byte order.)
memcpy(buffer.data(), &serialized_size, sizeof(serialized_size));
if (!request.SerializeToArray(buffer.data() + sizeof(serialized_size),
serialized_size)) {
return absl::InternalError("Failed to serialize QboneClientRequest");
}
QUICHE_ASSIGN_OR_RETURN(
int bytes_written,
server_->SendStreamData(
server_connection_id,
QboneConstants::GetControlStreamId(QUIC_VERSION_IETF_RFC_V1),
buffer));
QUICHE_CHECK_EQ(bytes_written, buffer.size());
return absl::OkStatus();
}
absl::Status QboneBasicQuicServerHandler::SendTunnelPacket(
QuicConnectionId server_connection_id, absl::Span<const std::byte> data) {
QUICHE_CHECK(connections_.contains(server_connection_id))
<< "No control stream for connection " << server_connection_id;
return server_->SendDatagram(server_connection_id, data);
}
void QboneBasicQuicServerHandler::OnSessionEnd(
QuicConnectionId server_connection_id) {
if (connections_.erase(server_connection_id) == 1) {
OnConnectionClosed(server_connection_id);
}
}
// Assume only QUIC_VERSION_IETF_RFC_V1 is used and validate the appropriate
// control stream ID for that QUIC version. Support only the control stream as
// the only stream in the connection.
bool QboneBasicQuicServerHandler::OnNewStream(
QuicConnectionId server_connection_id, QuicStreamId stream_id) {
auto [it, inserted] = connections_.insert(server_connection_id);
QUICHE_CHECK(inserted) << "Unexpected new stream for connection "
<< server_connection_id;
QUICHE_CHECK_EQ(stream_id,
QboneConstants::GetControlStreamId(QUIC_VERSION_IETF_RFC_V1))
<< "Server received stream with unexpected ID " << stream_id;
OnNewConnection(server_connection_id);
return true;
}
int QboneBasicQuicServerHandler::OnStreamDataAvailable(
QuicConnectionId server_connection_id, QuicStreamId stream_id,
const QuicStreamSequencer& data_sequencer) {
QUICHE_CHECK(connections_.contains(server_connection_id));
QUICHE_CHECK_EQ(stream_id,
QboneConstants::GetControlStreamId(QUIC_VERSION_IETF_RFC_V1));
SequencerReader reader(&data_sequencer);
int total_consumed = 0;
while (true) {
int consumed =
ParseAndHandleSingleControlRequest(server_connection_id, reader);
if (consumed == 0) {
break;
}
total_consumed += consumed;
}
return total_consumed;
}
void QboneBasicQuicServerHandler::OnDatagramReceived(
QuicConnectionId server_connection_id, absl::Span<const std::byte> data) {
QUICHE_CHECK(connections_.contains(server_connection_id))
<< "Unexpected datagram for connection " << server_connection_id
<< " before creating control stream.";
OnTunnelPacket(server_connection_id, data);
}
// Lots of unnecessary copying here (and wastefully throw it away on incomplete
// messages), but control messages are not performance-sensitive.
int QboneBasicQuicServerHandler::ParseAndHandleSingleControlRequest(
QuicConnectionId server_connection_id, SequencerReader& reader) {
// Expect size-prepended (uint16_t) serialized QboneServerRequest proto
// messages. (Not network byte order.)
uint16_t request_size;
if (!reader.Read(&request_size, sizeof(request_size))) {
// Wait for more data.
return 0;
}
std::vector<std::byte> raw_proto(request_size);
if (!reader.Read(raw_proto.data(), request_size)) {
// Wait for more data.
return 0;
}
QboneServerRequest request;
if (request.ParseFromArray(raw_proto.data(), raw_proto.size())) {
OnControlMessageReceived(server_connection_id, request);
} else {
// Could not parse proto
OnControlMessageParseError();
}
return sizeof(request_size) + request_size;
}
} // namespace quic::test