Move Connection into it's own .h/.cc file.

This patch is a NOP and moves
- class Connection
- class ConnectionInfo
- class ProxyConnection

from port.{h/cc} to a new file called connection.{h/cc}

BUG=webrtc:10647

Change-Id: I89322d3421d272657e24a46b28ab6679fcdc9450
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/137509
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Qingsi Wang <qingsi@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28101}
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index c389e81..a07afef 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -25,6 +25,10 @@
     "base/basic_packet_socket_factory.cc",
     "base/basic_packet_socket_factory.h",
     "base/candidate_pair_interface.h",
+    "base/connection.cc",
+    "base/connection.h",
+    "base/connection_info.cc",
+    "base/connection_info.h",
     "base/datagram_dtls_adaptor.cc",
     "base/datagram_dtls_adaptor.h",
     "base/dtls_transport.cc",
diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc
new file mode 100644
index 0000000..6fc93ed
--- /dev/null
+++ b/p2p/base/connection.cc
@@ -0,0 +1,1133 @@
+/*
+ *  Copyright 2019 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "p2p/base/connection.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/match.h"
+#include "p2p/base/port_allocator.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/crc32.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/mdns_responder_interface.h"
+#include "rtc_base/message_digest.h"
+#include "rtc_base/network.h"
+#include "rtc_base/numerics/safe_minmax.h"
+#include "rtc_base/string_encode.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/third_party/base64/base64.h"
+#include "system_wrappers/include/field_trial.h"
+
+namespace {
+
+// Determines whether we have seen at least the given maximum number of
+// pings fail to have a response.
+inline bool TooManyFailures(
+    const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
+    uint32_t maximum_failures,
+    int rtt_estimate,
+    int64_t now) {
+  // If we haven't sent that many pings, then we can't have failed that many.
+  if (pings_since_last_response.size() < maximum_failures)
+    return false;
+
+  // Check if the window in which we would expect a response to the ping has
+  // already elapsed.
+  int64_t expected_response_time =
+      pings_since_last_response[maximum_failures - 1].sent_time + rtt_estimate;
+  return now > expected_response_time;
+}
+
+// Determines whether we have gone too long without seeing any response.
+inline bool TooLongWithoutResponse(
+    const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
+    int64_t maximum_time,
+    int64_t now) {
+  if (pings_since_last_response.size() == 0)
+    return false;
+
+  auto first = pings_since_last_response[0];
+  return now > (first.sent_time + maximum_time);
+}
+
+// Helper methods for converting string values of log description fields to
+// enum.
+webrtc::IceCandidateType GetCandidateTypeByString(const std::string& type) {
+  if (type == cricket::LOCAL_PORT_TYPE) {
+    return webrtc::IceCandidateType::kLocal;
+  } else if (type == cricket::STUN_PORT_TYPE) {
+    return webrtc::IceCandidateType::kStun;
+  } else if (type == cricket::PRFLX_PORT_TYPE) {
+    return webrtc::IceCandidateType::kPrflx;
+  } else if (type == cricket::RELAY_PORT_TYPE) {
+    return webrtc::IceCandidateType::kRelay;
+  }
+  return webrtc::IceCandidateType::kUnknown;
+}
+
+webrtc::IceCandidatePairProtocol GetProtocolByString(
+    const std::string& protocol) {
+  if (protocol == cricket::UDP_PROTOCOL_NAME) {
+    return webrtc::IceCandidatePairProtocol::kUdp;
+  } else if (protocol == cricket::TCP_PROTOCOL_NAME) {
+    return webrtc::IceCandidatePairProtocol::kTcp;
+  } else if (protocol == cricket::SSLTCP_PROTOCOL_NAME) {
+    return webrtc::IceCandidatePairProtocol::kSsltcp;
+  } else if (protocol == cricket::TLS_PROTOCOL_NAME) {
+    return webrtc::IceCandidatePairProtocol::kTls;
+  }
+  return webrtc::IceCandidatePairProtocol::kUnknown;
+}
+
+webrtc::IceCandidatePairAddressFamily GetAddressFamilyByInt(
+    int address_family) {
+  if (address_family == AF_INET) {
+    return webrtc::IceCandidatePairAddressFamily::kIpv4;
+  } else if (address_family == AF_INET6) {
+    return webrtc::IceCandidatePairAddressFamily::kIpv6;
+  }
+  return webrtc::IceCandidatePairAddressFamily::kUnknown;
+}
+
+webrtc::IceCandidateNetworkType ConvertNetworkType(rtc::AdapterType type) {
+  if (type == rtc::ADAPTER_TYPE_ETHERNET) {
+    return webrtc::IceCandidateNetworkType::kEthernet;
+  } else if (type == rtc::ADAPTER_TYPE_LOOPBACK) {
+    return webrtc::IceCandidateNetworkType::kLoopback;
+  } else if (type == rtc::ADAPTER_TYPE_WIFI) {
+    return webrtc::IceCandidateNetworkType::kWifi;
+  } else if (type == rtc::ADAPTER_TYPE_VPN) {
+    return webrtc::IceCandidateNetworkType::kVpn;
+  } else if (type == rtc::ADAPTER_TYPE_CELLULAR) {
+    return webrtc::IceCandidateNetworkType::kCellular;
+  }
+  return webrtc::IceCandidateNetworkType::kUnknown;
+}
+
+// When we don't have any RTT data, we have to pick something reasonable.  We
+// use a large value just in case the connection is really slow.
+const int DEFAULT_RTT = 3000;  // 3 seconds
+
+// We will restrict RTT estimates (when used for determining state) to be
+// within a reasonable range.
+const int MINIMUM_RTT = 100;    // 0.1 seconds
+const int MAXIMUM_RTT = 60000;  // 60 seconds
+
+// Computes our estimate of the RTT given the current estimate.
+inline int ConservativeRTTEstimate(int rtt) {
+  return rtc::SafeClamp(2 * rtt, MINIMUM_RTT, MAXIMUM_RTT);
+}
+
+// Weighting of the old rtt value to new data.
+const int RTT_RATIO = 3;  // 3 : 1
+
+constexpr int64_t kMinExtraPingDelayMs = 100;
+
+}  // namespace
+
+namespace cricket {
+
+// A ConnectionRequest is a simple STUN ping used to determine writability.
+ConnectionRequest::ConnectionRequest(Connection* connection)
+    : StunRequest(new IceMessage()), connection_(connection) {}
+
+void ConnectionRequest::Prepare(StunMessage* request) {
+  request->SetType(STUN_BINDING_REQUEST);
+  std::string username;
+  connection_->port()->CreateStunUsername(
+      connection_->remote_candidate().username(), &username);
+  request->AddAttribute(
+      absl::make_unique<StunByteStringAttribute>(STUN_ATTR_USERNAME, username));
+
+  // connection_ already holds this ping, so subtract one from count.
+  if (connection_->port()->send_retransmit_count_attribute()) {
+    request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
+        STUN_ATTR_RETRANSMIT_COUNT,
+        static_cast<uint32_t>(connection_->pings_since_last_response_.size() -
+                              1)));
+  }
+  uint32_t network_info = connection_->port()->Network()->id();
+  network_info = (network_info << 16) | connection_->port()->network_cost();
+  request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
+      STUN_ATTR_NETWORK_INFO, network_info));
+
+  // Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
+  if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
+    request->AddAttribute(absl::make_unique<StunUInt64Attribute>(
+        STUN_ATTR_ICE_CONTROLLING, connection_->port()->IceTiebreaker()));
+    // We should have either USE_CANDIDATE attribute or ICE_NOMINATION
+    // attribute but not both. That was enforced in p2ptransportchannel.
+    if (connection_->use_candidate_attr()) {
+      request->AddAttribute(
+          absl::make_unique<StunByteStringAttribute>(STUN_ATTR_USE_CANDIDATE));
+    }
+    if (connection_->nomination() &&
+        connection_->nomination() != connection_->acked_nomination()) {
+      request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
+          STUN_ATTR_NOMINATION, connection_->nomination()));
+    }
+  } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) {
+    request->AddAttribute(absl::make_unique<StunUInt64Attribute>(
+        STUN_ATTR_ICE_CONTROLLED, connection_->port()->IceTiebreaker()));
+  } else {
+    RTC_NOTREACHED();
+  }
+
+  // Adding PRIORITY Attribute.
+  // Changing the type preference to Peer Reflexive and local preference
+  // and component id information is unchanged from the original priority.
+  // priority = (2^24)*(type preference) +
+  //           (2^8)*(local preference) +
+  //           (2^0)*(256 - component ID)
+  uint32_t type_preference =
+      (connection_->local_candidate().protocol() == TCP_PROTOCOL_NAME)
+          ? ICE_TYPE_PREFERENCE_PRFLX_TCP
+          : ICE_TYPE_PREFERENCE_PRFLX;
+  uint32_t prflx_priority =
+      type_preference << 24 |
+      (connection_->local_candidate().priority() & 0x00FFFFFF);
+  request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
+      STUN_ATTR_PRIORITY, prflx_priority));
+
+  // Adding Message Integrity attribute.
+  request->AddMessageIntegrity(connection_->remote_candidate().password());
+  // Adding Fingerprint.
+  request->AddFingerprint();
+}
+
+void ConnectionRequest::OnResponse(StunMessage* response) {
+  connection_->OnConnectionRequestResponse(this, response);
+}
+
+void ConnectionRequest::OnErrorResponse(StunMessage* response) {
+  connection_->OnConnectionRequestErrorResponse(this, response);
+}
+
+void ConnectionRequest::OnTimeout() {
+  connection_->OnConnectionRequestTimeout(this);
+}
+
+void ConnectionRequest::OnSent() {
+  connection_->OnConnectionRequestSent(this);
+  // Each request is sent only once.  After a single delay , the request will
+  // time out.
+  timeout_ = true;
+}
+
+int ConnectionRequest::resend_delay() {
+  return CONNECTION_RESPONSE_TIMEOUT;
+}
+
+Connection::Connection(Port* port,
+                       size_t index,
+                       const Candidate& remote_candidate)
+    : id_(rtc::CreateRandomId()),
+      port_(port),
+      local_candidate_index_(index),
+      remote_candidate_(remote_candidate),
+      recv_rate_tracker_(100, 10u),
+      send_rate_tracker_(100, 10u),
+      write_state_(STATE_WRITE_INIT),
+      receiving_(false),
+      connected_(true),
+      pruned_(false),
+      use_candidate_attr_(false),
+      remote_ice_mode_(ICEMODE_FULL),
+      requests_(port->thread()),
+      rtt_(DEFAULT_RTT),
+      last_ping_sent_(0),
+      last_ping_received_(0),
+      last_data_received_(0),
+      last_ping_response_received_(0),
+      reported_(false),
+      state_(IceCandidatePairState::WAITING),
+      time_created_ms_(rtc::TimeMillis()) {
+  // All of our connections start in WAITING state.
+  // TODO(mallinath) - Start connections from STATE_FROZEN.
+  // Wire up to send stun packets
+  requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket);
+  RTC_LOG(LS_INFO) << ToString() << ": Connection created";
+}
+
+Connection::~Connection() {}
+
+const Candidate& Connection::local_candidate() const {
+  RTC_DCHECK(local_candidate_index_ < port_->Candidates().size());
+  return port_->Candidates()[local_candidate_index_];
+}
+
+const Candidate& Connection::remote_candidate() const {
+  return remote_candidate_;
+}
+
+uint64_t Connection::priority() const {
+  uint64_t priority = 0;
+  // RFC 5245 - 5.7.2.  Computing Pair Priority and Ordering Pairs
+  // Let G be the priority for the candidate provided by the controlling
+  // agent.  Let D be the priority for the candidate provided by the
+  // controlled agent.
+  // pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
+  IceRole role = port_->GetIceRole();
+  if (role != ICEROLE_UNKNOWN) {
+    uint32_t g = 0;
+    uint32_t d = 0;
+    if (role == ICEROLE_CONTROLLING) {
+      g = local_candidate().priority();
+      d = remote_candidate_.priority();
+    } else {
+      g = remote_candidate_.priority();
+      d = local_candidate().priority();
+    }
+    priority = std::min(g, d);
+    priority = priority << 32;
+    priority += 2 * std::max(g, d) + (g > d ? 1 : 0);
+  }
+  return priority;
+}
+
+void Connection::set_write_state(WriteState value) {
+  WriteState old_value = write_state_;
+  write_state_ = value;
+  if (value != old_value) {
+    RTC_LOG(LS_VERBOSE) << ToString() << ": set_write_state from: " << old_value
+                        << " to " << value;
+    SignalStateChange(this);
+  }
+}
+
+void Connection::UpdateReceiving(int64_t now) {
+  bool receiving;
+  if (last_ping_sent() < last_ping_response_received()) {
+    // We consider any candidate pair that has its last connectivity check
+    // acknowledged by a response as receiving, particularly for backup
+    // candidate pairs that send checks at a much slower pace than the selected
+    // one. Otherwise, a backup candidate pair constantly becomes not receiving
+    // as a side effect of a long ping interval, since we do not have a separate
+    // receiving timeout for backup candidate pairs. See
+    // IceConfig.ice_backup_candidate_pair_ping_interval,
+    // IceConfig.ice_connection_receiving_timeout and their default value.
+    receiving = true;
+  } else {
+    receiving =
+        last_received() > 0 && now <= last_received() + receiving_timeout();
+  }
+  if (receiving_ == receiving) {
+    return;
+  }
+  RTC_LOG(LS_VERBOSE) << ToString() << ": set_receiving to " << receiving;
+  receiving_ = receiving;
+  receiving_unchanged_since_ = now;
+  SignalStateChange(this);
+}
+
+void Connection::set_state(IceCandidatePairState state) {
+  IceCandidatePairState old_state = state_;
+  state_ = state;
+  if (state != old_state) {
+    RTC_LOG(LS_VERBOSE) << ToString() << ": set_state";
+  }
+}
+
+void Connection::set_connected(bool value) {
+  bool old_value = connected_;
+  connected_ = value;
+  if (value != old_value) {
+    RTC_LOG(LS_VERBOSE) << ToString() << ": Change connected_ to " << value;
+    SignalStateChange(this);
+  }
+}
+
+void Connection::set_use_candidate_attr(bool enable) {
+  use_candidate_attr_ = enable;
+}
+
+int Connection::unwritable_timeout() const {
+  return unwritable_timeout_.value_or(CONNECTION_WRITE_CONNECT_TIMEOUT);
+}
+
+int Connection::unwritable_min_checks() const {
+  return unwritable_min_checks_.value_or(CONNECTION_WRITE_CONNECT_FAILURES);
+}
+
+int Connection::inactive_timeout() const {
+  return inactive_timeout_.value_or(CONNECTION_WRITE_TIMEOUT);
+}
+
+int Connection::receiving_timeout() const {
+  return receiving_timeout_.value_or(WEAK_CONNECTION_RECEIVE_TIMEOUT);
+}
+
+void Connection::OnSendStunPacket(const void* data,
+                                  size_t size,
+                                  StunRequest* req) {
+  rtc::PacketOptions options(port_->StunDscpValue());
+  options.info_signaled_after_sent.packet_type =
+      rtc::PacketType::kIceConnectivityCheck;
+  auto err =
+      port_->SendTo(data, size, remote_candidate_.address(), options, false);
+  if (err < 0) {
+    RTC_LOG(LS_WARNING) << ToString()
+                        << ": Failed to send STUN ping "
+                           " err="
+                        << err << " id=" << rtc::hex_encode(req->id());
+  }
+}
+
+void Connection::OnReadPacket(const char* data,
+                              size_t size,
+                              int64_t packet_time_us) {
+  std::unique_ptr<IceMessage> msg;
+  std::string remote_ufrag;
+  const rtc::SocketAddress& addr(remote_candidate_.address());
+  if (!port_->GetStunMessage(data, size, addr, &msg, &remote_ufrag)) {
+    // The packet did not parse as a valid STUN message
+    // This is a data packet, pass it along.
+    last_data_received_ = rtc::TimeMillis();
+    UpdateReceiving(last_data_received_);
+    recv_rate_tracker_.AddSamples(size);
+    SignalReadPacket(this, data, size, packet_time_us);
+
+    // If timed out sending writability checks, start up again
+    if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) {
+      RTC_LOG(LS_WARNING)
+          << "Received a data packet on a timed-out Connection. "
+             "Resetting state to STATE_WRITE_INIT.";
+      set_write_state(STATE_WRITE_INIT);
+    }
+  } else if (!msg) {
+    // The packet was STUN, but failed a check and was handled internally.
+  } else {
+    // The packet is STUN and passed the Port checks.
+    // Perform our own checks to ensure this packet is valid.
+    // If this is a STUN request, then update the receiving bit and respond.
+    // If this is a STUN response, then update the writable bit.
+    // Log at LS_INFO if we receive a ping on an unwritable connection.
+    rtc::LoggingSeverity sev = (!writable() ? rtc::LS_INFO : rtc::LS_VERBOSE);
+    switch (msg->type()) {
+      case STUN_BINDING_REQUEST:
+        RTC_LOG_V(sev) << ToString() << ": Received STUN ping, id="
+                       << rtc::hex_encode(msg->transaction_id());
+
+        if (remote_ufrag == remote_candidate_.username()) {
+          HandleBindingRequest(msg.get());
+        } else {
+          // The packet had the right local username, but the remote username
+          // was not the right one for the remote address.
+          RTC_LOG(LS_ERROR)
+              << ToString()
+              << ": Received STUN request with bad remote username "
+              << remote_ufrag;
+          port_->SendBindingErrorResponse(msg.get(), addr,
+                                          STUN_ERROR_UNAUTHORIZED,
+                                          STUN_ERROR_REASON_UNAUTHORIZED);
+        }
+        break;
+
+      // Response from remote peer. Does it match request sent?
+      // This doesn't just check, it makes callbacks if transaction
+      // id's match.
+      case STUN_BINDING_RESPONSE:
+      case STUN_BINDING_ERROR_RESPONSE:
+        if (msg->ValidateMessageIntegrity(data, size,
+                                          remote_candidate().password())) {
+          requests_.CheckResponse(msg.get());
+        }
+        // Otherwise silently discard the response message.
+        break;
+
+      // Remote end point sent an STUN indication instead of regular binding
+      // request. In this case |last_ping_received_| will be updated but no
+      // response will be sent.
+      case STUN_BINDING_INDICATION:
+        ReceivedPing();
+        break;
+
+      default:
+        RTC_NOTREACHED();
+        break;
+    }
+  }
+}
+
+void Connection::HandleBindingRequest(IceMessage* msg) {
+  // This connection should now be receiving.
+  ReceivedPing();
+  if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing") &&
+      last_ping_response_received_ == 0) {
+    if (local_candidate().type() == RELAY_PORT_TYPE ||
+        local_candidate().type() == PRFLX_PORT_TYPE ||
+        remote_candidate().type() == RELAY_PORT_TYPE ||
+        remote_candidate().type() == PRFLX_PORT_TYPE) {
+      const int64_t now = rtc::TimeMillis();
+      if (last_ping_sent_ + kMinExtraPingDelayMs <= now) {
+        RTC_LOG(LS_INFO) << ToString()
+                         << "WebRTC-ExtraICEPing/Sending extra ping"
+                         << " last_ping_sent_: " << last_ping_sent_
+                         << " now: " << now
+                         << " (diff: " << (now - last_ping_sent_) << ")";
+        Ping(now);
+      } else {
+        RTC_LOG(LS_INFO) << ToString()
+                         << "WebRTC-ExtraICEPing/Not sending extra ping"
+                         << " last_ping_sent_: " << last_ping_sent_
+                         << " now: " << now
+                         << " (diff: " << (now - last_ping_sent_) << ")";
+      }
+    }
+  }
+
+  const rtc::SocketAddress& remote_addr = remote_candidate_.address();
+  const std::string& remote_ufrag = remote_candidate_.username();
+  // Check for role conflicts.
+  if (!port_->MaybeIceRoleConflict(remote_addr, msg, remote_ufrag)) {
+    // Received conflicting role from the peer.
+    RTC_LOG(LS_INFO) << "Received conflicting role from the peer.";
+    return;
+  }
+
+  stats_.recv_ping_requests++;
+  LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckReceived,
+                        msg->reduced_transaction_id());
+
+  // This is a validated stun request from remote peer.
+  port_->SendBindingResponse(msg, remote_addr);
+
+  // If it timed out on writing check, start up again
+  if (!pruned_ && write_state_ == STATE_WRITE_TIMEOUT) {
+    set_write_state(STATE_WRITE_INIT);
+  }
+
+  if (port_->GetIceRole() == ICEROLE_CONTROLLED) {
+    const StunUInt32Attribute* nomination_attr =
+        msg->GetUInt32(STUN_ATTR_NOMINATION);
+    uint32_t nomination = 0;
+    if (nomination_attr) {
+      nomination = nomination_attr->value();
+      if (nomination == 0) {
+        RTC_LOG(LS_ERROR) << "Invalid nomination: " << nomination;
+      }
+    } else {
+      const StunByteStringAttribute* use_candidate_attr =
+          msg->GetByteString(STUN_ATTR_USE_CANDIDATE);
+      if (use_candidate_attr) {
+        nomination = 1;
+      }
+    }
+    // We don't un-nominate a connection, so we only keep a larger nomination.
+    if (nomination > remote_nomination_) {
+      set_remote_nomination(nomination);
+      SignalNominated(this);
+    }
+  }
+  // Set the remote cost if the network_info attribute is available.
+  // Note: If packets are re-ordered, we may get incorrect network cost
+  // temporarily, but it should get the correct value shortly after that.
+  const StunUInt32Attribute* network_attr =
+      msg->GetUInt32(STUN_ATTR_NETWORK_INFO);
+  if (network_attr) {
+    uint32_t network_info = network_attr->value();
+    uint16_t network_cost = static_cast<uint16_t>(network_info);
+    if (network_cost != remote_candidate_.network_cost()) {
+      remote_candidate_.set_network_cost(network_cost);
+      // Network cost change will affect the connection ranking, so signal
+      // state change to force a re-sort in P2PTransportChannel.
+      SignalStateChange(this);
+    }
+  }
+}
+
+void Connection::OnReadyToSend() {
+  SignalReadyToSend(this);
+}
+
+void Connection::Prune() {
+  if (!pruned_ || active()) {
+    RTC_LOG(LS_INFO) << ToString() << ": Connection pruned";
+    pruned_ = true;
+    requests_.Clear();
+    set_write_state(STATE_WRITE_TIMEOUT);
+  }
+}
+
+void Connection::Destroy() {
+  // TODO(deadbeef, nisse): This may leak if an application closes a
+  // PeerConnection and then quickly destroys the PeerConnectionFactory (along
+  // with the networking thread on which this message is posted). Also affects
+  // tests, with a workaround in
+  // AutoSocketServerThread::~AutoSocketServerThread.
+  RTC_LOG(LS_VERBOSE) << ToString() << ": Connection destroyed";
+  port_->thread()->Post(RTC_FROM_HERE, this, MSG_DELETE);
+  LogCandidatePairConfig(webrtc::IceCandidatePairConfigType::kDestroyed);
+}
+
+void Connection::FailAndDestroy() {
+  set_state(IceCandidatePairState::FAILED);
+  Destroy();
+}
+
+void Connection::FailAndPrune() {
+  set_state(IceCandidatePairState::FAILED);
+  Prune();
+}
+
+void Connection::PrintPingsSinceLastResponse(std::string* s, size_t max) {
+  rtc::StringBuilder oss;
+  if (pings_since_last_response_.size() > max) {
+    for (size_t i = 0; i < max; i++) {
+      const SentPing& ping = pings_since_last_response_[i];
+      oss << rtc::hex_encode(ping.id) << " ";
+    }
+    oss << "... " << (pings_since_last_response_.size() - max) << " more";
+  } else {
+    for (const SentPing& ping : pings_since_last_response_) {
+      oss << rtc::hex_encode(ping.id) << " ";
+    }
+  }
+  *s = oss.str();
+}
+
+void Connection::UpdateState(int64_t now) {
+  int rtt = ConservativeRTTEstimate(rtt_);
+
+  if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE)) {
+    std::string pings;
+    PrintPingsSinceLastResponse(&pings, 5);
+    RTC_LOG(LS_VERBOSE) << ToString()
+                        << ": UpdateState()"
+                           ", ms since last received response="
+                        << now - last_ping_response_received_
+                        << ", ms since last received data="
+                        << now - last_data_received_ << ", rtt=" << rtt
+                        << ", pings_since_last_response=" << pings;
+  }
+
+  // Check the writable state.  (The order of these checks is important.)
+  //
+  // Before becoming unwritable, we allow for a fixed number of pings to fail
+  // (i.e., receive no response).  We also have to give the response time to
+  // get back, so we include a conservative estimate of this.
+  //
+  // Before timing out writability, we give a fixed amount of time.  This is to
+  // allow for changes in network conditions.
+
+  if ((write_state_ == STATE_WRITABLE) &&
+      TooManyFailures(pings_since_last_response_, unwritable_min_checks(), rtt,
+                      now) &&
+      TooLongWithoutResponse(pings_since_last_response_, unwritable_timeout(),
+                             now)) {
+    uint32_t max_pings = unwritable_min_checks();
+    RTC_LOG(LS_INFO) << ToString() << ": Unwritable after " << max_pings
+                     << " ping failures and "
+                     << now - pings_since_last_response_[0].sent_time
+                     << " ms without a response,"
+                        " ms since last received ping="
+                     << now - last_ping_received_
+                     << " ms since last received data="
+                     << now - last_data_received_ << " rtt=" << rtt;
+    set_write_state(STATE_WRITE_UNRELIABLE);
+  }
+  if ((write_state_ == STATE_WRITE_UNRELIABLE ||
+       write_state_ == STATE_WRITE_INIT) &&
+      TooLongWithoutResponse(pings_since_last_response_, inactive_timeout(),
+                             now)) {
+    RTC_LOG(LS_INFO) << ToString() << ": Timed out after "
+                     << now - pings_since_last_response_[0].sent_time
+                     << " ms without a response, rtt=" << rtt;
+    set_write_state(STATE_WRITE_TIMEOUT);
+  }
+
+  // Update the receiving state.
+  UpdateReceiving(now);
+  if (dead(now)) {
+    Destroy();
+  }
+}
+
+void Connection::Ping(int64_t now) {
+  last_ping_sent_ = now;
+  ConnectionRequest* req = new ConnectionRequest(this);
+  // If not using renomination, we use "1" to mean "nominated" and "0" to mean
+  // "not nominated". If using renomination, values greater than 1 are used for
+  // re-nominated pairs.
+  int nomination = use_candidate_attr_ ? 1 : 0;
+  if (nomination_ > 0) {
+    nomination = nomination_;
+  }
+  pings_since_last_response_.push_back(SentPing(req->id(), now, nomination));
+  RTC_LOG(LS_VERBOSE) << ToString() << ": Sending STUN ping, id="
+                      << rtc::hex_encode(req->id())
+                      << ", nomination=" << nomination_;
+  requests_.Send(req);
+  state_ = IceCandidatePairState::IN_PROGRESS;
+  num_pings_sent_++;
+}
+
+void Connection::ReceivedPing() {
+  last_ping_received_ = rtc::TimeMillis();
+  UpdateReceiving(last_ping_received_);
+}
+
+void Connection::ReceivedPingResponse(int rtt, const std::string& request_id) {
+  RTC_DCHECK_GE(rtt, 0);
+  // We've already validated that this is a STUN binding response with
+  // the correct local and remote username for this connection.
+  // So if we're not already, become writable. We may be bringing a pruned
+  // connection back to life, but if we don't really want it, we can always
+  // prune it again.
+  auto iter = absl::c_find_if(
+      pings_since_last_response_,
+      [request_id](const SentPing& ping) { return ping.id == request_id; });
+  if (iter != pings_since_last_response_.end() &&
+      iter->nomination > acked_nomination_) {
+    acked_nomination_ = iter->nomination;
+  }
+
+  total_round_trip_time_ms_ += rtt;
+  current_round_trip_time_ms_ = static_cast<uint32_t>(rtt);
+
+  pings_since_last_response_.clear();
+  last_ping_response_received_ = rtc::TimeMillis();
+  UpdateReceiving(last_ping_response_received_);
+  set_write_state(STATE_WRITABLE);
+  set_state(IceCandidatePairState::SUCCEEDED);
+  if (rtt_samples_ > 0) {
+    rtt_ = rtc::GetNextMovingAverage(rtt_, rtt, RTT_RATIO);
+  } else {
+    rtt_ = rtt;
+  }
+  rtt_samples_++;
+}
+
+bool Connection::dead(int64_t now) const {
+  if (last_received() > 0) {
+    // If it has ever received anything, we keep it alive until it hasn't
+    // received anything for DEAD_CONNECTION_RECEIVE_TIMEOUT. This covers the
+    // normal case of a successfully used connection that stops working. This
+    // also allows a remote peer to continue pinging over a locally inactive
+    // (pruned) connection.
+    return (now > (last_received() + DEAD_CONNECTION_RECEIVE_TIMEOUT));
+  }
+
+  if (active()) {
+    // If it has never received anything, keep it alive as long as it is
+    // actively pinging and not pruned. Otherwise, the connection might be
+    // deleted before it has a chance to ping. This is the normal case for a
+    // new connection that is pinging but hasn't received anything yet.
+    return false;
+  }
+
+  // If it has never received anything and is not actively pinging (pruned), we
+  // keep it around for at least MIN_CONNECTION_LIFETIME to prevent connections
+  // from being pruned too quickly during a network change event when two
+  // networks would be up simultaneously but only for a brief period.
+  return now > (time_created_ms_ + MIN_CONNECTION_LIFETIME);
+}
+
+bool Connection::stable(int64_t now) const {
+  // A connection is stable if it's RTT has converged and it isn't missing any
+  // responses.  We should send pings at a higher rate until the RTT converges
+  // and whenever a ping response is missing (so that we can detect
+  // unwritability faster)
+  return rtt_converged() && !missing_responses(now);
+}
+
+std::string Connection::ToDebugId() const {
+  return rtc::ToHex(reinterpret_cast<uintptr_t>(this));
+}
+
+uint32_t Connection::ComputeNetworkCost() const {
+  // TODO(honghaiz): Will add rtt as part of the network cost.
+  return port()->network_cost() + remote_candidate_.network_cost();
+}
+
+std::string Connection::ToString() const {
+  const absl::string_view CONNECT_STATE_ABBREV[2] = {
+      "-",  // not connected (false)
+      "C",  // connected (true)
+  };
+  const absl::string_view RECEIVE_STATE_ABBREV[2] = {
+      "-",  // not receiving (false)
+      "R",  // receiving (true)
+  };
+  const absl::string_view WRITE_STATE_ABBREV[4] = {
+      "W",  // STATE_WRITABLE
+      "w",  // STATE_WRITE_UNRELIABLE
+      "-",  // STATE_WRITE_INIT
+      "x",  // STATE_WRITE_TIMEOUT
+  };
+  const absl::string_view ICESTATE[4] = {
+      "W",  // STATE_WAITING
+      "I",  // STATE_INPROGRESS
+      "S",  // STATE_SUCCEEDED
+      "F"   // STATE_FAILED
+  };
+  const absl::string_view SELECTED_STATE_ABBREV[2] = {
+      "-",  // candidate pair not selected (false)
+      "S",  // selected (true)
+  };
+  const Candidate& local = local_candidate();
+  const Candidate& remote = remote_candidate();
+  rtc::StringBuilder ss;
+  ss << "Conn[" << ToDebugId() << ":" << port_->content_name() << ":"
+     << port_->Network()->ToString() << ":" << local.id() << ":"
+     << local.component() << ":" << local.generation() << ":" << local.type()
+     << ":" << local.protocol() << ":" << local.address().ToSensitiveString()
+     << "->" << remote.id() << ":" << remote.component() << ":"
+     << remote.priority() << ":" << remote.type() << ":" << remote.protocol()
+     << ":" << remote.address().ToSensitiveString() << "|"
+     << CONNECT_STATE_ABBREV[connected()] << RECEIVE_STATE_ABBREV[receiving()]
+     << WRITE_STATE_ABBREV[write_state()] << ICESTATE[static_cast<int>(state())]
+     << "|" << SELECTED_STATE_ABBREV[selected()] << "|" << remote_nomination()
+     << "|" << nomination() << "|" << priority() << "|";
+  if (rtt_ < DEFAULT_RTT) {
+    ss << rtt_ << "]";
+  } else {
+    ss << "-]";
+  }
+  return ss.Release();
+}
+
+std::string Connection::ToSensitiveString() const {
+  return ToString();
+}
+
+const webrtc::IceCandidatePairDescription& Connection::ToLogDescription() {
+  if (log_description_.has_value()) {
+    return log_description_.value();
+  }
+  const Candidate& local = local_candidate();
+  const Candidate& remote = remote_candidate();
+  const rtc::Network* network = port()->Network();
+  log_description_ = webrtc::IceCandidatePairDescription();
+  log_description_->local_candidate_type =
+      GetCandidateTypeByString(local.type());
+  log_description_->local_relay_protocol =
+      GetProtocolByString(local.relay_protocol());
+  log_description_->local_network_type = ConvertNetworkType(network->type());
+  log_description_->local_address_family =
+      GetAddressFamilyByInt(local.address().family());
+  log_description_->remote_candidate_type =
+      GetCandidateTypeByString(remote.type());
+  log_description_->remote_address_family =
+      GetAddressFamilyByInt(remote.address().family());
+  log_description_->candidate_pair_protocol =
+      GetProtocolByString(local.protocol());
+  return log_description_.value();
+}
+
+void Connection::LogCandidatePairConfig(
+    webrtc::IceCandidatePairConfigType type) {
+  if (ice_event_log_ == nullptr) {
+    return;
+  }
+  ice_event_log_->LogCandidatePairConfig(type, id(), ToLogDescription());
+}
+
+void Connection::LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
+                                       uint32_t transaction_id) {
+  if (ice_event_log_ == nullptr) {
+    return;
+  }
+  ice_event_log_->LogCandidatePairEvent(type, id(), transaction_id);
+}
+
+void Connection::OnConnectionRequestResponse(ConnectionRequest* request,
+                                             StunMessage* response) {
+  // Log at LS_INFO if we receive a ping response on an unwritable
+  // connection.
+  rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
+
+  int rtt = request->Elapsed();
+
+  if (RTC_LOG_CHECK_LEVEL_V(sev)) {
+    std::string pings;
+    PrintPingsSinceLastResponse(&pings, 5);
+    RTC_LOG_V(sev) << ToString() << ": Received STUN ping response, id="
+                   << rtc::hex_encode(request->id())
+                   << ", code=0"  // Makes logging easier to parse.
+                      ", rtt="
+                   << rtt << ", pings_since_last_response=" << pings;
+  }
+  ReceivedPingResponse(rtt, request->id());
+
+  stats_.recv_ping_responses++;
+  LogCandidatePairEvent(
+      webrtc::IceCandidatePairEventType::kCheckResponseReceived,
+      response->reduced_transaction_id());
+
+  MaybeUpdateLocalCandidate(request, response);
+}
+
+void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request,
+                                                  StunMessage* response) {
+  int error_code = response->GetErrorCodeValue();
+  RTC_LOG(LS_WARNING) << ToString() << ": Received STUN error response id="
+                      << rtc::hex_encode(request->id())
+                      << " code=" << error_code
+                      << " rtt=" << request->Elapsed();
+
+  if (error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE ||
+      error_code == STUN_ERROR_SERVER_ERROR ||
+      error_code == STUN_ERROR_UNAUTHORIZED) {
+    // Recoverable error, retry
+  } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) {
+    // Race failure, retry
+  } else if (error_code == STUN_ERROR_ROLE_CONFLICT) {
+    HandleRoleConflictFromPeer();
+  } else {
+    // This is not a valid connection.
+    RTC_LOG(LS_ERROR) << ToString()
+                      << ": Received STUN error response, code=" << error_code
+                      << "; killing connection";
+    FailAndDestroy();
+  }
+}
+
+void Connection::OnConnectionRequestTimeout(ConnectionRequest* request) {
+  // Log at LS_INFO if we miss a ping on a writable connection.
+  rtc::LoggingSeverity sev = writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
+  RTC_LOG_V(sev) << ToString() << ": Timing-out STUN ping "
+                 << rtc::hex_encode(request->id()) << " after "
+                 << request->Elapsed() << " ms";
+}
+
+void Connection::OnConnectionRequestSent(ConnectionRequest* request) {
+  // Log at LS_INFO if we send a ping on an unwritable connection.
+  rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
+  RTC_LOG_V(sev) << ToString()
+                 << ": Sent STUN ping, id=" << rtc::hex_encode(request->id())
+                 << ", use_candidate=" << use_candidate_attr()
+                 << ", nomination=" << nomination();
+  stats_.sent_ping_requests_total++;
+  LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckSent,
+                        request->reduced_transaction_id());
+  if (stats_.recv_ping_responses == 0) {
+    stats_.sent_ping_requests_before_first_response++;
+  }
+}
+
+void Connection::HandleRoleConflictFromPeer() {
+  port_->SignalRoleConflict(port_);
+}
+
+void Connection::MaybeSetRemoteIceParametersAndGeneration(
+    const IceParameters& ice_params,
+    int generation) {
+  if (remote_candidate_.username() == ice_params.ufrag &&
+      remote_candidate_.password().empty()) {
+    remote_candidate_.set_password(ice_params.pwd);
+  }
+  // TODO(deadbeef): A value of '0' for the generation is used for both
+  // generation 0 and "generation unknown". It should be changed to an
+  // absl::optional to fix this.
+  if (remote_candidate_.username() == ice_params.ufrag &&
+      remote_candidate_.password() == ice_params.pwd &&
+      remote_candidate_.generation() == 0) {
+    remote_candidate_.set_generation(generation);
+  }
+}
+
+void Connection::MaybeUpdatePeerReflexiveCandidate(
+    const Candidate& new_candidate) {
+  if (remote_candidate_.type() == PRFLX_PORT_TYPE &&
+      new_candidate.type() != PRFLX_PORT_TYPE &&
+      remote_candidate_.protocol() == new_candidate.protocol() &&
+      remote_candidate_.address() == new_candidate.address() &&
+      remote_candidate_.username() == new_candidate.username() &&
+      remote_candidate_.password() == new_candidate.password() &&
+      remote_candidate_.generation() == new_candidate.generation()) {
+    remote_candidate_ = new_candidate;
+  }
+}
+
+void Connection::OnMessage(rtc::Message* pmsg) {
+  RTC_DCHECK(pmsg->message_id == MSG_DELETE);
+  RTC_LOG(LS_INFO) << "Connection deleted with number of pings sent: "
+                   << num_pings_sent_;
+  SignalDestroyed(this);
+  delete this;
+}
+
+int64_t Connection::last_received() const {
+  return std::max(last_data_received_,
+                  std::max(last_ping_received_, last_ping_response_received_));
+}
+
+ConnectionInfo Connection::stats() {
+  stats_.recv_bytes_second = round(recv_rate_tracker_.ComputeRate());
+  stats_.recv_total_bytes = recv_rate_tracker_.TotalSampleCount();
+  stats_.sent_bytes_second = round(send_rate_tracker_.ComputeRate());
+  stats_.sent_total_bytes = send_rate_tracker_.TotalSampleCount();
+  stats_.receiving = receiving_;
+  stats_.writable = write_state_ == STATE_WRITABLE;
+  stats_.timeout = write_state_ == STATE_WRITE_TIMEOUT;
+  stats_.new_connection = !reported_;
+  stats_.rtt = rtt_;
+  stats_.key = this;
+  stats_.state = state_;
+  stats_.priority = priority();
+  stats_.nominated = nominated();
+  stats_.total_round_trip_time_ms = total_round_trip_time_ms_;
+  stats_.current_round_trip_time_ms = current_round_trip_time_ms_;
+  CopyCandidatesToStatsAndSanitizeIfNecessary();
+  return stats_;
+}
+
+void Connection::MaybeUpdateLocalCandidate(ConnectionRequest* request,
+                                           StunMessage* response) {
+  // RFC 5245
+  // The agent checks the mapped address from the STUN response.  If the
+  // transport address does not match any of the local candidates that the
+  // agent knows about, the mapped address represents a new candidate -- a
+  // peer reflexive candidate.
+  const StunAddressAttribute* addr =
+      response->GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
+  if (!addr) {
+    RTC_LOG(LS_WARNING)
+        << "Connection::OnConnectionRequestResponse - "
+           "No MAPPED-ADDRESS or XOR-MAPPED-ADDRESS found in the "
+           "stun response message";
+    return;
+  }
+
+  for (size_t i = 0; i < port_->Candidates().size(); ++i) {
+    if (port_->Candidates()[i].address() == addr->GetAddress()) {
+      if (local_candidate_index_ != i) {
+        RTC_LOG(LS_INFO) << ToString()
+                         << ": Updating local candidate type to srflx.";
+        local_candidate_index_ = i;
+        // SignalStateChange to force a re-sort in P2PTransportChannel as this
+        // Connection's local candidate has changed.
+        SignalStateChange(this);
+      }
+      return;
+    }
+  }
+
+  // RFC 5245
+  // Its priority is set equal to the value of the PRIORITY attribute
+  // in the Binding request.
+  const StunUInt32Attribute* priority_attr =
+      request->msg()->GetUInt32(STUN_ATTR_PRIORITY);
+  if (!priority_attr) {
+    RTC_LOG(LS_WARNING) << "Connection::OnConnectionRequestResponse - "
+                           "No STUN_ATTR_PRIORITY found in the "
+                           "stun response message";
+    return;
+  }
+  const uint32_t priority = priority_attr->value();
+  std::string id = rtc::CreateRandomString(8);
+
+  Candidate new_local_candidate;
+  new_local_candidate.set_id(id);
+  new_local_candidate.set_component(local_candidate().component());
+  new_local_candidate.set_type(PRFLX_PORT_TYPE);
+  new_local_candidate.set_protocol(local_candidate().protocol());
+  new_local_candidate.set_address(addr->GetAddress());
+  new_local_candidate.set_priority(priority);
+  new_local_candidate.set_username(local_candidate().username());
+  new_local_candidate.set_password(local_candidate().password());
+  new_local_candidate.set_network_name(local_candidate().network_name());
+  new_local_candidate.set_network_type(local_candidate().network_type());
+  new_local_candidate.set_related_address(local_candidate().address());
+  new_local_candidate.set_generation(local_candidate().generation());
+  new_local_candidate.set_foundation(Port::ComputeFoundation(
+      PRFLX_PORT_TYPE, local_candidate().protocol(),
+      local_candidate().relay_protocol(), local_candidate().address()));
+  new_local_candidate.set_network_id(local_candidate().network_id());
+  new_local_candidate.set_network_cost(local_candidate().network_cost());
+
+  // Change the local candidate of this Connection to the new prflx candidate.
+  RTC_LOG(LS_INFO) << ToString() << ": Updating local candidate type to prflx.";
+  local_candidate_index_ = port_->AddPrflxCandidate(new_local_candidate);
+
+  // SignalStateChange to force a re-sort in P2PTransportChannel as this
+  // Connection's local candidate has changed.
+  SignalStateChange(this);
+}
+
+void Connection::CopyCandidatesToStatsAndSanitizeIfNecessary() {
+  auto get_sanitized_copy = [](const Candidate& c) {
+    bool use_hostname_address = c.type() == LOCAL_PORT_TYPE;
+    bool filter_related_address = c.type() == STUN_PORT_TYPE;
+    return c.ToSanitizedCopy(use_hostname_address, filter_related_address);
+  };
+
+  if (port_->Network()->GetMdnsResponder() != nullptr) {
+    // When the mDNS obfuscation of local IPs is enabled, we sanitize local
+    // candidates.
+    stats_.local_candidate = get_sanitized_copy(local_candidate());
+  } else {
+    stats_.local_candidate = local_candidate();
+  }
+
+  if (!remote_candidate().address().hostname().empty()) {
+    // If the remote endpoint signaled us a hostname candidate, we assume it is
+    // supposed to be sanitized in the stats.
+    //
+    // A prflx remote candidate should not have a hostname set.
+    RTC_DCHECK(remote_candidate().type() != PRFLX_PORT_TYPE);
+    // A remote hostname candidate should have a resolved IP before we can form
+    // a candidate pair.
+    RTC_DCHECK(!remote_candidate().address().IsUnresolvedIP());
+    stats_.remote_candidate = get_sanitized_copy(remote_candidate());
+  } else {
+    stats_.remote_candidate = remote_candidate();
+  }
+}
+
+bool Connection::rtt_converged() const {
+  return rtt_samples_ > (RTT_RATIO + 1);
+}
+
+bool Connection::missing_responses(int64_t now) const {
+  if (pings_since_last_response_.empty()) {
+    return false;
+  }
+
+  int64_t waiting = now - pings_since_last_response_[0].sent_time;
+  return waiting > 2 * rtt();
+}
+
+ProxyConnection::ProxyConnection(Port* port,
+                                 size_t index,
+                                 const Candidate& remote_candidate)
+    : Connection(port, index, remote_candidate) {}
+
+int ProxyConnection::Send(const void* data,
+                          size_t size,
+                          const rtc::PacketOptions& options) {
+  stats_.sent_total_packets++;
+  int sent =
+      port_->SendTo(data, size, remote_candidate_.address(), options, true);
+  if (sent <= 0) {
+    RTC_DCHECK(sent < 0);
+    error_ = port_->GetError();
+    stats_.sent_discarded_packets++;
+  } else {
+    send_rate_tracker_.AddSamples(sent);
+  }
+  return sent;
+}
+
+int ProxyConnection::GetError() {
+  return error_;
+}
+
+}  // namespace cricket
diff --git a/p2p/base/connection.h b/p2p/base/connection.h
new file mode 100644
index 0000000..601d34c
--- /dev/null
+++ b/p2p/base/connection.h
@@ -0,0 +1,404 @@
+/*
+ *  Copyright 2019 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef P2P_BASE_CONNECTION_H_
+#define P2P_BASE_CONNECTION_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/candidate.h"
+#include "logging/rtc_event_log/ice_logger.h"
+#include "p2p/base/candidate_pair_interface.h"
+#include "p2p/base/connection_info.h"
+#include "p2p/base/stun.h"
+#include "p2p/base/stun_request.h"
+#include "p2p/base/transport_description.h"
+#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/message_handler.h"
+#include "rtc_base/rate_tracker.h"
+#include "rtc_base/third_party/sigslot/sigslot.h"
+
+namespace cricket {
+
+// Connection and Port has circular dependencies.
+// So we use forward declaration rather than include.
+class Port;
+
+// Forward declaration so that a ConnectionRequest can contain a Connection.
+class Connection;
+
+// A ConnectionRequest is a simple STUN ping used to determine writability.
+class ConnectionRequest : public StunRequest {
+ public:
+  explicit ConnectionRequest(Connection* connection);
+  void Prepare(StunMessage* request) override;
+  void OnResponse(StunMessage* response) override;
+  void OnErrorResponse(StunMessage* response) override;
+  void OnTimeout() override;
+  void OnSent() override;
+  int resend_delay() override;
+
+ private:
+  Connection* connection_;
+};
+
+// Represents a communication link between a port on the local client and a
+// port on the remote client.
+class Connection : public CandidatePairInterface,
+                   public rtc::MessageHandler,
+                   public sigslot::has_slots<> {
+ public:
+  struct SentPing {
+    SentPing(const std::string id, int64_t sent_time, uint32_t nomination)
+        : id(id), sent_time(sent_time), nomination(nomination) {}
+
+    std::string id;
+    int64_t sent_time;
+    uint32_t nomination;
+  };
+
+  ~Connection() override;
+
+  // A unique ID assigned when the connection is created.
+  uint32_t id() { return id_; }
+
+  // The local port where this connection sends and receives packets.
+  Port* port() { return port_; }
+  const Port* port() const { return port_; }
+
+  // Implementation of virtual methods in CandidatePairInterface.
+  // Returns the description of the local port
+  const Candidate& local_candidate() const override;
+  // Returns the description of the remote port to which we communicate.
+  const Candidate& remote_candidate() const override;
+
+  // Returns the pair priority.
+  uint64_t priority() const;
+
+  enum WriteState {
+    STATE_WRITABLE = 0,          // we have received ping responses recently
+    STATE_WRITE_UNRELIABLE = 1,  // we have had a few ping failures
+    STATE_WRITE_INIT = 2,        // we have yet to receive a ping response
+    STATE_WRITE_TIMEOUT = 3,     // we have had a large number of ping failures
+  };
+
+  WriteState write_state() const { return write_state_; }
+  bool writable() const { return write_state_ == STATE_WRITABLE; }
+  bool receiving() const { return receiving_; }
+
+  // Determines whether the connection has finished connecting.  This can only
+  // be false for TCP connections.
+  bool connected() const { return connected_; }
+  bool weak() const { return !(writable() && receiving() && connected()); }
+  bool active() const { return write_state_ != STATE_WRITE_TIMEOUT; }
+
+  // A connection is dead if it can be safely deleted.
+  bool dead(int64_t now) const;
+
+  // Estimate of the round-trip time over this connection.
+  int rtt() const { return rtt_; }
+
+  int unwritable_timeout() const;
+  void set_unwritable_timeout(const absl::optional<int>& value_ms) {
+    unwritable_timeout_ = value_ms;
+  }
+  int unwritable_min_checks() const;
+  void set_unwritable_min_checks(const absl::optional<int>& value) {
+    unwritable_min_checks_ = value;
+  }
+  int inactive_timeout() const;
+  void set_inactive_timeout(const absl::optional<int>& value) {
+    inactive_timeout_ = value;
+  }
+
+  // Gets the |ConnectionInfo| stats, where |best_connection| has not been
+  // populated (default value false).
+  ConnectionInfo stats();
+
+  sigslot::signal1<Connection*> SignalStateChange;
+
+  // Sent when the connection has decided that it is no longer of value.  It
+  // will delete itself immediately after this call.
+  sigslot::signal1<Connection*> SignalDestroyed;
+
+  // The connection can send and receive packets asynchronously.  This matches
+  // the interface of AsyncPacketSocket, which may use UDP or TCP under the
+  // covers.
+  virtual int Send(const void* data,
+                   size_t size,
+                   const rtc::PacketOptions& options) = 0;
+
+  // Error if Send() returns < 0
+  virtual int GetError() = 0;
+
+  sigslot::signal4<Connection*, const char*, size_t, int64_t> SignalReadPacket;
+
+  sigslot::signal1<Connection*> SignalReadyToSend;
+
+  // Called when a packet is received on this connection.
+  void OnReadPacket(const char* data, size_t size, int64_t packet_time_us);
+
+  // Called when the socket is currently able to send.
+  void OnReadyToSend();
+
+  // Called when a connection is determined to be no longer useful to us.  We
+  // still keep it around in case the other side wants to use it.  But we can
+  // safely stop pinging on it and we can allow it to time out if the other
+  // side stops using it as well.
+  bool pruned() const { return pruned_; }
+  void Prune();
+
+  bool use_candidate_attr() const { return use_candidate_attr_; }
+  void set_use_candidate_attr(bool enable);
+
+  void set_nomination(uint32_t value) { nomination_ = value; }
+
+  uint32_t remote_nomination() const { return remote_nomination_; }
+  // One or several pairs may be nominated based on if Regular or Aggressive
+  // Nomination is used. https://tools.ietf.org/html/rfc5245#section-8
+  // |nominated| is defined both for the controlling or controlled agent based
+  // on if a nomination has been pinged or acknowledged. The controlled agent
+  // gets its |remote_nomination_| set when pinged by the controlling agent with
+  // a nomination value. The controlling agent gets its |acked_nomination_| set
+  // when receiving a response to a nominating ping.
+  bool nominated() const { return acked_nomination_ || remote_nomination_; }
+  // Public for unit tests.
+  void set_remote_nomination(uint32_t remote_nomination) {
+    remote_nomination_ = remote_nomination;
+  }
+  // Public for unit tests.
+  uint32_t acked_nomination() const { return acked_nomination_; }
+
+  void set_remote_ice_mode(IceMode mode) { remote_ice_mode_ = mode; }
+
+  int receiving_timeout() const;
+  void set_receiving_timeout(absl::optional<int> receiving_timeout_ms) {
+    receiving_timeout_ = receiving_timeout_ms;
+  }
+
+  // Makes the connection go away.
+  void Destroy();
+
+  // Makes the connection go away, in a failed state.
+  void FailAndDestroy();
+
+  // Prunes the connection and sets its state to STATE_FAILED,
+  // It will not be used or send pings although it can still receive packets.
+  void FailAndPrune();
+
+  // Checks that the state of this connection is up-to-date.  The argument is
+  // the current time, which is compared against various timeouts.
+  void UpdateState(int64_t now);
+
+  // Called when this connection should try checking writability again.
+  int64_t last_ping_sent() const { return last_ping_sent_; }
+  void Ping(int64_t now);
+  void ReceivedPingResponse(int rtt, const std::string& request_id);
+  int64_t last_ping_response_received() const {
+    return last_ping_response_received_;
+  }
+  // Used to check if any STUN ping response has been received.
+  int rtt_samples() const { return rtt_samples_; }
+
+  // Called whenever a valid ping is received on this connection.  This is
+  // public because the connection intercepts the first ping for us.
+  int64_t last_ping_received() const { return last_ping_received_; }
+  void ReceivedPing();
+  // Handles the binding request; sends a response if this is a valid request.
+  void HandleBindingRequest(IceMessage* msg);
+
+  int64_t last_data_received() const { return last_data_received_; }
+
+  // Debugging description of this connection
+  std::string ToDebugId() const;
+  std::string ToString() const;
+  std::string ToSensitiveString() const;
+  // Structured description of this candidate pair.
+  const webrtc::IceCandidatePairDescription& ToLogDescription();
+  void set_ice_event_log(webrtc::IceEventLog* ice_event_log) {
+    ice_event_log_ = ice_event_log;
+  }
+  // Prints pings_since_last_response_ into a string.
+  void PrintPingsSinceLastResponse(std::string* pings, size_t max);
+
+  bool reported() const { return reported_; }
+  void set_reported(bool reported) { reported_ = reported; }
+  // The following two methods are only used for logging in ToString above, and
+  // this flag is set true by P2PTransportChannel for its selected candidate
+  // pair.
+  bool selected() const { return selected_; }
+  void set_selected(bool selected) { selected_ = selected; }
+
+  // This signal will be fired if this connection is nominated by the
+  // controlling side.
+  sigslot::signal1<Connection*> SignalNominated;
+
+  // Invoked when Connection receives STUN error response with 487 code.
+  void HandleRoleConflictFromPeer();
+
+  IceCandidatePairState state() const { return state_; }
+
+  int num_pings_sent() const { return num_pings_sent_; }
+
+  IceMode remote_ice_mode() const { return remote_ice_mode_; }
+
+  uint32_t ComputeNetworkCost() const;
+
+  // Update the ICE password and/or generation of the remote candidate if the
+  // ufrag in |params| matches the candidate's ufrag, and the
+  // candidate's password and/or ufrag has not been set.
+  void MaybeSetRemoteIceParametersAndGeneration(const IceParameters& params,
+                                                int generation);
+
+  // If |remote_candidate_| is peer reflexive and is equivalent to
+  // |new_candidate| except the type, update |remote_candidate_| to
+  // |new_candidate|.
+  void MaybeUpdatePeerReflexiveCandidate(const Candidate& new_candidate);
+
+  // Returns the last received time of any data, stun request, or stun
+  // response in milliseconds
+  int64_t last_received() const;
+  // Returns the last time when the connection changed its receiving state.
+  int64_t receiving_unchanged_since() const {
+    return receiving_unchanged_since_;
+  }
+
+  bool stable(int64_t now) const;
+
+ protected:
+  enum { MSG_DELETE = 0, MSG_FIRST_AVAILABLE };
+
+  // Constructs a new connection to the given remote port.
+  Connection(Port* port, size_t index, const Candidate& candidate);
+
+  // Called back when StunRequestManager has a stun packet to send
+  void OnSendStunPacket(const void* data, size_t size, StunRequest* req);
+
+  // Callbacks from ConnectionRequest
+  virtual void OnConnectionRequestResponse(ConnectionRequest* req,
+                                           StunMessage* response);
+  void OnConnectionRequestErrorResponse(ConnectionRequest* req,
+                                        StunMessage* response);
+  void OnConnectionRequestTimeout(ConnectionRequest* req);
+  void OnConnectionRequestSent(ConnectionRequest* req);
+
+  bool rtt_converged() const;
+
+  // If the response is not received within 2 * RTT, the response is assumed to
+  // be missing.
+  bool missing_responses(int64_t now) const;
+
+  // Changes the state and signals if necessary.
+  void set_write_state(WriteState value);
+  void UpdateReceiving(int64_t now);
+  void set_state(IceCandidatePairState state);
+  void set_connected(bool value);
+
+  uint32_t nomination() const { return nomination_; }
+
+  void OnMessage(rtc::Message* pmsg) override;
+
+  uint32_t id_;
+  Port* port_;
+  size_t local_candidate_index_;
+  Candidate remote_candidate_;
+
+  ConnectionInfo stats_;
+  rtc::RateTracker recv_rate_tracker_;
+  rtc::RateTracker send_rate_tracker_;
+
+ private:
+  // Update the local candidate based on the mapped address attribute.
+  // If the local candidate changed, fires SignalStateChange.
+  void MaybeUpdateLocalCandidate(ConnectionRequest* request,
+                                 StunMessage* response);
+
+  void CopyCandidatesToStatsAndSanitizeIfNecessary();
+
+  void LogCandidatePairConfig(webrtc::IceCandidatePairConfigType type);
+  void LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
+                             uint32_t transaction_id);
+
+  WriteState write_state_;
+  bool receiving_;
+  bool connected_;
+  bool pruned_;
+  bool selected_ = false;
+  // By default |use_candidate_attr_| flag will be true,
+  // as we will be using aggressive nomination.
+  // But when peer is ice-lite, this flag "must" be initialized to false and
+  // turn on when connection becomes "best connection".
+  bool use_candidate_attr_;
+  // Used by the controlling side to indicate that this connection will be
+  // selected for transmission if the peer supports ICE-renomination when this
+  // value is positive. A larger-value indicates that a connection is nominated
+  // later and should be selected by the controlled side with higher precedence.
+  // A zero-value indicates not nominating this connection.
+  uint32_t nomination_ = 0;
+  // The last nomination that has been acknowledged.
+  uint32_t acked_nomination_ = 0;
+  // Used by the controlled side to remember the nomination value received from
+  // the controlling side. When the peer does not support ICE re-nomination,
+  // its value will be 1 if the connection has been nominated.
+  uint32_t remote_nomination_ = 0;
+
+  IceMode remote_ice_mode_;
+  StunRequestManager requests_;
+  int rtt_;
+  int rtt_samples_ = 0;
+  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
+  uint64_t total_round_trip_time_ms_ = 0;
+  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
+  absl::optional<uint32_t> current_round_trip_time_ms_;
+  int64_t last_ping_sent_;      // last time we sent a ping to the other side
+  int64_t last_ping_received_;  // last time we received a ping from the other
+                                // side
+  int64_t last_data_received_;
+  int64_t last_ping_response_received_;
+  int64_t receiving_unchanged_since_ = 0;
+  std::vector<SentPing> pings_since_last_response_;
+
+  absl::optional<int> unwritable_timeout_;
+  absl::optional<int> unwritable_min_checks_;
+  absl::optional<int> inactive_timeout_;
+
+  bool reported_;
+  IceCandidatePairState state_;
+  // Time duration to switch from receiving to not receiving.
+  absl::optional<int> receiving_timeout_;
+  int64_t time_created_ms_;
+  int num_pings_sent_ = 0;
+
+  absl::optional<webrtc::IceCandidatePairDescription> log_description_;
+  webrtc::IceEventLog* ice_event_log_ = nullptr;
+
+  friend class Port;
+  friend class ConnectionRequest;
+};
+
+// ProxyConnection defers all the interesting work to the port.
+class ProxyConnection : public Connection {
+ public:
+  ProxyConnection(Port* port, size_t index, const Candidate& remote_candidate);
+
+  int Send(const void* data,
+           size_t size,
+           const rtc::PacketOptions& options) override;
+  int GetError() override;
+
+ private:
+  int error_ = 0;
+};
+
+}  // namespace cricket
+
+#endif  // P2P_BASE_CONNECTION_H_
diff --git a/p2p/base/connection_info.cc b/p2p/base/connection_info.cc
new file mode 100644
index 0000000..a4f8036
--- /dev/null
+++ b/p2p/base/connection_info.cc
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 2019 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "p2p/base/connection_info.h"
+
+namespace cricket {
+
+ConnectionInfo::ConnectionInfo()
+    : best_connection(false),
+      writable(false),
+      receiving(false),
+      timeout(false),
+      new_connection(false),
+      rtt(0),
+      sent_total_bytes(0),
+      sent_bytes_second(0),
+      sent_discarded_packets(0),
+      sent_total_packets(0),
+      sent_ping_requests_total(0),
+      sent_ping_requests_before_first_response(0),
+      sent_ping_responses(0),
+      recv_total_bytes(0),
+      recv_bytes_second(0),
+      recv_ping_requests(0),
+      recv_ping_responses(0),
+      key(nullptr),
+      state(IceCandidatePairState::WAITING),
+      priority(0),
+      nominated(false),
+      total_round_trip_time_ms(0) {}
+
+ConnectionInfo::ConnectionInfo(const ConnectionInfo&) = default;
+
+ConnectionInfo::~ConnectionInfo() = default;
+
+}  // namespace cricket
diff --git a/p2p/base/connection_info.h b/p2p/base/connection_info.h
new file mode 100644
index 0000000..a62e8ae
--- /dev/null
+++ b/p2p/base/connection_info.h
@@ -0,0 +1,79 @@
+/*
+ *  Copyright 2019 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef P2P_BASE_CONNECTION_INFO_H_
+#define P2P_BASE_CONNECTION_INFO_H_
+
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/candidate.h"
+
+namespace cricket {
+
+// States are from RFC 5245. http://tools.ietf.org/html/rfc5245#section-5.7.4
+enum class IceCandidatePairState {
+  WAITING = 0,  // Check has not been performed, Waiting pair on CL.
+  IN_PROGRESS,  // Check has been sent, transaction is in progress.
+  SUCCEEDED,    // Check already done, produced a successful result.
+  FAILED,       // Check for this connection failed.
+  // According to spec there should also be a frozen state, but nothing is ever
+  // frozen because we have not implemented ICE freezing logic.
+};
+
+// Stats that we can return about the connections for a transport channel.
+// TODO(hta): Rename to ConnectionStats
+struct ConnectionInfo {
+  ConnectionInfo();
+  ConnectionInfo(const ConnectionInfo&);
+  ~ConnectionInfo();
+
+  bool best_connection;      // Is this the best connection we have?
+  bool writable;             // Has this connection received a STUN response?
+  bool receiving;            // Has this connection received anything?
+  bool timeout;              // Has this connection timed out?
+  bool new_connection;       // Is this a newly created connection?
+  size_t rtt;                // The STUN RTT for this connection.
+  size_t sent_total_bytes;   // Total bytes sent on this connection.
+  size_t sent_bytes_second;  // Bps over the last measurement interval.
+  size_t sent_discarded_packets;  // Number of outgoing packets discarded due to
+                                  // socket errors.
+  size_t sent_total_packets;  // Number of total outgoing packets attempted for
+                              // sending.
+  size_t sent_ping_requests_total;  // Number of STUN ping request sent.
+  size_t sent_ping_requests_before_first_response;  // Number of STUN ping
+  // sent before receiving the first response.
+  size_t sent_ping_responses;  // Number of STUN ping response sent.
+
+  size_t recv_total_bytes;     // Total bytes received on this connection.
+  size_t recv_bytes_second;    // Bps over the last measurement interval.
+  size_t recv_ping_requests;   // Number of STUN ping request received.
+  size_t recv_ping_responses;  // Number of STUN ping response received.
+  Candidate local_candidate;   // The local candidate for this connection.
+  Candidate remote_candidate;  // The remote candidate for this connection.
+  void* key;                   // A static value that identifies this conn.
+  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-state
+  IceCandidatePairState state;
+  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-priority
+  uint64_t priority;
+  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-nominated
+  bool nominated;
+  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
+  uint64_t total_round_trip_time_ms;
+  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
+  absl::optional<uint32_t> current_round_trip_time_ms;
+};
+
+// Information about all the candidate pairs of a channel.
+typedef std::vector<ConnectionInfo> ConnectionInfos;
+
+}  // namespace cricket
+
+#endif  // P2P_BASE_CONNECTION_INFO_H_
diff --git a/p2p/base/ice_transport_internal.h b/p2p/base/ice_transport_internal.h
index 8f21673..93eec14 100644
--- a/p2p/base/ice_transport_internal.h
+++ b/p2p/base/ice_transport_internal.h
@@ -18,6 +18,7 @@
 #include "absl/types/optional.h"
 #include "api/candidate.h"
 #include "api/transport/enums.h"
+#include "p2p/base/connection.h"
 #include "p2p/base/packet_transport_internal.h"
 #include "p2p/base/port.h"
 #include "p2p/base/transport_description.h"
diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc
index 6b6727e..57e0e12 100644
--- a/p2p/base/p2p_transport_channel.cc
+++ b/p2p/base/p2p_transport_channel.cc
@@ -19,6 +19,7 @@
 #include "api/candidate.h"
 #include "logging/rtc_event_log/ice_logger.h"
 #include "p2p/base/candidate_pair_interface.h"
+#include "p2p/base/connection.h"
 #include "p2p/base/port.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/crc32.h"
diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc
index 623bb7c..e20f92d 100644
--- a/p2p/base/p2p_transport_channel_unittest.cc
+++ b/p2p/base/p2p_transport_channel_unittest.cc
@@ -13,6 +13,7 @@
 #include <utility>
 
 #include "absl/memory/memory.h"
+#include "p2p/base/connection.h"
 #include "p2p/base/fake_port_allocator.h"
 #include "p2p/base/ice_transport_internal.h"
 #include "p2p/base/mock_async_resolver.h"
diff --git a/p2p/base/port.cc b/p2p/base/port.cc
index b6fca24..9574323 100644
--- a/p2p/base/port.cc
+++ b/p2p/base/port.cc
@@ -19,6 +19,7 @@
 #include "absl/algorithm/container.h"
 #include "absl/memory/memory.h"
 #include "absl/strings/match.h"
+#include "p2p/base/connection.h"
 #include "p2p/base/port_allocator.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/crc32.h"
@@ -35,90 +36,6 @@
 
 namespace {
 
-// Determines whether we have seen at least the given maximum number of
-// pings fail to have a response.
-inline bool TooManyFailures(
-    const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
-    uint32_t maximum_failures,
-    int rtt_estimate,
-    int64_t now) {
-  // If we haven't sent that many pings, then we can't have failed that many.
-  if (pings_since_last_response.size() < maximum_failures)
-    return false;
-
-  // Check if the window in which we would expect a response to the ping has
-  // already elapsed.
-  int64_t expected_response_time =
-      pings_since_last_response[maximum_failures - 1].sent_time + rtt_estimate;
-  return now > expected_response_time;
-}
-
-// Determines whether we have gone too long without seeing any response.
-inline bool TooLongWithoutResponse(
-    const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
-    int64_t maximum_time,
-    int64_t now) {
-  if (pings_since_last_response.size() == 0)
-    return false;
-
-  auto first = pings_since_last_response[0];
-  return now > (first.sent_time + maximum_time);
-}
-
-// Helper methods for converting string values of log description fields to
-// enum.
-webrtc::IceCandidateType GetCandidateTypeByString(const std::string& type) {
-  if (type == cricket::LOCAL_PORT_TYPE) {
-    return webrtc::IceCandidateType::kLocal;
-  } else if (type == cricket::STUN_PORT_TYPE) {
-    return webrtc::IceCandidateType::kStun;
-  } else if (type == cricket::PRFLX_PORT_TYPE) {
-    return webrtc::IceCandidateType::kPrflx;
-  } else if (type == cricket::RELAY_PORT_TYPE) {
-    return webrtc::IceCandidateType::kRelay;
-  }
-  return webrtc::IceCandidateType::kUnknown;
-}
-
-webrtc::IceCandidatePairProtocol GetProtocolByString(
-    const std::string& protocol) {
-  if (protocol == cricket::UDP_PROTOCOL_NAME) {
-    return webrtc::IceCandidatePairProtocol::kUdp;
-  } else if (protocol == cricket::TCP_PROTOCOL_NAME) {
-    return webrtc::IceCandidatePairProtocol::kTcp;
-  } else if (protocol == cricket::SSLTCP_PROTOCOL_NAME) {
-    return webrtc::IceCandidatePairProtocol::kSsltcp;
-  } else if (protocol == cricket::TLS_PROTOCOL_NAME) {
-    return webrtc::IceCandidatePairProtocol::kTls;
-  }
-  return webrtc::IceCandidatePairProtocol::kUnknown;
-}
-
-webrtc::IceCandidatePairAddressFamily GetAddressFamilyByInt(
-    int address_family) {
-  if (address_family == AF_INET) {
-    return webrtc::IceCandidatePairAddressFamily::kIpv4;
-  } else if (address_family == AF_INET6) {
-    return webrtc::IceCandidatePairAddressFamily::kIpv6;
-  }
-  return webrtc::IceCandidatePairAddressFamily::kUnknown;
-}
-
-webrtc::IceCandidateNetworkType ConvertNetworkType(rtc::AdapterType type) {
-  if (type == rtc::ADAPTER_TYPE_ETHERNET) {
-    return webrtc::IceCandidateNetworkType::kEthernet;
-  } else if (type == rtc::ADAPTER_TYPE_LOOPBACK) {
-    return webrtc::IceCandidateNetworkType::kLoopback;
-  } else if (type == rtc::ADAPTER_TYPE_WIFI) {
-    return webrtc::IceCandidateNetworkType::kWifi;
-  } else if (type == rtc::ADAPTER_TYPE_VPN) {
-    return webrtc::IceCandidateNetworkType::kVpn;
-  } else if (type == rtc::ADAPTER_TYPE_CELLULAR) {
-    return webrtc::IceCandidateNetworkType::kCellular;
-  }
-  return webrtc::IceCandidateNetworkType::kUnknown;
-}
-
 rtc::PacketInfoProtocolType ConvertProtocolTypeToPacketInfoProtocolType(
     cricket::ProtocolType type) {
   switch (type) {
@@ -135,29 +52,10 @@
   }
 }
 
-// We will restrict RTT estimates (when used for determining state) to be
-// within a reasonable range.
-const int MINIMUM_RTT = 100;    // 0.1 seconds
-const int MAXIMUM_RTT = 60000;  // 60 seconds
-
-// When we don't have any RTT data, we have to pick something reasonable.  We
-// use a large value just in case the connection is really slow.
-const int DEFAULT_RTT = 3000;  // 3 seconds
-
-// Computes our estimate of the RTT given the current estimate.
-inline int ConservativeRTTEstimate(int rtt) {
-  return rtc::SafeClamp(2 * rtt, MINIMUM_RTT, MAXIMUM_RTT);
-}
-
-// Weighting of the old rtt value to new data.
-const int RTT_RATIO = 3;  // 3 : 1
-
 // The delay before we begin checking if this port is useless. We set
 // it to a little higher than a total STUN timeout.
 const int kPortTimeoutDelay = cricket::STUN_TOTAL_TIMEOUT + 5000;
 
-constexpr int64_t kMinExtraPingDelayMs = 100;
-
 }  // namespace
 
 namespace cricket {
@@ -196,16 +94,10 @@
 const char TCPTYPE_PASSIVE_STR[] = "passive";
 const char TCPTYPE_SIMOPEN_STR[] = "so";
 
-// Foundation:  An arbitrary string that is the same for two candidates
-//   that have the same type, base IP address, protocol (UDP, TCP,
-//   etc.), and STUN or TURN server.  If any of these are different,
-//   then the foundation will be different.  Two candidate pairs with
-//   the same foundation pairs are likely to have similar network
-//   characteristics.  Foundations are used in the frozen algorithm.
-static std::string ComputeFoundation(const std::string& type,
-                                     const std::string& protocol,
-                                     const std::string& relay_protocol,
-                                     const rtc::SocketAddress& base_address) {
+std::string Port::ComputeFoundation(const std::string& type,
+                                    const std::string& protocol,
+                                    const std::string& relay_protocol,
+                                    const rtc::SocketAddress& base_address) {
   rtc::StringBuilder sb;
   sb << type << base_address.ipaddr().ToString() << protocol << relay_protocol;
   return rtc::ToString(rtc::ComputeCrc32(sb.Release()));
@@ -221,34 +113,6 @@
 
 CandidateStats::~CandidateStats() = default;
 
-ConnectionInfo::ConnectionInfo()
-    : best_connection(false),
-      writable(false),
-      receiving(false),
-      timeout(false),
-      new_connection(false),
-      rtt(0),
-      sent_total_bytes(0),
-      sent_bytes_second(0),
-      sent_discarded_packets(0),
-      sent_total_packets(0),
-      sent_ping_requests_total(0),
-      sent_ping_requests_before_first_response(0),
-      sent_ping_responses(0),
-      recv_total_bytes(0),
-      recv_bytes_second(0),
-      recv_ping_requests(0),
-      recv_ping_responses(0),
-      key(nullptr),
-      state(IceCandidatePairState::WAITING),
-      priority(0),
-      nominated(false),
-      total_round_trip_time_ms(0) {}
-
-ConnectionInfo::ConnectionInfo(const ConnectionInfo&) = default;
-
-ConnectionInfo::~ConnectionInfo() = default;
-
 Port::Port(rtc::Thread* thread,
            const std::string& type,
            rtc::PacketSocketFactory* factory,
@@ -1001,997 +865,4 @@
   info->network_id = Network()->id();
 }
 
-// A ConnectionRequest is a simple STUN ping used to determine writability.
-class ConnectionRequest : public StunRequest {
- public:
-  explicit ConnectionRequest(Connection* connection)
-      : StunRequest(new IceMessage()), connection_(connection) {}
-
-  void Prepare(StunMessage* request) override {
-    request->SetType(STUN_BINDING_REQUEST);
-    std::string username;
-    connection_->port()->CreateStunUsername(
-        connection_->remote_candidate().username(), &username);
-    request->AddAttribute(absl::make_unique<StunByteStringAttribute>(
-        STUN_ATTR_USERNAME, username));
-
-    // connection_ already holds this ping, so subtract one from count.
-    if (connection_->port()->send_retransmit_count_attribute()) {
-      request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
-          STUN_ATTR_RETRANSMIT_COUNT,
-          static_cast<uint32_t>(connection_->pings_since_last_response_.size() -
-                                1)));
-    }
-    uint32_t network_info = connection_->port()->Network()->id();
-    network_info = (network_info << 16) | connection_->port()->network_cost();
-    request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
-        STUN_ATTR_NETWORK_INFO, network_info));
-
-    // Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
-    if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
-      request->AddAttribute(absl::make_unique<StunUInt64Attribute>(
-          STUN_ATTR_ICE_CONTROLLING, connection_->port()->IceTiebreaker()));
-      // We should have either USE_CANDIDATE attribute or ICE_NOMINATION
-      // attribute but not both. That was enforced in p2ptransportchannel.
-      if (connection_->use_candidate_attr()) {
-        request->AddAttribute(absl::make_unique<StunByteStringAttribute>(
-            STUN_ATTR_USE_CANDIDATE));
-      }
-      if (connection_->nomination() &&
-          connection_->nomination() != connection_->acked_nomination()) {
-        request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
-            STUN_ATTR_NOMINATION, connection_->nomination()));
-      }
-    } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) {
-      request->AddAttribute(absl::make_unique<StunUInt64Attribute>(
-          STUN_ATTR_ICE_CONTROLLED, connection_->port()->IceTiebreaker()));
-    } else {
-      RTC_NOTREACHED();
-    }
-
-    // Adding PRIORITY Attribute.
-    // Changing the type preference to Peer Reflexive and local preference
-    // and component id information is unchanged from the original priority.
-    // priority = (2^24)*(type preference) +
-    //           (2^8)*(local preference) +
-    //           (2^0)*(256 - component ID)
-    uint32_t type_preference =
-        (connection_->local_candidate().protocol() == TCP_PROTOCOL_NAME)
-            ? ICE_TYPE_PREFERENCE_PRFLX_TCP
-            : ICE_TYPE_PREFERENCE_PRFLX;
-    uint32_t prflx_priority =
-        type_preference << 24 |
-        (connection_->local_candidate().priority() & 0x00FFFFFF);
-    request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
-        STUN_ATTR_PRIORITY, prflx_priority));
-
-    // Adding Message Integrity attribute.
-    request->AddMessageIntegrity(connection_->remote_candidate().password());
-    // Adding Fingerprint.
-    request->AddFingerprint();
-  }
-
-  void OnResponse(StunMessage* response) override {
-    connection_->OnConnectionRequestResponse(this, response);
-  }
-
-  void OnErrorResponse(StunMessage* response) override {
-    connection_->OnConnectionRequestErrorResponse(this, response);
-  }
-
-  void OnTimeout() override { connection_->OnConnectionRequestTimeout(this); }
-
-  void OnSent() override {
-    connection_->OnConnectionRequestSent(this);
-    // Each request is sent only once.  After a single delay , the request will
-    // time out.
-    timeout_ = true;
-  }
-
-  int resend_delay() override { return CONNECTION_RESPONSE_TIMEOUT; }
-
- private:
-  Connection* connection_;
-};
-
-//
-// Connection
-//
-
-Connection::Connection(Port* port,
-                       size_t index,
-                       const Candidate& remote_candidate)
-    : id_(rtc::CreateRandomId()),
-      port_(port),
-      local_candidate_index_(index),
-      remote_candidate_(remote_candidate),
-      recv_rate_tracker_(100, 10u),
-      send_rate_tracker_(100, 10u),
-      write_state_(STATE_WRITE_INIT),
-      receiving_(false),
-      connected_(true),
-      pruned_(false),
-      use_candidate_attr_(false),
-      remote_ice_mode_(ICEMODE_FULL),
-      requests_(port->thread()),
-      rtt_(DEFAULT_RTT),
-      last_ping_sent_(0),
-      last_ping_received_(0),
-      last_data_received_(0),
-      last_ping_response_received_(0),
-      reported_(false),
-      state_(IceCandidatePairState::WAITING),
-      time_created_ms_(rtc::TimeMillis()) {
-  // All of our connections start in WAITING state.
-  // TODO(mallinath) - Start connections from STATE_FROZEN.
-  // Wire up to send stun packets
-  requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket);
-  RTC_LOG(LS_INFO) << ToString() << ": Connection created";
-}
-
-Connection::~Connection() {}
-
-const Candidate& Connection::local_candidate() const {
-  RTC_DCHECK(local_candidate_index_ < port_->Candidates().size());
-  return port_->Candidates()[local_candidate_index_];
-}
-
-const Candidate& Connection::remote_candidate() const {
-  return remote_candidate_;
-}
-
-uint64_t Connection::priority() const {
-  uint64_t priority = 0;
-  // RFC 5245 - 5.7.2.  Computing Pair Priority and Ordering Pairs
-  // Let G be the priority for the candidate provided by the controlling
-  // agent.  Let D be the priority for the candidate provided by the
-  // controlled agent.
-  // pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
-  IceRole role = port_->GetIceRole();
-  if (role != ICEROLE_UNKNOWN) {
-    uint32_t g = 0;
-    uint32_t d = 0;
-    if (role == ICEROLE_CONTROLLING) {
-      g = local_candidate().priority();
-      d = remote_candidate_.priority();
-    } else {
-      g = remote_candidate_.priority();
-      d = local_candidate().priority();
-    }
-    priority = std::min(g, d);
-    priority = priority << 32;
-    priority += 2 * std::max(g, d) + (g > d ? 1 : 0);
-  }
-  return priority;
-}
-
-void Connection::set_write_state(WriteState value) {
-  WriteState old_value = write_state_;
-  write_state_ = value;
-  if (value != old_value) {
-    RTC_LOG(LS_VERBOSE) << ToString() << ": set_write_state from: " << old_value
-                        << " to " << value;
-    SignalStateChange(this);
-  }
-}
-
-void Connection::UpdateReceiving(int64_t now) {
-  bool receiving;
-  if (last_ping_sent() < last_ping_response_received()) {
-    // We consider any candidate pair that has its last connectivity check
-    // acknowledged by a response as receiving, particularly for backup
-    // candidate pairs that send checks at a much slower pace than the selected
-    // one. Otherwise, a backup candidate pair constantly becomes not receiving
-    // as a side effect of a long ping interval, since we do not have a separate
-    // receiving timeout for backup candidate pairs. See
-    // IceConfig.ice_backup_candidate_pair_ping_interval,
-    // IceConfig.ice_connection_receiving_timeout and their default value.
-    receiving = true;
-  } else {
-    receiving =
-        last_received() > 0 && now <= last_received() + receiving_timeout();
-  }
-  if (receiving_ == receiving) {
-    return;
-  }
-  RTC_LOG(LS_VERBOSE) << ToString() << ": set_receiving to " << receiving;
-  receiving_ = receiving;
-  receiving_unchanged_since_ = now;
-  SignalStateChange(this);
-}
-
-void Connection::set_state(IceCandidatePairState state) {
-  IceCandidatePairState old_state = state_;
-  state_ = state;
-  if (state != old_state) {
-    RTC_LOG(LS_VERBOSE) << ToString() << ": set_state";
-  }
-}
-
-void Connection::set_connected(bool value) {
-  bool old_value = connected_;
-  connected_ = value;
-  if (value != old_value) {
-    RTC_LOG(LS_VERBOSE) << ToString() << ": Change connected_ to " << value;
-    SignalStateChange(this);
-  }
-}
-
-void Connection::set_use_candidate_attr(bool enable) {
-  use_candidate_attr_ = enable;
-}
-
-int Connection::unwritable_timeout() const {
-  return unwritable_timeout_.value_or(CONNECTION_WRITE_CONNECT_TIMEOUT);
-}
-
-int Connection::unwritable_min_checks() const {
-  return unwritable_min_checks_.value_or(CONNECTION_WRITE_CONNECT_FAILURES);
-}
-
-int Connection::inactive_timeout() const {
-  return inactive_timeout_.value_or(CONNECTION_WRITE_TIMEOUT);
-}
-
-int Connection::receiving_timeout() const {
-  return receiving_timeout_.value_or(WEAK_CONNECTION_RECEIVE_TIMEOUT);
-}
-
-void Connection::OnSendStunPacket(const void* data,
-                                  size_t size,
-                                  StunRequest* req) {
-  rtc::PacketOptions options(port_->StunDscpValue());
-  options.info_signaled_after_sent.packet_type =
-      rtc::PacketType::kIceConnectivityCheck;
-  auto err =
-      port_->SendTo(data, size, remote_candidate_.address(), options, false);
-  if (err < 0) {
-    RTC_LOG(LS_WARNING) << ToString()
-                        << ": Failed to send STUN ping "
-                           " err="
-                        << err << " id=" << rtc::hex_encode(req->id());
-  }
-}
-
-void Connection::OnReadPacket(const char* data,
-                              size_t size,
-                              int64_t packet_time_us) {
-  std::unique_ptr<IceMessage> msg;
-  std::string remote_ufrag;
-  const rtc::SocketAddress& addr(remote_candidate_.address());
-  if (!port_->GetStunMessage(data, size, addr, &msg, &remote_ufrag)) {
-    // The packet did not parse as a valid STUN message
-    // This is a data packet, pass it along.
-    last_data_received_ = rtc::TimeMillis();
-    UpdateReceiving(last_data_received_);
-    recv_rate_tracker_.AddSamples(size);
-    SignalReadPacket(this, data, size, packet_time_us);
-
-    // If timed out sending writability checks, start up again
-    if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) {
-      RTC_LOG(LS_WARNING)
-          << "Received a data packet on a timed-out Connection. "
-             "Resetting state to STATE_WRITE_INIT.";
-      set_write_state(STATE_WRITE_INIT);
-    }
-  } else if (!msg) {
-    // The packet was STUN, but failed a check and was handled internally.
-  } else {
-    // The packet is STUN and passed the Port checks.
-    // Perform our own checks to ensure this packet is valid.
-    // If this is a STUN request, then update the receiving bit and respond.
-    // If this is a STUN response, then update the writable bit.
-    // Log at LS_INFO if we receive a ping on an unwritable connection.
-    rtc::LoggingSeverity sev = (!writable() ? rtc::LS_INFO : rtc::LS_VERBOSE);
-    switch (msg->type()) {
-      case STUN_BINDING_REQUEST:
-        RTC_LOG_V(sev) << ToString() << ": Received STUN ping, id="
-                       << rtc::hex_encode(msg->transaction_id());
-
-        if (remote_ufrag == remote_candidate_.username()) {
-          HandleBindingRequest(msg.get());
-        } else {
-          // The packet had the right local username, but the remote username
-          // was not the right one for the remote address.
-          RTC_LOG(LS_ERROR)
-              << ToString()
-              << ": Received STUN request with bad remote username "
-              << remote_ufrag;
-          port_->SendBindingErrorResponse(msg.get(), addr,
-                                          STUN_ERROR_UNAUTHORIZED,
-                                          STUN_ERROR_REASON_UNAUTHORIZED);
-        }
-        break;
-
-      // Response from remote peer. Does it match request sent?
-      // This doesn't just check, it makes callbacks if transaction
-      // id's match.
-      case STUN_BINDING_RESPONSE:
-      case STUN_BINDING_ERROR_RESPONSE:
-        if (msg->ValidateMessageIntegrity(data, size,
-                                          remote_candidate().password())) {
-          requests_.CheckResponse(msg.get());
-        }
-        // Otherwise silently discard the response message.
-        break;
-
-      // Remote end point sent an STUN indication instead of regular binding
-      // request. In this case |last_ping_received_| will be updated but no
-      // response will be sent.
-      case STUN_BINDING_INDICATION:
-        ReceivedPing();
-        break;
-
-      default:
-        RTC_NOTREACHED();
-        break;
-    }
-  }
-}
-
-void Connection::HandleBindingRequest(IceMessage* msg) {
-  // This connection should now be receiving.
-  ReceivedPing();
-  if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing") &&
-      last_ping_response_received_ == 0) {
-    if (local_candidate().type() == RELAY_PORT_TYPE ||
-        local_candidate().type() == PRFLX_PORT_TYPE ||
-        remote_candidate().type() == RELAY_PORT_TYPE ||
-        remote_candidate().type() == PRFLX_PORT_TYPE) {
-      const int64_t now = rtc::TimeMillis();
-      if (last_ping_sent_ + kMinExtraPingDelayMs <= now) {
-        RTC_LOG(LS_INFO) << ToString()
-                         << "WebRTC-ExtraICEPing/Sending extra ping"
-                         << " last_ping_sent_: " << last_ping_sent_
-                         << " now: " << now
-                         << " (diff: " << (now - last_ping_sent_) << ")";
-        Ping(now);
-      } else {
-        RTC_LOG(LS_INFO) << ToString()
-                         << "WebRTC-ExtraICEPing/Not sending extra ping"
-                         << " last_ping_sent_: " << last_ping_sent_
-                         << " now: " << now
-                         << " (diff: " << (now - last_ping_sent_) << ")";
-      }
-    }
-  }
-
-  const rtc::SocketAddress& remote_addr = remote_candidate_.address();
-  const std::string& remote_ufrag = remote_candidate_.username();
-  // Check for role conflicts.
-  if (!port_->MaybeIceRoleConflict(remote_addr, msg, remote_ufrag)) {
-    // Received conflicting role from the peer.
-    RTC_LOG(LS_INFO) << "Received conflicting role from the peer.";
-    return;
-  }
-
-  stats_.recv_ping_requests++;
-  LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckReceived,
-                        msg->reduced_transaction_id());
-
-  // This is a validated stun request from remote peer.
-  port_->SendBindingResponse(msg, remote_addr);
-
-  // If it timed out on writing check, start up again
-  if (!pruned_ && write_state_ == STATE_WRITE_TIMEOUT) {
-    set_write_state(STATE_WRITE_INIT);
-  }
-
-  if (port_->GetIceRole() == ICEROLE_CONTROLLED) {
-    const StunUInt32Attribute* nomination_attr =
-        msg->GetUInt32(STUN_ATTR_NOMINATION);
-    uint32_t nomination = 0;
-    if (nomination_attr) {
-      nomination = nomination_attr->value();
-      if (nomination == 0) {
-        RTC_LOG(LS_ERROR) << "Invalid nomination: " << nomination;
-      }
-    } else {
-      const StunByteStringAttribute* use_candidate_attr =
-          msg->GetByteString(STUN_ATTR_USE_CANDIDATE);
-      if (use_candidate_attr) {
-        nomination = 1;
-      }
-    }
-    // We don't un-nominate a connection, so we only keep a larger nomination.
-    if (nomination > remote_nomination_) {
-      set_remote_nomination(nomination);
-      SignalNominated(this);
-    }
-  }
-  // Set the remote cost if the network_info attribute is available.
-  // Note: If packets are re-ordered, we may get incorrect network cost
-  // temporarily, but it should get the correct value shortly after that.
-  const StunUInt32Attribute* network_attr =
-      msg->GetUInt32(STUN_ATTR_NETWORK_INFO);
-  if (network_attr) {
-    uint32_t network_info = network_attr->value();
-    uint16_t network_cost = static_cast<uint16_t>(network_info);
-    if (network_cost != remote_candidate_.network_cost()) {
-      remote_candidate_.set_network_cost(network_cost);
-      // Network cost change will affect the connection ranking, so signal
-      // state change to force a re-sort in P2PTransportChannel.
-      SignalStateChange(this);
-    }
-  }
-}
-
-void Connection::OnReadyToSend() {
-  SignalReadyToSend(this);
-}
-
-void Connection::Prune() {
-  if (!pruned_ || active()) {
-    RTC_LOG(LS_INFO) << ToString() << ": Connection pruned";
-    pruned_ = true;
-    requests_.Clear();
-    set_write_state(STATE_WRITE_TIMEOUT);
-  }
-}
-
-void Connection::Destroy() {
-  // TODO(deadbeef, nisse): This may leak if an application closes a
-  // PeerConnection and then quickly destroys the PeerConnectionFactory (along
-  // with the networking thread on which this message is posted). Also affects
-  // tests, with a workaround in
-  // AutoSocketServerThread::~AutoSocketServerThread.
-  RTC_LOG(LS_VERBOSE) << ToString() << ": Connection destroyed";
-  port_->thread()->Post(RTC_FROM_HERE, this, MSG_DELETE);
-  LogCandidatePairConfig(webrtc::IceCandidatePairConfigType::kDestroyed);
-}
-
-void Connection::FailAndDestroy() {
-  set_state(IceCandidatePairState::FAILED);
-  Destroy();
-}
-
-void Connection::FailAndPrune() {
-  set_state(IceCandidatePairState::FAILED);
-  Prune();
-}
-
-void Connection::PrintPingsSinceLastResponse(std::string* s, size_t max) {
-  rtc::StringBuilder oss;
-  if (pings_since_last_response_.size() > max) {
-    for (size_t i = 0; i < max; i++) {
-      const SentPing& ping = pings_since_last_response_[i];
-      oss << rtc::hex_encode(ping.id) << " ";
-    }
-    oss << "... " << (pings_since_last_response_.size() - max) << " more";
-  } else {
-    for (const SentPing& ping : pings_since_last_response_) {
-      oss << rtc::hex_encode(ping.id) << " ";
-    }
-  }
-  *s = oss.str();
-}
-
-void Connection::UpdateState(int64_t now) {
-  int rtt = ConservativeRTTEstimate(rtt_);
-
-  if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE)) {
-    std::string pings;
-    PrintPingsSinceLastResponse(&pings, 5);
-    RTC_LOG(LS_VERBOSE) << ToString()
-                        << ": UpdateState()"
-                           ", ms since last received response="
-                        << now - last_ping_response_received_
-                        << ", ms since last received data="
-                        << now - last_data_received_ << ", rtt=" << rtt
-                        << ", pings_since_last_response=" << pings;
-  }
-
-  // Check the writable state.  (The order of these checks is important.)
-  //
-  // Before becoming unwritable, we allow for a fixed number of pings to fail
-  // (i.e., receive no response).  We also have to give the response time to
-  // get back, so we include a conservative estimate of this.
-  //
-  // Before timing out writability, we give a fixed amount of time.  This is to
-  // allow for changes in network conditions.
-
-  if ((write_state_ == STATE_WRITABLE) &&
-      TooManyFailures(pings_since_last_response_, unwritable_min_checks(), rtt,
-                      now) &&
-      TooLongWithoutResponse(pings_since_last_response_, unwritable_timeout(),
-                             now)) {
-    uint32_t max_pings = unwritable_min_checks();
-    RTC_LOG(LS_INFO) << ToString() << ": Unwritable after " << max_pings
-                     << " ping failures and "
-                     << now - pings_since_last_response_[0].sent_time
-                     << " ms without a response,"
-                        " ms since last received ping="
-                     << now - last_ping_received_
-                     << " ms since last received data="
-                     << now - last_data_received_ << " rtt=" << rtt;
-    set_write_state(STATE_WRITE_UNRELIABLE);
-  }
-  if ((write_state_ == STATE_WRITE_UNRELIABLE ||
-       write_state_ == STATE_WRITE_INIT) &&
-      TooLongWithoutResponse(pings_since_last_response_, inactive_timeout(),
-                             now)) {
-    RTC_LOG(LS_INFO) << ToString() << ": Timed out after "
-                     << now - pings_since_last_response_[0].sent_time
-                     << " ms without a response, rtt=" << rtt;
-    set_write_state(STATE_WRITE_TIMEOUT);
-  }
-
-  // Update the receiving state.
-  UpdateReceiving(now);
-  if (dead(now)) {
-    Destroy();
-  }
-}
-
-void Connection::Ping(int64_t now) {
-  last_ping_sent_ = now;
-  ConnectionRequest* req = new ConnectionRequest(this);
-  // If not using renomination, we use "1" to mean "nominated" and "0" to mean
-  // "not nominated". If using renomination, values greater than 1 are used for
-  // re-nominated pairs.
-  int nomination = use_candidate_attr_ ? 1 : 0;
-  if (nomination_ > 0) {
-    nomination = nomination_;
-  }
-  pings_since_last_response_.push_back(SentPing(req->id(), now, nomination));
-  RTC_LOG(LS_VERBOSE) << ToString() << ": Sending STUN ping, id="
-                      << rtc::hex_encode(req->id())
-                      << ", nomination=" << nomination_;
-  requests_.Send(req);
-  state_ = IceCandidatePairState::IN_PROGRESS;
-  num_pings_sent_++;
-}
-
-void Connection::ReceivedPing() {
-  last_ping_received_ = rtc::TimeMillis();
-  UpdateReceiving(last_ping_received_);
-}
-
-void Connection::ReceivedPingResponse(int rtt, const std::string& request_id) {
-  RTC_DCHECK_GE(rtt, 0);
-  // We've already validated that this is a STUN binding response with
-  // the correct local and remote username for this connection.
-  // So if we're not already, become writable. We may be bringing a pruned
-  // connection back to life, but if we don't really want it, we can always
-  // prune it again.
-  auto iter = absl::c_find_if(
-      pings_since_last_response_,
-      [request_id](const SentPing& ping) { return ping.id == request_id; });
-  if (iter != pings_since_last_response_.end() &&
-      iter->nomination > acked_nomination_) {
-    acked_nomination_ = iter->nomination;
-  }
-
-  total_round_trip_time_ms_ += rtt;
-  current_round_trip_time_ms_ = static_cast<uint32_t>(rtt);
-
-  pings_since_last_response_.clear();
-  last_ping_response_received_ = rtc::TimeMillis();
-  UpdateReceiving(last_ping_response_received_);
-  set_write_state(STATE_WRITABLE);
-  set_state(IceCandidatePairState::SUCCEEDED);
-  if (rtt_samples_ > 0) {
-    rtt_ = rtc::GetNextMovingAverage(rtt_, rtt, RTT_RATIO);
-  } else {
-    rtt_ = rtt;
-  }
-  rtt_samples_++;
-}
-
-bool Connection::dead(int64_t now) const {
-  if (last_received() > 0) {
-    // If it has ever received anything, we keep it alive until it hasn't
-    // received anything for DEAD_CONNECTION_RECEIVE_TIMEOUT. This covers the
-    // normal case of a successfully used connection that stops working. This
-    // also allows a remote peer to continue pinging over a locally inactive
-    // (pruned) connection.
-    return (now > (last_received() + DEAD_CONNECTION_RECEIVE_TIMEOUT));
-  }
-
-  if (active()) {
-    // If it has never received anything, keep it alive as long as it is
-    // actively pinging and not pruned. Otherwise, the connection might be
-    // deleted before it has a chance to ping. This is the normal case for a
-    // new connection that is pinging but hasn't received anything yet.
-    return false;
-  }
-
-  // If it has never received anything and is not actively pinging (pruned), we
-  // keep it around for at least MIN_CONNECTION_LIFETIME to prevent connections
-  // from being pruned too quickly during a network change event when two
-  // networks would be up simultaneously but only for a brief period.
-  return now > (time_created_ms_ + MIN_CONNECTION_LIFETIME);
-}
-
-bool Connection::stable(int64_t now) const {
-  // A connection is stable if it's RTT has converged and it isn't missing any
-  // responses.  We should send pings at a higher rate until the RTT converges
-  // and whenever a ping response is missing (so that we can detect
-  // unwritability faster)
-  return rtt_converged() && !missing_responses(now);
-}
-
-std::string Connection::ToDebugId() const {
-  return rtc::ToHex(reinterpret_cast<uintptr_t>(this));
-}
-
-uint32_t Connection::ComputeNetworkCost() const {
-  // TODO(honghaiz): Will add rtt as part of the network cost.
-  return port()->network_cost() + remote_candidate_.network_cost();
-}
-
-std::string Connection::ToString() const {
-  const absl::string_view CONNECT_STATE_ABBREV[2] = {
-      "-",  // not connected (false)
-      "C",  // connected (true)
-  };
-  const absl::string_view RECEIVE_STATE_ABBREV[2] = {
-      "-",  // not receiving (false)
-      "R",  // receiving (true)
-  };
-  const absl::string_view WRITE_STATE_ABBREV[4] = {
-      "W",  // STATE_WRITABLE
-      "w",  // STATE_WRITE_UNRELIABLE
-      "-",  // STATE_WRITE_INIT
-      "x",  // STATE_WRITE_TIMEOUT
-  };
-  const absl::string_view ICESTATE[4] = {
-      "W",  // STATE_WAITING
-      "I",  // STATE_INPROGRESS
-      "S",  // STATE_SUCCEEDED
-      "F"   // STATE_FAILED
-  };
-  const absl::string_view SELECTED_STATE_ABBREV[2] = {
-      "-",  // candidate pair not selected (false)
-      "S",  // selected (true)
-  };
-  const Candidate& local = local_candidate();
-  const Candidate& remote = remote_candidate();
-  rtc::StringBuilder ss;
-  ss << "Conn[" << ToDebugId() << ":" << port_->content_name() << ":"
-     << port_->Network()->ToString() << ":" << local.id() << ":"
-     << local.component() << ":" << local.generation() << ":" << local.type()
-     << ":" << local.protocol() << ":" << local.address().ToSensitiveString()
-     << "->" << remote.id() << ":" << remote.component() << ":"
-     << remote.priority() << ":" << remote.type() << ":" << remote.protocol()
-     << ":" << remote.address().ToSensitiveString() << "|"
-     << CONNECT_STATE_ABBREV[connected()] << RECEIVE_STATE_ABBREV[receiving()]
-     << WRITE_STATE_ABBREV[write_state()] << ICESTATE[static_cast<int>(state())]
-     << "|" << SELECTED_STATE_ABBREV[selected()] << "|" << remote_nomination()
-     << "|" << nomination() << "|" << priority() << "|";
-  if (rtt_ < DEFAULT_RTT) {
-    ss << rtt_ << "]";
-  } else {
-    ss << "-]";
-  }
-  return ss.Release();
-}
-
-std::string Connection::ToSensitiveString() const {
-  return ToString();
-}
-
-const webrtc::IceCandidatePairDescription& Connection::ToLogDescription() {
-  if (log_description_.has_value()) {
-    return log_description_.value();
-  }
-  const Candidate& local = local_candidate();
-  const Candidate& remote = remote_candidate();
-  const rtc::Network* network = port()->Network();
-  log_description_ = webrtc::IceCandidatePairDescription();
-  log_description_->local_candidate_type =
-      GetCandidateTypeByString(local.type());
-  log_description_->local_relay_protocol =
-      GetProtocolByString(local.relay_protocol());
-  log_description_->local_network_type = ConvertNetworkType(network->type());
-  log_description_->local_address_family =
-      GetAddressFamilyByInt(local.address().family());
-  log_description_->remote_candidate_type =
-      GetCandidateTypeByString(remote.type());
-  log_description_->remote_address_family =
-      GetAddressFamilyByInt(remote.address().family());
-  log_description_->candidate_pair_protocol =
-      GetProtocolByString(local.protocol());
-  return log_description_.value();
-}
-
-void Connection::LogCandidatePairConfig(
-    webrtc::IceCandidatePairConfigType type) {
-  if (ice_event_log_ == nullptr) {
-    return;
-  }
-  ice_event_log_->LogCandidatePairConfig(type, id(), ToLogDescription());
-}
-
-void Connection::LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
-                                       uint32_t transaction_id) {
-  if (ice_event_log_ == nullptr) {
-    return;
-  }
-  ice_event_log_->LogCandidatePairEvent(type, id(), transaction_id);
-}
-
-void Connection::OnConnectionRequestResponse(ConnectionRequest* request,
-                                             StunMessage* response) {
-  // Log at LS_INFO if we receive a ping response on an unwritable
-  // connection.
-  rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
-
-  int rtt = request->Elapsed();
-
-  if (RTC_LOG_CHECK_LEVEL_V(sev)) {
-    std::string pings;
-    PrintPingsSinceLastResponse(&pings, 5);
-    RTC_LOG_V(sev) << ToString() << ": Received STUN ping response, id="
-                   << rtc::hex_encode(request->id())
-                   << ", code=0"  // Makes logging easier to parse.
-                      ", rtt="
-                   << rtt << ", pings_since_last_response=" << pings;
-  }
-  ReceivedPingResponse(rtt, request->id());
-
-  stats_.recv_ping_responses++;
-  LogCandidatePairEvent(
-      webrtc::IceCandidatePairEventType::kCheckResponseReceived,
-      response->reduced_transaction_id());
-
-  MaybeUpdateLocalCandidate(request, response);
-}
-
-void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request,
-                                                  StunMessage* response) {
-  int error_code = response->GetErrorCodeValue();
-  RTC_LOG(LS_WARNING) << ToString() << ": Received STUN error response id="
-                      << rtc::hex_encode(request->id())
-                      << " code=" << error_code
-                      << " rtt=" << request->Elapsed();
-
-  if (error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE ||
-      error_code == STUN_ERROR_SERVER_ERROR ||
-      error_code == STUN_ERROR_UNAUTHORIZED) {
-    // Recoverable error, retry
-  } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) {
-    // Race failure, retry
-  } else if (error_code == STUN_ERROR_ROLE_CONFLICT) {
-    HandleRoleConflictFromPeer();
-  } else {
-    // This is not a valid connection.
-    RTC_LOG(LS_ERROR) << ToString()
-                      << ": Received STUN error response, code=" << error_code
-                      << "; killing connection";
-    FailAndDestroy();
-  }
-}
-
-void Connection::OnConnectionRequestTimeout(ConnectionRequest* request) {
-  // Log at LS_INFO if we miss a ping on a writable connection.
-  rtc::LoggingSeverity sev = writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
-  RTC_LOG_V(sev) << ToString() << ": Timing-out STUN ping "
-                 << rtc::hex_encode(request->id()) << " after "
-                 << request->Elapsed() << " ms";
-}
-
-void Connection::OnConnectionRequestSent(ConnectionRequest* request) {
-  // Log at LS_INFO if we send a ping on an unwritable connection.
-  rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
-  RTC_LOG_V(sev) << ToString()
-                 << ": Sent STUN ping, id=" << rtc::hex_encode(request->id())
-                 << ", use_candidate=" << use_candidate_attr()
-                 << ", nomination=" << nomination();
-  stats_.sent_ping_requests_total++;
-  LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckSent,
-                        request->reduced_transaction_id());
-  if (stats_.recv_ping_responses == 0) {
-    stats_.sent_ping_requests_before_first_response++;
-  }
-}
-
-void Connection::HandleRoleConflictFromPeer() {
-  port_->SignalRoleConflict(port_);
-}
-
-void Connection::MaybeSetRemoteIceParametersAndGeneration(
-    const IceParameters& ice_params,
-    int generation) {
-  if (remote_candidate_.username() == ice_params.ufrag &&
-      remote_candidate_.password().empty()) {
-    remote_candidate_.set_password(ice_params.pwd);
-  }
-  // TODO(deadbeef): A value of '0' for the generation is used for both
-  // generation 0 and "generation unknown". It should be changed to an
-  // absl::optional to fix this.
-  if (remote_candidate_.username() == ice_params.ufrag &&
-      remote_candidate_.password() == ice_params.pwd &&
-      remote_candidate_.generation() == 0) {
-    remote_candidate_.set_generation(generation);
-  }
-}
-
-void Connection::MaybeUpdatePeerReflexiveCandidate(
-    const Candidate& new_candidate) {
-  if (remote_candidate_.type() == PRFLX_PORT_TYPE &&
-      new_candidate.type() != PRFLX_PORT_TYPE &&
-      remote_candidate_.protocol() == new_candidate.protocol() &&
-      remote_candidate_.address() == new_candidate.address() &&
-      remote_candidate_.username() == new_candidate.username() &&
-      remote_candidate_.password() == new_candidate.password() &&
-      remote_candidate_.generation() == new_candidate.generation()) {
-    remote_candidate_ = new_candidate;
-  }
-}
-
-void Connection::OnMessage(rtc::Message* pmsg) {
-  RTC_DCHECK(pmsg->message_id == MSG_DELETE);
-  RTC_LOG(LS_INFO) << "Connection deleted with number of pings sent: "
-                   << num_pings_sent_;
-  SignalDestroyed(this);
-  delete this;
-}
-
-int64_t Connection::last_received() const {
-  return std::max(last_data_received_,
-                  std::max(last_ping_received_, last_ping_response_received_));
-}
-
-ConnectionInfo Connection::stats() {
-  stats_.recv_bytes_second = round(recv_rate_tracker_.ComputeRate());
-  stats_.recv_total_bytes = recv_rate_tracker_.TotalSampleCount();
-  stats_.sent_bytes_second = round(send_rate_tracker_.ComputeRate());
-  stats_.sent_total_bytes = send_rate_tracker_.TotalSampleCount();
-  stats_.receiving = receiving_;
-  stats_.writable = write_state_ == STATE_WRITABLE;
-  stats_.timeout = write_state_ == STATE_WRITE_TIMEOUT;
-  stats_.new_connection = !reported_;
-  stats_.rtt = rtt_;
-  stats_.key = this;
-  stats_.state = state_;
-  stats_.priority = priority();
-  stats_.nominated = nominated();
-  stats_.total_round_trip_time_ms = total_round_trip_time_ms_;
-  stats_.current_round_trip_time_ms = current_round_trip_time_ms_;
-  CopyCandidatesToStatsAndSanitizeIfNecessary();
-  return stats_;
-}
-
-void Connection::MaybeUpdateLocalCandidate(ConnectionRequest* request,
-                                           StunMessage* response) {
-  // RFC 5245
-  // The agent checks the mapped address from the STUN response.  If the
-  // transport address does not match any of the local candidates that the
-  // agent knows about, the mapped address represents a new candidate -- a
-  // peer reflexive candidate.
-  const StunAddressAttribute* addr =
-      response->GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
-  if (!addr) {
-    RTC_LOG(LS_WARNING)
-        << "Connection::OnConnectionRequestResponse - "
-           "No MAPPED-ADDRESS or XOR-MAPPED-ADDRESS found in the "
-           "stun response message";
-    return;
-  }
-
-  for (size_t i = 0; i < port_->Candidates().size(); ++i) {
-    if (port_->Candidates()[i].address() == addr->GetAddress()) {
-      if (local_candidate_index_ != i) {
-        RTC_LOG(LS_INFO) << ToString()
-                         << ": Updating local candidate type to srflx.";
-        local_candidate_index_ = i;
-        // SignalStateChange to force a re-sort in P2PTransportChannel as this
-        // Connection's local candidate has changed.
-        SignalStateChange(this);
-      }
-      return;
-    }
-  }
-
-  // RFC 5245
-  // Its priority is set equal to the value of the PRIORITY attribute
-  // in the Binding request.
-  const StunUInt32Attribute* priority_attr =
-      request->msg()->GetUInt32(STUN_ATTR_PRIORITY);
-  if (!priority_attr) {
-    RTC_LOG(LS_WARNING) << "Connection::OnConnectionRequestResponse - "
-                           "No STUN_ATTR_PRIORITY found in the "
-                           "stun response message";
-    return;
-  }
-  const uint32_t priority = priority_attr->value();
-  std::string id = rtc::CreateRandomString(8);
-
-  Candidate new_local_candidate;
-  new_local_candidate.set_id(id);
-  new_local_candidate.set_component(local_candidate().component());
-  new_local_candidate.set_type(PRFLX_PORT_TYPE);
-  new_local_candidate.set_protocol(local_candidate().protocol());
-  new_local_candidate.set_address(addr->GetAddress());
-  new_local_candidate.set_priority(priority);
-  new_local_candidate.set_username(local_candidate().username());
-  new_local_candidate.set_password(local_candidate().password());
-  new_local_candidate.set_network_name(local_candidate().network_name());
-  new_local_candidate.set_network_type(local_candidate().network_type());
-  new_local_candidate.set_related_address(local_candidate().address());
-  new_local_candidate.set_generation(local_candidate().generation());
-  new_local_candidate.set_foundation(ComputeFoundation(
-      PRFLX_PORT_TYPE, local_candidate().protocol(),
-      local_candidate().relay_protocol(), local_candidate().address()));
-  new_local_candidate.set_network_id(local_candidate().network_id());
-  new_local_candidate.set_network_cost(local_candidate().network_cost());
-
-  // Change the local candidate of this Connection to the new prflx candidate.
-  RTC_LOG(LS_INFO) << ToString() << ": Updating local candidate type to prflx.";
-  local_candidate_index_ = port_->AddPrflxCandidate(new_local_candidate);
-
-  // SignalStateChange to force a re-sort in P2PTransportChannel as this
-  // Connection's local candidate has changed.
-  SignalStateChange(this);
-}
-
-void Connection::CopyCandidatesToStatsAndSanitizeIfNecessary() {
-  auto get_sanitized_copy = [](const Candidate& c) {
-    bool use_hostname_address = c.type() == LOCAL_PORT_TYPE;
-    bool filter_related_address = c.type() == STUN_PORT_TYPE;
-    return c.ToSanitizedCopy(use_hostname_address, filter_related_address);
-  };
-
-  if (port_->Network()->GetMdnsResponder() != nullptr) {
-    // When the mDNS obfuscation of local IPs is enabled, we sanitize local
-    // candidates.
-    stats_.local_candidate = get_sanitized_copy(local_candidate());
-  } else {
-    stats_.local_candidate = local_candidate();
-  }
-
-  if (!remote_candidate().address().hostname().empty()) {
-    // If the remote endpoint signaled us a hostname candidate, we assume it is
-    // supposed to be sanitized in the stats.
-    //
-    // A prflx remote candidate should not have a hostname set.
-    RTC_DCHECK(remote_candidate().type() != PRFLX_PORT_TYPE);
-    // A remote hostname candidate should have a resolved IP before we can form
-    // a candidate pair.
-    RTC_DCHECK(!remote_candidate().address().IsUnresolvedIP());
-    stats_.remote_candidate = get_sanitized_copy(remote_candidate());
-  } else {
-    stats_.remote_candidate = remote_candidate();
-  }
-}
-
-bool Connection::rtt_converged() const {
-  return rtt_samples_ > (RTT_RATIO + 1);
-}
-
-bool Connection::missing_responses(int64_t now) const {
-  if (pings_since_last_response_.empty()) {
-    return false;
-  }
-
-  int64_t waiting = now - pings_since_last_response_[0].sent_time;
-  return waiting > 2 * rtt();
-}
-
-ProxyConnection::ProxyConnection(Port* port,
-                                 size_t index,
-                                 const Candidate& remote_candidate)
-    : Connection(port, index, remote_candidate) {}
-
-int ProxyConnection::Send(const void* data,
-                          size_t size,
-                          const rtc::PacketOptions& options) {
-  stats_.sent_total_packets++;
-  int sent =
-      port_->SendTo(data, size, remote_candidate_.address(), options, true);
-  if (sent <= 0) {
-    RTC_DCHECK(sent < 0);
-    error_ = port_->GetError();
-    stats_.sent_discarded_packets++;
-  } else {
-    send_rate_tracker_.AddSamples(sent);
-  }
-  return sent;
-}
-
-int ProxyConnection::GetError() {
-  return error_;
-}
-
 }  // namespace cricket
diff --git a/p2p/base/port.h b/p2p/base/port.h
index c42ee51..a0e2606 100644
--- a/p2p/base/port.h
+++ b/p2p/base/port.h
@@ -24,6 +24,8 @@
 #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
 #include "logging/rtc_event_log/ice_logger.h"
 #include "p2p/base/candidate_pair_interface.h"
+#include "p2p/base/connection.h"
+#include "p2p/base/connection_info.h"
 #include "p2p/base/p2p_constants.h"
 #include "p2p/base/packet_socket_factory.h"
 #include "p2p/base/port_interface.h"
@@ -43,9 +45,6 @@
 
 namespace cricket {
 
-class Connection;
-class ConnectionRequest;
-
 RTC_EXPORT extern const char LOCAL_PORT_TYPE[];
 RTC_EXPORT extern const char STUN_PORT_TYPE[];
 RTC_EXPORT extern const char PRFLX_PORT_TYPE[];
@@ -73,16 +72,6 @@
   ICE_TYPE_PREFERENCE_HOST = 126
 };
 
-// States are from RFC 5245. http://tools.ietf.org/html/rfc5245#section-5.7.4
-enum class IceCandidatePairState {
-  WAITING = 0,  // Check has not been performed, Waiting pair on CL.
-  IN_PROGRESS,  // Check has been sent, transaction is in progress.
-  SUCCEEDED,    // Check already done, produced a successful result.
-  FAILED,       // Check for this connection failed.
-  // According to spec there should also be a frozen state, but nothing is ever
-  // frozen because we have not implemented ICE freezing logic.
-};
-
 enum class MdnsNameRegistrationStatus {
   // IP concealment with mDNS is not enabled or the name registration process is
   // not started yet.
@@ -125,52 +114,6 @@
 
 typedef std::vector<CandidateStats> CandidateStatsList;
 
-// Stats that we can return about the connections for a transport channel.
-// TODO(hta): Rename to ConnectionStats
-struct ConnectionInfo {
-  ConnectionInfo();
-  ConnectionInfo(const ConnectionInfo&);
-  ~ConnectionInfo();
-
-  bool best_connection;      // Is this the best connection we have?
-  bool writable;             // Has this connection received a STUN response?
-  bool receiving;            // Has this connection received anything?
-  bool timeout;              // Has this connection timed out?
-  bool new_connection;       // Is this a newly created connection?
-  size_t rtt;                // The STUN RTT for this connection.
-  size_t sent_total_bytes;   // Total bytes sent on this connection.
-  size_t sent_bytes_second;  // Bps over the last measurement interval.
-  size_t sent_discarded_packets;  // Number of outgoing packets discarded due to
-                                  // socket errors.
-  size_t sent_total_packets;  // Number of total outgoing packets attempted for
-                              // sending.
-  size_t sent_ping_requests_total;  // Number of STUN ping request sent.
-  size_t sent_ping_requests_before_first_response;  // Number of STUN ping
-  // sent before receiving the first response.
-  size_t sent_ping_responses;  // Number of STUN ping response sent.
-
-  size_t recv_total_bytes;     // Total bytes received on this connection.
-  size_t recv_bytes_second;    // Bps over the last measurement interval.
-  size_t recv_ping_requests;   // Number of STUN ping request received.
-  size_t recv_ping_responses;  // Number of STUN ping response received.
-  Candidate local_candidate;   // The local candidate for this connection.
-  Candidate remote_candidate;  // The remote candidate for this connection.
-  void* key;                   // A static value that identifies this conn.
-  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-state
-  IceCandidatePairState state;
-  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-priority
-  uint64_t priority;
-  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-nominated
-  bool nominated;
-  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
-  uint64_t total_round_trip_time_ms;
-  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
-  absl::optional<uint32_t> current_round_trip_time_ms;
-};
-
-// Information about all the candidate pairs of a channel.
-typedef std::vector<ConnectionInfo> ConnectionInfos;
-
 const char* ProtoToString(ProtocolType proto);
 bool StringToProto(const char* value, ProtocolType* proto);
 
@@ -386,6 +329,17 @@
 
   void GetStunStats(absl::optional<StunStats>* stats) override {}
 
+  // Foundation:  An arbitrary string that is the same for two candidates
+  //   that have the same type, base IP address, protocol (UDP, TCP,
+  //   etc.), and STUN or TURN server.  If any of these are different,
+  //   then the foundation will be different.  Two candidate pairs with
+  //   the same foundation pairs are likely to have similar network
+  //   characteristics. Foundations are used in the frozen algorithm.
+  static std::string ComputeFoundation(const std::string& type,
+                                       const std::string& protocol,
+                                       const std::string& relay_protocol,
+                                       const rtc::SocketAddress& base_address);
+
  protected:
   enum { MSG_DESTROY_IF_DEAD = 0, MSG_FIRST_AVAILABLE };
 
@@ -520,354 +474,6 @@
   friend class Connection;
 };
 
-// Represents a communication link between a port on the local client and a
-// port on the remote client.
-class Connection : public CandidatePairInterface,
-                   public rtc::MessageHandler,
-                   public sigslot::has_slots<> {
- public:
-  struct SentPing {
-    SentPing(const std::string id, int64_t sent_time, uint32_t nomination)
-        : id(id), sent_time(sent_time), nomination(nomination) {}
-
-    std::string id;
-    int64_t sent_time;
-    uint32_t nomination;
-  };
-
-  ~Connection() override;
-
-  // A unique ID assigned when the connection is created.
-  uint32_t id() { return id_; }
-
-  // The local port where this connection sends and receives packets.
-  Port* port() { return port_; }
-  const Port* port() const { return port_; }
-
-  // Implementation of virtual methods in CandidatePairInterface.
-  // Returns the description of the local port
-  const Candidate& local_candidate() const override;
-  // Returns the description of the remote port to which we communicate.
-  const Candidate& remote_candidate() const override;
-
-  // Returns the pair priority.
-  uint64_t priority() const;
-
-  enum WriteState {
-    STATE_WRITABLE = 0,          // we have received ping responses recently
-    STATE_WRITE_UNRELIABLE = 1,  // we have had a few ping failures
-    STATE_WRITE_INIT = 2,        // we have yet to receive a ping response
-    STATE_WRITE_TIMEOUT = 3,     // we have had a large number of ping failures
-  };
-
-  WriteState write_state() const { return write_state_; }
-  bool writable() const { return write_state_ == STATE_WRITABLE; }
-  bool receiving() const { return receiving_; }
-
-  // Determines whether the connection has finished connecting.  This can only
-  // be false for TCP connections.
-  bool connected() const { return connected_; }
-  bool weak() const { return !(writable() && receiving() && connected()); }
-  bool active() const { return write_state_ != STATE_WRITE_TIMEOUT; }
-
-  // A connection is dead if it can be safely deleted.
-  bool dead(int64_t now) const;
-
-  // Estimate of the round-trip time over this connection.
-  int rtt() const { return rtt_; }
-
-  int unwritable_timeout() const;
-  void set_unwritable_timeout(const absl::optional<int>& value_ms) {
-    unwritable_timeout_ = value_ms;
-  }
-  int unwritable_min_checks() const;
-  void set_unwritable_min_checks(const absl::optional<int>& value) {
-    unwritable_min_checks_ = value;
-  }
-  int inactive_timeout() const;
-  void set_inactive_timeout(const absl::optional<int>& value) {
-    inactive_timeout_ = value;
-  }
-
-  // Gets the |ConnectionInfo| stats, where |best_connection| has not been
-  // populated (default value false).
-  ConnectionInfo stats();
-
-  sigslot::signal1<Connection*> SignalStateChange;
-
-  // Sent when the connection has decided that it is no longer of value.  It
-  // will delete itself immediately after this call.
-  sigslot::signal1<Connection*> SignalDestroyed;
-
-  // The connection can send and receive packets asynchronously.  This matches
-  // the interface of AsyncPacketSocket, which may use UDP or TCP under the
-  // covers.
-  virtual int Send(const void* data,
-                   size_t size,
-                   const rtc::PacketOptions& options) = 0;
-
-  // Error if Send() returns < 0
-  virtual int GetError() = 0;
-
-  sigslot::signal4<Connection*, const char*, size_t, int64_t> SignalReadPacket;
-
-  sigslot::signal1<Connection*> SignalReadyToSend;
-
-  // Called when a packet is received on this connection.
-  void OnReadPacket(const char* data, size_t size, int64_t packet_time_us);
-
-  // Called when the socket is currently able to send.
-  void OnReadyToSend();
-
-  // Called when a connection is determined to be no longer useful to us.  We
-  // still keep it around in case the other side wants to use it.  But we can
-  // safely stop pinging on it and we can allow it to time out if the other
-  // side stops using it as well.
-  bool pruned() const { return pruned_; }
-  void Prune();
-
-  bool use_candidate_attr() const { return use_candidate_attr_; }
-  void set_use_candidate_attr(bool enable);
-
-  void set_nomination(uint32_t value) { nomination_ = value; }
-
-  uint32_t remote_nomination() const { return remote_nomination_; }
-  // One or several pairs may be nominated based on if Regular or Aggressive
-  // Nomination is used. https://tools.ietf.org/html/rfc5245#section-8
-  // |nominated| is defined both for the controlling or controlled agent based
-  // on if a nomination has been pinged or acknowledged. The controlled agent
-  // gets its |remote_nomination_| set when pinged by the controlling agent with
-  // a nomination value. The controlling agent gets its |acked_nomination_| set
-  // when receiving a response to a nominating ping.
-  bool nominated() const { return acked_nomination_ || remote_nomination_; }
-  // Public for unit tests.
-  void set_remote_nomination(uint32_t remote_nomination) {
-    remote_nomination_ = remote_nomination;
-  }
-  // Public for unit tests.
-  uint32_t acked_nomination() const { return acked_nomination_; }
-
-  void set_remote_ice_mode(IceMode mode) { remote_ice_mode_ = mode; }
-
-  int receiving_timeout() const;
-  void set_receiving_timeout(absl::optional<int> receiving_timeout_ms) {
-    receiving_timeout_ = receiving_timeout_ms;
-  }
-
-  // Makes the connection go away.
-  void Destroy();
-
-  // Makes the connection go away, in a failed state.
-  void FailAndDestroy();
-
-  // Prunes the connection and sets its state to STATE_FAILED,
-  // It will not be used or send pings although it can still receive packets.
-  void FailAndPrune();
-
-  // Checks that the state of this connection is up-to-date.  The argument is
-  // the current time, which is compared against various timeouts.
-  void UpdateState(int64_t now);
-
-  // Called when this connection should try checking writability again.
-  int64_t last_ping_sent() const { return last_ping_sent_; }
-  void Ping(int64_t now);
-  void ReceivedPingResponse(int rtt, const std::string& request_id);
-  int64_t last_ping_response_received() const {
-    return last_ping_response_received_;
-  }
-  // Used to check if any STUN ping response has been received.
-  int rtt_samples() const { return rtt_samples_; }
-
-  // Called whenever a valid ping is received on this connection.  This is
-  // public because the connection intercepts the first ping for us.
-  int64_t last_ping_received() const { return last_ping_received_; }
-  void ReceivedPing();
-  // Handles the binding request; sends a response if this is a valid request.
-  void HandleBindingRequest(IceMessage* msg);
-
-  int64_t last_data_received() const { return last_data_received_; }
-
-  // Debugging description of this connection
-  std::string ToDebugId() const;
-  std::string ToString() const;
-  std::string ToSensitiveString() const;
-  // Structured description of this candidate pair.
-  const webrtc::IceCandidatePairDescription& ToLogDescription();
-  void set_ice_event_log(webrtc::IceEventLog* ice_event_log) {
-    ice_event_log_ = ice_event_log;
-  }
-  // Prints pings_since_last_response_ into a string.
-  void PrintPingsSinceLastResponse(std::string* pings, size_t max);
-
-  bool reported() const { return reported_; }
-  void set_reported(bool reported) { reported_ = reported; }
-  // The following two methods are only used for logging in ToString above, and
-  // this flag is set true by P2PTransportChannel for its selected candidate
-  // pair.
-  bool selected() const { return selected_; }
-  void set_selected(bool selected) { selected_ = selected; }
-
-  // This signal will be fired if this connection is nominated by the
-  // controlling side.
-  sigslot::signal1<Connection*> SignalNominated;
-
-  // Invoked when Connection receives STUN error response with 487 code.
-  void HandleRoleConflictFromPeer();
-
-  IceCandidatePairState state() const { return state_; }
-
-  int num_pings_sent() const { return num_pings_sent_; }
-
-  IceMode remote_ice_mode() const { return remote_ice_mode_; }
-
-  uint32_t ComputeNetworkCost() const;
-
-  // Update the ICE password and/or generation of the remote candidate if the
-  // ufrag in |params| matches the candidate's ufrag, and the
-  // candidate's password and/or ufrag has not been set.
-  void MaybeSetRemoteIceParametersAndGeneration(const IceParameters& params,
-                                                int generation);
-
-  // If |remote_candidate_| is peer reflexive and is equivalent to
-  // |new_candidate| except the type, update |remote_candidate_| to
-  // |new_candidate|.
-  void MaybeUpdatePeerReflexiveCandidate(const Candidate& new_candidate);
-
-  // Returns the last received time of any data, stun request, or stun
-  // response in milliseconds
-  int64_t last_received() const;
-  // Returns the last time when the connection changed its receiving state.
-  int64_t receiving_unchanged_since() const {
-    return receiving_unchanged_since_;
-  }
-
-  bool stable(int64_t now) const;
-
- protected:
-  enum { MSG_DELETE = 0, MSG_FIRST_AVAILABLE };
-
-  // Constructs a new connection to the given remote port.
-  Connection(Port* port, size_t index, const Candidate& candidate);
-
-  // Called back when StunRequestManager has a stun packet to send
-  void OnSendStunPacket(const void* data, size_t size, StunRequest* req);
-
-  // Callbacks from ConnectionRequest
-  virtual void OnConnectionRequestResponse(ConnectionRequest* req,
-                                           StunMessage* response);
-  void OnConnectionRequestErrorResponse(ConnectionRequest* req,
-                                        StunMessage* response);
-  void OnConnectionRequestTimeout(ConnectionRequest* req);
-  void OnConnectionRequestSent(ConnectionRequest* req);
-
-  bool rtt_converged() const;
-
-  // If the response is not received within 2 * RTT, the response is assumed to
-  // be missing.
-  bool missing_responses(int64_t now) const;
-
-  // Changes the state and signals if necessary.
-  void set_write_state(WriteState value);
-  void UpdateReceiving(int64_t now);
-  void set_state(IceCandidatePairState state);
-  void set_connected(bool value);
-
-  uint32_t nomination() const { return nomination_; }
-
-  void OnMessage(rtc::Message* pmsg) override;
-
-  uint32_t id_;
-  Port* port_;
-  size_t local_candidate_index_;
-  Candidate remote_candidate_;
-
-  ConnectionInfo stats_;
-  rtc::RateTracker recv_rate_tracker_;
-  rtc::RateTracker send_rate_tracker_;
-
- private:
-  // Update the local candidate based on the mapped address attribute.
-  // If the local candidate changed, fires SignalStateChange.
-  void MaybeUpdateLocalCandidate(ConnectionRequest* request,
-                                 StunMessage* response);
-
-  void CopyCandidatesToStatsAndSanitizeIfNecessary();
-
-  void LogCandidatePairConfig(webrtc::IceCandidatePairConfigType type);
-  void LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
-                             uint32_t transaction_id);
-
-  WriteState write_state_;
-  bool receiving_;
-  bool connected_;
-  bool pruned_;
-  bool selected_ = false;
-  // By default |use_candidate_attr_| flag will be true,
-  // as we will be using aggressive nomination.
-  // But when peer is ice-lite, this flag "must" be initialized to false and
-  // turn on when connection becomes "best connection".
-  bool use_candidate_attr_;
-  // Used by the controlling side to indicate that this connection will be
-  // selected for transmission if the peer supports ICE-renomination when this
-  // value is positive. A larger-value indicates that a connection is nominated
-  // later and should be selected by the controlled side with higher precedence.
-  // A zero-value indicates not nominating this connection.
-  uint32_t nomination_ = 0;
-  // The last nomination that has been acknowledged.
-  uint32_t acked_nomination_ = 0;
-  // Used by the controlled side to remember the nomination value received from
-  // the controlling side. When the peer does not support ICE re-nomination,
-  // its value will be 1 if the connection has been nominated.
-  uint32_t remote_nomination_ = 0;
-
-  IceMode remote_ice_mode_;
-  StunRequestManager requests_;
-  int rtt_;
-  int rtt_samples_ = 0;
-  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
-  uint64_t total_round_trip_time_ms_ = 0;
-  // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
-  absl::optional<uint32_t> current_round_trip_time_ms_;
-  int64_t last_ping_sent_;      // last time we sent a ping to the other side
-  int64_t last_ping_received_;  // last time we received a ping from the other
-                                // side
-  int64_t last_data_received_;
-  int64_t last_ping_response_received_;
-  int64_t receiving_unchanged_since_ = 0;
-  std::vector<SentPing> pings_since_last_response_;
-
-  absl::optional<int> unwritable_timeout_;
-  absl::optional<int> unwritable_min_checks_;
-  absl::optional<int> inactive_timeout_;
-
-  bool reported_;
-  IceCandidatePairState state_;
-  // Time duration to switch from receiving to not receiving.
-  absl::optional<int> receiving_timeout_;
-  int64_t time_created_ms_;
-  int num_pings_sent_ = 0;
-
-  absl::optional<webrtc::IceCandidatePairDescription> log_description_;
-  webrtc::IceEventLog* ice_event_log_ = nullptr;
-
-  friend class Port;
-  friend class ConnectionRequest;
-};
-
-// ProxyConnection defers all the interesting work to the port.
-class ProxyConnection : public Connection {
- public:
-  ProxyConnection(Port* port, size_t index, const Candidate& remote_candidate);
-
-  int Send(const void* data,
-           size_t size,
-           const rtc::PacketOptions& options) override;
-  int GetError() override;
-
- private:
-  int error_ = 0;
-};
-
 }  // namespace cricket
 
 #endif  // P2P_BASE_PORT_H_
diff --git a/p2p/base/relay_port.cc b/p2p/base/relay_port.cc
index ebaefa7..9aecb6e 100644
--- a/p2p/base/relay_port.cc
+++ b/p2p/base/relay_port.cc
@@ -11,6 +11,7 @@
 #include <string.h>
 #include <algorithm>
 
+#include "p2p/base/connection.h"
 #include "p2p/base/relay_port.h"
 #include "p2p/base/stun.h"
 #include "p2p/base/stun_request.h"
diff --git a/p2p/base/stun_port.cc b/p2p/base/stun_port.cc
index 03d9553..65112f2 100644
--- a/p2p/base/stun_port.cc
+++ b/p2p/base/stun_port.cc
@@ -13,6 +13,7 @@
 #include <utility>
 #include <vector>
 
+#include "p2p/base/connection.h"
 #include "p2p/base/p2p_constants.h"
 #include "p2p/base/port_allocator.h"
 #include "p2p/base/stun.h"
diff --git a/p2p/base/tcp_port.h b/p2p/base/tcp_port.h
index 60c2d04..f6953c0 100644
--- a/p2p/base/tcp_port.h
+++ b/p2p/base/tcp_port.h
@@ -16,6 +16,7 @@
 #include <string>
 
 #include "absl/memory/memory.h"
+#include "p2p/base/connection.h"
 #include "p2p/base/port.h"
 #include "rtc_base/async_packet_socket.h"
 
diff --git a/p2p/base/turn_port.cc b/p2p/base/turn_port.cc
index 098aefb..bcf574e 100644
--- a/p2p/base/turn_port.cc
+++ b/p2p/base/turn_port.cc
@@ -17,6 +17,7 @@
 #include "absl/algorithm/container.h"
 #include "absl/memory/memory.h"
 #include "absl/types/optional.h"
+#include "p2p/base/connection.h"
 #include "p2p/base/stun.h"
 #include "rtc_base/async_packet_socket.h"
 #include "rtc_base/byte_order.h"
diff --git a/p2p/base/turn_port_unittest.cc b/p2p/base/turn_port_unittest.cc
index b21a257..e713b2a 100644
--- a/p2p/base/turn_port_unittest.cc
+++ b/p2p/base/turn_port_unittest.cc
@@ -19,6 +19,7 @@
 #include "absl/types/optional.h"
 #include "api/units/time_delta.h"
 #include "p2p/base/basic_packet_socket_factory.h"
+#include "p2p/base/connection.h"
 #include "p2p/base/p2p_constants.h"
 #include "p2p/base/port_allocator.h"
 #include "p2p/base/stun_port.h"