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/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc index 75ab6d2..193196f 100644 --- a/quic/core/congestion_control/bbr2_simulator_test.cc +++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -741,7 +741,7 @@ protected: Bbr2MultiSenderTest() { uint64_t first_connection_id = 42; - std::vector<simulator::QuicEndpoint*> receiver_endpoint_pointers; + std::vector<simulator::QuicEndpointBase*> receiver_endpoint_pointers; for (size_t i = 0; i < MultiSenderTopologyParams::kNumLocalLinks; ++i) { std::string sender_name = QuicStrCat("Sender", i + 1); std::string receiver_name = QuicStrCat("Receiver", i + 1);
diff --git a/quic/test_tools/simulator/quic_endpoint.cc b/quic/test_tools/simulator/quic_endpoint.cc index bf84b37..9f50435 100644 --- a/quic/test_tools/simulator/quic_endpoint.cc +++ b/quic/test_tools/simulator/quic_endpoint.cc
@@ -4,6 +4,7 @@ #include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h" +#include <memory> #include <utility> #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" @@ -23,87 +24,41 @@ const QuicByteCount kWriteChunkSize = 128 * 1024; const char kStreamDataContents = 'Q'; -// 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); -} - QuicEndpoint::QuicEndpoint(Simulator* simulator, std::string name, std::string peer_name, Perspective perspective, QuicConnectionId connection_id) - : Endpoint(simulator, name), - peer_name_(peer_name), - writer_(this), - nic_tx_queue_(simulator, - QuicStringPrintf("%s (TX Queue)", name.c_str()), - kMaxOutgoingPacketSize * kTxQueueSize), - connection_(connection_id, - GetAddressFromName(peer_name), - simulator, - simulator->GetAlarmFactory(), - &writer_, - false, - perspective, - ParsedVersionOfIndex(CurrentSupportedVersions(), 0)), + : QuicEndpointBase(simulator, name, peer_name), bytes_to_transfer_(0), bytes_transferred_(0), - write_blocked_count_(0), wrong_data_received_(false), - drop_next_packet_(false), notifier_(nullptr) { - nic_tx_queue_.set_listener_interface(this); - - connection_.SetSelfAddress(GetAddressFromName(name)); - connection_.set_visitor(this); - connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, - std::make_unique<NullEncrypter>(perspective)); - connection_.SetEncrypter(ENCRYPTION_INITIAL, nullptr); - if (connection_.version().KnowsWhichDecrypterToUse()) { - connection_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, - std::make_unique<NullDecrypter>(perspective)); - connection_.RemoveDecrypter(ENCRYPTION_INITIAL); + connection_ = std::make_unique<QuicConnection>( + connection_id, GetAddressFromName(peer_name), simulator, + simulator->GetAlarmFactory(), &writer_, false, perspective, + ParsedVersionOfIndex(CurrentSupportedVersions(), 0)); + connection_->SetSelfAddress(GetAddressFromName(name)); + connection_->set_visitor(this); + connection_->SetEncrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<NullEncrypter>(perspective)); + connection_->SetEncrypter(ENCRYPTION_INITIAL, nullptr); + if (connection_->version().KnowsWhichDecrypterToUse()) { + connection_->InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<NullDecrypter>(perspective)); + connection_->RemoveDecrypter(ENCRYPTION_INITIAL); } else { - connection_.SetDecrypter(ENCRYPTION_FORWARD_SECURE, - std::make_unique<NullDecrypter>(perspective)); + connection_->SetDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<NullDecrypter>(perspective)); } - connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); if (perspective == Perspective::IS_SERVER) { // Skip version negotiation. - test::QuicConnectionPeer::SetNegotiatedVersion(&connection_); + test::QuicConnectionPeer::SetNegotiatedVersion(connection_.get()); } - connection_.SetDataProducer(&producer_); - connection_.SetSessionNotifier(this); - notifier_ = std::make_unique<test::SimpleSessionNotifier>(&connection_); + connection_->SetDataProducer(&producer_); + connection_->SetSessionNotifier(this); + notifier_ = std::make_unique<test::SimpleSessionNotifier>(connection_.get()); // Configure the connection as if it received a handshake. This is important // primarily because @@ -120,19 +75,7 @@ peer_hello, perspective == Perspective::IS_CLIENT ? SERVER : CLIENT, &error); DCHECK_EQ(error_code, QUIC_NO_ERROR) << "Configuration failed: " << error; - connection_.SetFromConfig(config); -} - -QuicEndpoint::~QuicEndpoint() { - 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()); - } + connection_->SetFromConfig(config); } QuicByteCount QuicEndpoint::bytes_received() const { @@ -174,48 +117,6 @@ WriteStreamData(); } -void QuicEndpoint::DropNextIncomingPacket() { - drop_next_packet_ = true; -} - -void QuicEndpoint::RecordTrace() { - trace_visitor_ = std::make_unique<QuicTraceVisitor>(&connection_); - connection_.set_debug_visitor(trace_visitor_.get()); -} - -void QuicEndpoint::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* QuicEndpoint::GetRxPort() { - return this; -} - -void QuicEndpoint::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 QuicEndpoint::OnPacketDequeued() { - if (writer_.IsWriteBlocked() && - (nic_tx_queue_.capacity() - nic_tx_queue_.bytes_queued()) >= - kMaxOutgoingPacketSize) { - writer_.SetWritable(); - connection_.OnCanWrite(); - } -} - void QuicEndpoint::OnStreamFrame(const QuicStreamFrame& frame) { // Verify that the data received always matches the expected. DCHECK(frame.stream_id == kDataStream); @@ -300,73 +201,6 @@ return false; } -QuicEndpoint::Writer::Writer(QuicEndpoint* endpoint) - : endpoint_(endpoint), is_blocked_(false) {} - -QuicEndpoint::Writer::~Writer() {} - -WriteResult QuicEndpoint::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 QuicEndpoint::Writer::IsWriteBlocked() const { - return is_blocked_; -} - -void QuicEndpoint::Writer::SetWritable() { - is_blocked_ = false; -} - -QuicByteCount QuicEndpoint::Writer::GetMaxPacketSize( - const QuicSocketAddress& /*peer_address*/) const { - return kMaxOutgoingPacketSize; -} - -bool QuicEndpoint::Writer::SupportsReleaseTime() const { - return false; -} - -bool QuicEndpoint::Writer::IsBatchMode() const { - return false; -} - -char* QuicEndpoint::Writer::GetNextWriteLocation( - const QuicIpAddress& /*self_address*/, - const QuicSocketAddress& /*peer_address*/) { - return nullptr; -} - -WriteResult QuicEndpoint::Writer::Flush() { - return WriteResult(WRITE_STATUS_OK, 0); -} - WriteStreamDataResult QuicEndpoint::DataProducer::WriteStreamData( QuicStreamId /*id*/, QuicStreamOffset /*offset*/, @@ -386,14 +220,14 @@ void QuicEndpoint::WriteStreamData() { // Instantiate a flusher which would normally be here due to QuicSession. - QuicConnection::ScopedPacketFlusher flusher(&connection_); + QuicConnection::ScopedPacketFlusher flusher(connection_.get()); while (bytes_to_transfer_ > 0) { // Transfer data in chunks of size at most |kWriteChunkSize|. const size_t transmission_size = std::min(kWriteChunkSize, bytes_to_transfer_); - QuicConsumedData consumed_data = connection_.SendStreamData( + QuicConsumedData consumed_data = connection_->SendStreamData( kDataStream, transmission_size, bytes_transferred_, NO_FIN); DCHECK(consumed_data.bytes_consumed <= transmission_size); @@ -405,33 +239,5 @@ } } -QuicEndpointMultiplexer::QuicEndpointMultiplexer( - std::string name, - const std::vector<QuicEndpoint*>& endpoints) - : Endpoint((*endpoints.begin())->simulator(), name) { - for (QuicEndpoint* 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
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h index 43fce53..0b9b54a 100644 --- a/quic/test_tools/simulator/quic_endpoint.h +++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -16,26 +16,17 @@ #include "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h" #include "net/third_party/quiche/src/quic/test_tools/simulator/link.h" #include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h" namespace quic { namespace simulator { -// Size of the TX queue used by the kernel/NIC. 1000 is the Linux -// kernel default. -const QuicByteCount kTxQueueSize = 1000; - -// Generate a random local network host-port tuple based on the name of the -// endpoint. -QuicSocketAddress GetAddressFromName(std::string name); - // A QUIC connection endpoint. Wraps around QuicConnection. In order to // initiate a transfer, the caller has to call AddBytesToTransfer(). The data // transferred is always the same and is always transferred on a single stream. // The endpoint receives all packets addressed to it, and verifies that the data // received is what it's supposed to be. -class QuicEndpoint : public Endpoint, - public UnconstrainedPortInterface, - public Queue::ListenerInterface, +class QuicEndpoint : public QuicEndpointBase, public QuicConnectionVisitorInterface, public SessionNotifierInterface { public: @@ -44,40 +35,16 @@ std::string peer_name, Perspective perspective, QuicConnectionId connection_id); - ~QuicEndpoint() override; - inline QuicConnection* connection() { return &connection_; } QuicByteCount bytes_to_transfer() const; QuicByteCount bytes_transferred() const; QuicByteCount bytes_received() const; - inline size_t write_blocked_count() { return write_blocked_count_; } inline bool wrong_data_received() const { return wrong_data_received_; } // Send |bytes| bytes. Initiates the transfer if one is not already in // progress. void AddBytesToTransfer(QuicByteCount bytes); - // Drop the next packet upon receipt. - void DropNextIncomingPacket(); - - // UnconstrainedPortInterface method. Called whenever the endpoint receives a - // packet. - void AcceptPacket(std::unique_ptr<Packet> packet) override; - - // Enables logging of the connection trace at the end of the unit test. - void RecordTrace(); - - // Begin Endpoint implementation. - UnconstrainedPortInterface* GetRxPort() override; - void SetTxPort(ConstrainedPortInterface* port) override; - // End Endpoint implementation. - - // Actor method. - void Act() override {} - - // Queue::ListenerInterface method. - void OnPacketDequeued() override; - // Begin QuicConnectionVisitorInterface implementation. void OnStreamFrame(const QuicStreamFrame& frame) override; void OnCryptoFrame(const QuicCryptoFrame& frame) override; @@ -134,33 +101,6 @@ // End SessionNotifierInterface implementation. private: - // A Writer object that writes into the |nic_tx_queue_|. - class Writer : public QuicPacketWriter { - public: - explicit Writer(QuicEndpoint* endpoint); - ~Writer() override; - - WriteResult WritePacket(const char* buffer, - size_t buf_len, - const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address, - PerPacketOptions* options) override; - bool IsWriteBlocked() const override; - void SetWritable() override; - QuicByteCount GetMaxPacketSize( - const QuicSocketAddress& peer_address) const override; - bool SupportsReleaseTime() const override; - bool IsBatchMode() const override; - char* GetNextWriteLocation(const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address) override; - WriteResult Flush() override; - - private: - QuicEndpoint* endpoint_; - - bool is_blocked_; - }; - // The producer outputs the repetition of the same byte. That sequence is // verified by the receiver. class DataProducer : public QuicStreamFrameDataProducer { @@ -175,60 +115,30 @@ QuicDataWriter* writer) override; }; + std::unique_ptr<QuicConnection> CreateConnection( + Simulator* simulator, + std::string name, + std::string peer_name, + Perspective perspective, + QuicConnectionId connection_id); + // Write stream data until |bytes_to_transfer_| is zero or the connection is // write-blocked. void WriteStreamData(); - std::string peer_name_; - - Writer writer_; DataProducer producer_; - // The queue for the outgoing packets. In reality, this might be either on - // the network card, or in the kernel, but for concreteness we assume it's on - // the network card. - Queue nic_tx_queue_; - QuicConnection connection_; QuicByteCount bytes_to_transfer_; QuicByteCount bytes_transferred_; - // Counts the number of times the writer became write-blocked. - size_t write_blocked_count_; - // Set to true if the endpoint receives stream data different from what it // expects. bool wrong_data_received_; - // If true, drop the next packet when receiving it. - bool drop_next_packet_; - // Record of received offsets in the data stream. QuicIntervalSet<QuicStreamOffset> offsets_received_; std::unique_ptr<test::SimpleSessionNotifier> notifier_; - std::unique_ptr<QuicTraceVisitor> trace_visitor_; -}; - -// Multiplexes multiple connections at the same host on the network. -class QuicEndpointMultiplexer : public Endpoint, - public UnconstrainedPortInterface { - public: - QuicEndpointMultiplexer(std::string name, - const std::vector<QuicEndpoint*>& endpoints); - ~QuicEndpointMultiplexer() override; - - // Receives a packet and passes it to the specified endpoint if that endpoint - // is one of the endpoints being multiplexed, otherwise ignores the packet. - void AcceptPacket(std::unique_ptr<Packet> packet) override; - UnconstrainedPortInterface* GetRxPort() override; - - // Sets the egress port for all the endpoints being multiplexed. - void SetTxPort(ConstrainedPortInterface* port) override; - - void Act() override {} - - private: - QuicUnorderedMap<std::string, QuicEndpoint*> mapping_; }; } // namespace simulator
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
diff --git a/quic/test_tools/simulator/quic_endpoint_base.h b/quic/test_tools/simulator/quic_endpoint_base.h new file mode 100644 index 0000000..ae9f69b --- /dev/null +++ b/quic/test_tools/simulator/quic_endpoint_base.h
@@ -0,0 +1,158 @@ +// 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. + +#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_BASE_H_ +#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_BASE_H_ + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h" +#include "net/third_party/quiche/src/quic/core/quic_trace_visitor.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h" + +namespace quic { +namespace simulator { + +// Size of the TX queue used by the kernel/NIC. 1000 is the Linux +// kernel default. +const QuicByteCount kTxQueueSize = 1000; + +// Generate a random local network host-port tuple based on the name of the +// endpoint. +QuicSocketAddress GetAddressFromName(std::string name); + +// A QUIC connection endpoint. If the specific data transmitted does not matter +// (e.g. for congestion control purposes), QuicEndpoint is the subclass that +// transmits dummy data. If the actual semantics of the connection matter, +// subclassing QuicEndpointBase is required. +class QuicEndpointBase : public Endpoint, + public UnconstrainedPortInterface, + public Queue::ListenerInterface { + public: + // Does not create the connection; the subclass has to create connection by + // itself. + QuicEndpointBase(Simulator* simulator, + std::string name, + std::string peer_name); + ~QuicEndpointBase() override; + + inline QuicConnection* connection() { return connection_.get(); } + inline size_t write_blocked_count() { return write_blocked_count_; } + + // Drop the next packet upon receipt. + void DropNextIncomingPacket(); + + // UnconstrainedPortInterface method. Called whenever the endpoint receives a + // packet. + void AcceptPacket(std::unique_ptr<Packet> packet) override; + + // Enables logging of the connection trace at the end of the unit test. + void RecordTrace(); + + // Begin Endpoint implementation. + UnconstrainedPortInterface* GetRxPort() override; + void SetTxPort(ConstrainedPortInterface* port) override; + // End Endpoint implementation. + + // Actor method. + void Act() override {} + + // Queue::ListenerInterface method. + void OnPacketDequeued() override; + + protected: + // A Writer object that writes into the |nic_tx_queue_|. + class Writer : public QuicPacketWriter { + public: + explicit Writer(QuicEndpointBase* endpoint); + ~Writer() override; + + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) override; + bool IsWriteBlocked() const override; + void SetWritable() override; + QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& peer_address) const override; + bool SupportsReleaseTime() const override; + bool IsBatchMode() const override; + char* GetNextWriteLocation(const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override; + WriteResult Flush() override; + + private: + QuicEndpointBase* endpoint_; + + bool is_blocked_; + }; + + // The producer outputs the repetition of the same byte. That sequence is + // verified by the receiver. + class DataProducer : public QuicStreamFrameDataProducer { + public: + WriteStreamDataResult WriteStreamData(QuicStreamId id, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) override; + bool WriteCryptoData(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) override; + }; + + std::string peer_name_; + + Writer writer_; + // The queue for the outgoing packets. In reality, this might be either on + // the network card, or in the kernel, but for concreteness we assume it's on + // the network card. + Queue nic_tx_queue_; + // Created by the subclass. + std::unique_ptr<QuicConnection> connection_; + + // Counts the number of times the writer became write-blocked. + size_t write_blocked_count_; + + // If true, drop the next packet when receiving it. + bool drop_next_packet_; + + std::unique_ptr<QuicTraceVisitor> trace_visitor_; +}; + +// Multiplexes multiple connections at the same host on the network. +class QuicEndpointMultiplexer : public Endpoint, + public UnconstrainedPortInterface { + public: + QuicEndpointMultiplexer(std::string name, + const std::vector<QuicEndpointBase*>& endpoints); + ~QuicEndpointMultiplexer() override; + + // Receives a packet and passes it to the specified endpoint if that endpoint + // is one of the endpoints being multiplexed, otherwise ignores the packet. + void AcceptPacket(std::unique_ptr<Packet> packet) override; + UnconstrainedPortInterface* GetRxPort() override; + + // Sets the egress port for all the endpoints being multiplexed. + void SetTxPort(ConstrainedPortInterface* port) override; + + void Act() override {} + + private: + QuicUnorderedMap<std::string, QuicEndpointBase*> mapping_; +}; + +} // namespace simulator +} // namespace quic + +#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_BASE_H_