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