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_