Split QuicEndpoint into QuicEndpoint and QuicEndpointBase. QuicEndpointBase handles network I/O integration, while QuicEndpoint handles writing test data into the session. This allows running custom sessions with meaningful semantics using the QUIC simulator. gfe-relnote: n/a (test-only change) PiperOrigin-RevId: 276154770 Change-Id: Iff8778578cb484182fd28ea51d263a636651b943
diff --git a/quic/test_tools/simulator/quic_endpoint_base.cc b/quic/test_tools/simulator/quic_endpoint_base.cc new file mode 100644 index 0000000..537a947 --- /dev/null +++ b/quic/test_tools/simulator/quic_endpoint_base.cc
@@ -0,0 +1,222 @@ +// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h" + +#include <memory> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_output.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" + +namespace quic { +namespace simulator { + +// Takes a SHA-1 hash of the name and converts it into five 32-bit integers. +static std::vector<uint32_t> HashNameIntoFive32BitIntegers(std::string name) { + const std::string hash = test::Sha1Hash(name); + + std::vector<uint32_t> output; + uint32_t current_number = 0; + for (size_t i = 0; i < hash.size(); i++) { + current_number = (current_number << 8) + hash[i]; + if (i % 4 == 3) { + output.push_back(i); + current_number = 0; + } + } + + return output; +} + +QuicSocketAddress GetAddressFromName(std::string name) { + const std::vector<uint32_t> hash = HashNameIntoFive32BitIntegers(name); + + // Generate a random port between 1025 and 65535. + const uint16_t port = 1025 + hash[0] % (65535 - 1025 + 1); + + // Generate a random 10.x.x.x address, where x is between 1 and 254. + std::string ip_address{"\xa\0\0\0", 4}; + for (size_t i = 1; i < 4; i++) { + ip_address[i] = 1 + hash[i] % 254; + } + QuicIpAddress host; + host.FromPackedString(ip_address.c_str(), ip_address.length()); + return QuicSocketAddress(host, port); +} + +QuicEndpointBase::QuicEndpointBase(Simulator* simulator, + std::string name, + std::string peer_name) + : Endpoint(simulator, name), + peer_name_(peer_name), + writer_(this), + nic_tx_queue_(simulator, + QuicStringPrintf("%s (TX Queue)", name.c_str()), + kMaxOutgoingPacketSize * kTxQueueSize), + connection_(nullptr), + write_blocked_count_(0), + drop_next_packet_(false) { + nic_tx_queue_.set_listener_interface(this); +} + +QuicEndpointBase::~QuicEndpointBase() { + if (trace_visitor_ != nullptr) { + const char* perspective_prefix = + connection_->perspective() == Perspective::IS_CLIENT ? "C" : "S"; + + std::string identifier = + QuicStrCat(perspective_prefix, connection_->connection_id().ToString()); + QuicRecordTestOutput(identifier, + trace_visitor_->trace()->SerializeAsString()); + } +} + +void QuicEndpointBase::DropNextIncomingPacket() { + drop_next_packet_ = true; +} + +void QuicEndpointBase::RecordTrace() { + trace_visitor_ = std::make_unique<QuicTraceVisitor>(connection_.get()); + connection_->set_debug_visitor(trace_visitor_.get()); +} + +void QuicEndpointBase::AcceptPacket(std::unique_ptr<Packet> packet) { + if (packet->destination != name_) { + return; + } + if (drop_next_packet_) { + drop_next_packet_ = false; + return; + } + + QuicReceivedPacket received_packet(packet->contents.data(), + packet->contents.size(), clock_->Now()); + connection_->ProcessUdpPacket(connection_->self_address(), + connection_->peer_address(), received_packet); +} + +UnconstrainedPortInterface* QuicEndpointBase::GetRxPort() { + return this; +} + +void QuicEndpointBase::SetTxPort(ConstrainedPortInterface* port) { + // Any egress done by the endpoint is actually handled by a queue on an NIC. + nic_tx_queue_.set_tx_port(port); +} + +void QuicEndpointBase::OnPacketDequeued() { + if (writer_.IsWriteBlocked() && + (nic_tx_queue_.capacity() - nic_tx_queue_.bytes_queued()) >= + kMaxOutgoingPacketSize) { + writer_.SetWritable(); + connection_->OnCanWrite(); + } +} + +QuicEndpointBase::Writer::Writer(QuicEndpointBase* endpoint) + : endpoint_(endpoint), is_blocked_(false) {} + +QuicEndpointBase::Writer::~Writer() {} + +WriteResult QuicEndpointBase::Writer::WritePacket( + const char* buffer, + size_t buf_len, + const QuicIpAddress& /*self_address*/, + const QuicSocketAddress& /*peer_address*/, + PerPacketOptions* options) { + DCHECK(!IsWriteBlocked()); + DCHECK(options == nullptr); + DCHECK(buf_len <= kMaxOutgoingPacketSize); + + // Instead of losing a packet, become write-blocked when the egress queue is + // full. + if (endpoint_->nic_tx_queue_.packets_queued() > kTxQueueSize) { + is_blocked_ = true; + endpoint_->write_blocked_count_++; + return WriteResult(WRITE_STATUS_BLOCKED, 0); + } + + auto packet = std::make_unique<Packet>(); + packet->source = endpoint_->name(); + packet->destination = endpoint_->peer_name_; + packet->tx_timestamp = endpoint_->clock_->Now(); + + packet->contents = std::string(buffer, buf_len); + packet->size = buf_len; + + endpoint_->nic_tx_queue_.AcceptPacket(std::move(packet)); + + return WriteResult(WRITE_STATUS_OK, buf_len); +} + +bool QuicEndpointBase::Writer::IsWriteBlocked() const { + return is_blocked_; +} + +void QuicEndpointBase::Writer::SetWritable() { + is_blocked_ = false; +} + +QuicByteCount QuicEndpointBase::Writer::GetMaxPacketSize( + const QuicSocketAddress& /*peer_address*/) const { + return kMaxOutgoingPacketSize; +} + +bool QuicEndpointBase::Writer::SupportsReleaseTime() const { + return false; +} + +bool QuicEndpointBase::Writer::IsBatchMode() const { + return false; +} + +char* QuicEndpointBase::Writer::GetNextWriteLocation( + const QuicIpAddress& /*self_address*/, + const QuicSocketAddress& /*peer_address*/) { + return nullptr; +} + +WriteResult QuicEndpointBase::Writer::Flush() { + return WriteResult(WRITE_STATUS_OK, 0); +} + +QuicEndpointMultiplexer::QuicEndpointMultiplexer( + std::string name, + const std::vector<QuicEndpointBase*>& endpoints) + : Endpoint((*endpoints.begin())->simulator(), name) { + for (QuicEndpointBase* endpoint : endpoints) { + mapping_.insert(std::make_pair(endpoint->name(), endpoint)); + } +} + +QuicEndpointMultiplexer::~QuicEndpointMultiplexer() {} + +void QuicEndpointMultiplexer::AcceptPacket(std::unique_ptr<Packet> packet) { + auto key_value_pair_it = mapping_.find(packet->destination); + if (key_value_pair_it == mapping_.end()) { + return; + } + + key_value_pair_it->second->GetRxPort()->AcceptPacket(std::move(packet)); +} +UnconstrainedPortInterface* QuicEndpointMultiplexer::GetRxPort() { + return this; +} +void QuicEndpointMultiplexer::SetTxPort(ConstrainedPortInterface* port) { + for (auto& key_value_pair : mapping_) { + key_value_pair.second->SetTxPort(port); + } +} + +} // namespace simulator +} // namespace quic