text: // 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 "quic/test_tools/simulator/quic_endpoint_base.h"
  
  #include <memory>
  #include <utility>
  
  #include "absl/strings/str_cat.h"
  #include "quic/core/crypto/crypto_handshake_message.h"
  #include "quic/core/crypto/crypto_protocol.h"
  #include "quic/core/quic_connection.h"
  #include "quic/core/quic_data_writer.h"
  #include "quic/platform/api/quic_test_output.h"
  #include "quic/test_tools/quic_connection_peer.h"
  #include "quic/test_tools/quic_test_utils.h"
  #include "quic/test_tools/simulator/simulator.h"
  #include "common/platform/api/quiche_str_cat.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,
                      quiche::QuicheStringPrintf("%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 = absl::StrCat(
          perspective_prefix, connection_->connection_id().ToString());
      QuicRecordTrace(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;
  }
  
  QuicPacketBuffer QuicEndpointBase::Writer::GetNextWriteLocation(
      const QuicIpAddress& /*self_address*/,
      const QuicSocketAddress& /*peer_address*/) {
    return {nullptr, 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
  