| // 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 |