[Connection] Construct ping/connection requests in one step.

This moves the construction of StunMessage instances for
ConnectionRequest, outside of the Prepare() method.

Following this, removing Construct()+Prepare() is relatively
straight forward.

Bug: none
Change-Id: Ibcf0510cef30a6e648005b43602c7ae1fb06729e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264558
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Jonas Oreland <jonaso@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37122}
diff --git a/api/transport/stun.cc b/api/transport/stun.cc
index bc88dcc..e7cb5ed 100644
--- a/api/transport/stun.cc
+++ b/api/transport/stun.cc
@@ -362,22 +362,19 @@
                 mi_attr_size) == 0;
 }
 
-bool StunMessage::AddMessageIntegrity(const std::string& password) {
+bool StunMessage::AddMessageIntegrity(absl::string_view password) {
   return AddMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
-                                   kStunMessageIntegritySize, password.c_str(),
-                                   password.size());
+                                   kStunMessageIntegritySize, password);
 }
 
 bool StunMessage::AddMessageIntegrity32(absl::string_view password) {
   return AddMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32,
-                                   kStunMessageIntegrity32Size, password.data(),
-                                   password.length());
+                                   kStunMessageIntegrity32Size, password);
 }
 
 bool StunMessage::AddMessageIntegrityOfType(int attr_type,
                                             size_t attr_size,
-                                            const char* key,
-                                            size_t keylen) {
+                                            absl::string_view key) {
   // Add the attribute with a dummy value. Since this is a known attribute, it
   // can't fail.
   RTC_DCHECK(attr_size <= kStunMessageIntegritySize);
@@ -394,8 +391,9 @@
   int msg_len_for_hmac = static_cast<int>(
       buf.Length() - kStunAttributeHeaderSize - msg_integrity_attr->length());
   char hmac[kStunMessageIntegritySize];
-  size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1, key, keylen, buf.Data(),
-                                msg_len_for_hmac, hmac, sizeof(hmac));
+  size_t ret =
+      rtc::ComputeHmac(rtc::DIGEST_SHA_1, key.data(), key.size(), buf.Data(),
+                       msg_len_for_hmac, hmac, sizeof(hmac));
   RTC_DCHECK(ret == sizeof(hmac));
   if (ret != sizeof(hmac)) {
     RTC_LOG(LS_ERROR) << "HMAC computation failed. Message-Integrity "
@@ -405,7 +403,7 @@
 
   // Insert correct HMAC into the attribute.
   msg_integrity_attr->CopyBytes(hmac, attr_size);
-  password_.assign(key, keylen);
+  password_ = std::string(key);
   integrity_ = IntegrityStatus::kIntegrityOk;
   return true;
 }
@@ -1006,9 +1004,9 @@
     : StunAttribute(type, 0), bytes_(NULL) {}
 
 StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
-                                                 const std::string& str)
+                                                 absl::string_view str)
     : StunAttribute(type, 0), bytes_(NULL) {
-  CopyBytes(str.c_str(), str.size());
+  CopyBytes(str);
 }
 
 StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
@@ -1029,8 +1027,10 @@
   return STUN_VALUE_BYTE_STRING;
 }
 
-void StunByteStringAttribute::CopyBytes(const char* bytes) {
-  CopyBytes(bytes, strlen(bytes));
+void StunByteStringAttribute::CopyBytes(absl::string_view bytes) {
+  char* new_bytes = new char[bytes.size()];
+  memcpy(new_bytes, bytes.data(), bytes.size());
+  SetBytes(new_bytes, bytes.size());
 }
 
 void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) {
diff --git a/api/transport/stun.h b/api/transport/stun.h
index cf9c101..43e2a08 100644
--- a/api/transport/stun.h
+++ b/api/transport/stun.h
@@ -233,7 +233,7 @@
   }
 
   // Adds a MESSAGE-INTEGRITY attribute that is valid for the current message.
-  bool AddMessageIntegrity(const std::string& password);
+  bool AddMessageIntegrity(absl::string_view password);
 
   // Adds a STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32 attribute that is valid for the
   // current message.
@@ -316,8 +316,7 @@
   static bool IsValidTransactionId(absl::string_view transaction_id);
   bool AddMessageIntegrityOfType(int mi_attr_type,
                                  size_t mi_attr_size,
-                                 const char* key,
-                                 size_t keylen);
+                                 absl::string_view key);
   static bool ValidateMessageIntegrityOfType(int mi_attr_type,
                                              size_t mi_attr_size,
                                              const char* data,
@@ -507,7 +506,7 @@
 class StunByteStringAttribute : public StunAttribute {
  public:
   explicit StunByteStringAttribute(uint16_t type);
-  StunByteStringAttribute(uint16_t type, const std::string& str);
+  StunByteStringAttribute(uint16_t type, absl::string_view str);
   StunByteStringAttribute(uint16_t type, const void* bytes, size_t length);
   StunByteStringAttribute(uint16_t type, uint16_t length);
   ~StunByteStringAttribute() override;
@@ -515,10 +514,16 @@
   StunAttributeValueType value_type() const override;
 
   const char* bytes() const { return bytes_; }
-  std::string GetString() const { return std::string(bytes_, length()); }
+  absl::string_view string_view() const {
+    return absl::string_view(bytes_, length());
+  }
 
-  void CopyBytes(const char* bytes);  // uses strlen
+  [[deprecated]] std::string GetString() const {
+    return std::string(bytes_, length());
+  }
+
   void CopyBytes(const void* bytes, size_t length);
+  void CopyBytes(absl::string_view bytes);
 
   uint8_t GetByte(size_t index) const;
   void SetByte(size_t index, uint8_t value);
diff --git a/api/transport/stun_unittest.cc b/api/transport/stun_unittest.cc
index 14cb1fa..54f91c5 100644
--- a/api/transport/stun_unittest.cc
+++ b/api/transport/stun_unittest.cc
@@ -650,12 +650,12 @@
   const StunByteStringAttribute* software =
       msg.GetByteString(STUN_ATTR_SOFTWARE);
   ASSERT_TRUE(software != NULL);
-  EXPECT_EQ(kRfc5769SampleMsgClientSoftware, software->GetString());
+  EXPECT_EQ(kRfc5769SampleMsgClientSoftware, software->string_view());
 
   const StunByteStringAttribute* username =
       msg.GetByteString(STUN_ATTR_USERNAME);
   ASSERT_TRUE(username != NULL);
-  EXPECT_EQ(kRfc5769SampleMsgUsername, username->GetString());
+  EXPECT_EQ(kRfc5769SampleMsgUsername, username->string_view());
 
   // Actual M-I value checked in a later test.
   ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
@@ -677,7 +677,7 @@
   const StunByteStringAttribute* software =
       msg.GetByteString(STUN_ATTR_SOFTWARE);
   ASSERT_TRUE(software != NULL);
-  EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->GetString());
+  EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->string_view());
 
   const StunAddressAttribute* mapped_address =
       msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
@@ -700,7 +700,7 @@
   const StunByteStringAttribute* software =
       msg.GetByteString(STUN_ATTR_SOFTWARE);
   ASSERT_TRUE(software != NULL);
-  EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->GetString());
+  EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->string_view());
 
   const StunAddressAttribute* mapped_address =
       msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
@@ -723,15 +723,15 @@
   const StunByteStringAttribute* username =
       msg.GetByteString(STUN_ATTR_USERNAME);
   ASSERT_TRUE(username != NULL);
-  EXPECT_EQ(kRfc5769SampleMsgWithAuthUsername, username->GetString());
+  EXPECT_EQ(kRfc5769SampleMsgWithAuthUsername, username->string_view());
 
   const StunByteStringAttribute* nonce = msg.GetByteString(STUN_ATTR_NONCE);
   ASSERT_TRUE(nonce != NULL);
-  EXPECT_EQ(kRfc5769SampleMsgWithAuthNonce, nonce->GetString());
+  EXPECT_EQ(kRfc5769SampleMsgWithAuthNonce, nonce->string_view());
 
   const StunByteStringAttribute* realm = msg.GetByteString(STUN_ATTR_REALM);
   ASSERT_TRUE(realm != NULL);
-  EXPECT_EQ(kRfc5769SampleMsgWithAuthRealm, realm->GetString());
+  EXPECT_EQ(kRfc5769SampleMsgWithAuthRealm, realm->string_view());
 
   // No fingerprint, actual M-I checked in later tests.
   ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
@@ -1013,7 +1013,7 @@
   const StunByteStringAttribute* username =
       msg.GetByteString(STUN_ATTR_USERNAME);
   ASSERT_TRUE(username != NULL);
-  EXPECT_EQ(kTestUserName1, username->GetString());
+  EXPECT_EQ(kTestUserName1, username->string_view());
 }
 
 TEST_F(StunTest, ReadPaddedByteStringAttribute) {
@@ -1026,7 +1026,7 @@
   const StunByteStringAttribute* username =
       msg.GetByteString(STUN_ATTR_USERNAME);
   ASSERT_TRUE(username != NULL);
-  EXPECT_EQ(kTestUserName2, username->GetString());
+  EXPECT_EQ(kTestUserName2, username->string_view());
 }
 
 TEST_F(StunTest, ReadErrorCodeAttribute) {
@@ -1073,7 +1073,7 @@
   const StunByteStringAttribute* username =
       msg.GetByteString(STUN_ATTR_USERNAME);
   ASSERT_TRUE(username != NULL);
-  EXPECT_EQ(kTestUserName2, username->GetString());
+  EXPECT_EQ(kTestUserName2, username->string_view());
 }
 
 TEST_F(StunTest, WriteMessageWithAnErrorCodeAttribute) {
@@ -1498,7 +1498,7 @@
   const StunByteStringAttribute* bytes = msg.GetByteString(STUN_ATTR_USERNAME);
   ASSERT_TRUE(bytes != NULL);
   EXPECT_EQ(12U, bytes->length());
-  EXPECT_EQ("abcdefghijkl", bytes->GetString());
+  EXPECT_EQ("abcdefghijkl", bytes->string_view());
 
   auto bytes2 = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
   bytes2->CopyBytes("abcdefghijkl");
@@ -1556,7 +1556,7 @@
   bytes = msg.GetByteString(STUN_ATTR_DATA);
   ASSERT_TRUE(bytes != NULL);
   EXPECT_EQ(7U, bytes->length());
-  EXPECT_EQ("abcdefg", bytes->GetString());
+  EXPECT_EQ("abcdefg", bytes->string_view());
 
   bytes2 = StunAttribute::CreateByteString(STUN_ATTR_DATA);
   bytes2->CopyBytes("abcdefg");
diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc
index 4b9fa7e..58adb64 100644
--- a/p2p/base/connection.cc
+++ b/p2p/base/connection.cc
@@ -34,12 +34,13 @@
 #include "rtc_base/strings/string_builder.h"
 #include "rtc_base/third_party/base64/base64.h"
 
+namespace cricket {
 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,
+    const std::vector<Connection::SentPing>& pings_since_last_response,
     uint32_t maximum_failures,
     int rtt_estimate,
     int64_t now) {
@@ -56,7 +57,7 @@
 
 // Determines whether we have gone too long without seeing any response.
 inline bool TooLongWithoutResponse(
-    const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
+    const std::vector<Connection::SentPing>& pings_since_last_response,
     int64_t maximum_time,
     int64_t now) {
   if (pings_since_last_response.size() == 0)
@@ -69,13 +70,13 @@
 // 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) {
+  if (type == LOCAL_PORT_TYPE) {
     return webrtc::IceCandidateType::kLocal;
-  } else if (type == cricket::STUN_PORT_TYPE) {
+  } else if (type == STUN_PORT_TYPE) {
     return webrtc::IceCandidateType::kStun;
-  } else if (type == cricket::PRFLX_PORT_TYPE) {
+  } else if (type == PRFLX_PORT_TYPE) {
     return webrtc::IceCandidateType::kPrflx;
-  } else if (type == cricket::RELAY_PORT_TYPE) {
+  } else if (type == RELAY_PORT_TYPE) {
     return webrtc::IceCandidateType::kRelay;
   }
   return webrtc::IceCandidateType::kUnknown;
@@ -83,13 +84,13 @@
 
 webrtc::IceCandidatePairProtocol GetProtocolByString(
     const std::string& protocol) {
-  if (protocol == cricket::UDP_PROTOCOL_NAME) {
+  if (protocol == UDP_PROTOCOL_NAME) {
     return webrtc::IceCandidatePairProtocol::kUdp;
-  } else if (protocol == cricket::TCP_PROTOCOL_NAME) {
+  } else if (protocol == TCP_PROTOCOL_NAME) {
     return webrtc::IceCandidatePairProtocol::kTcp;
-  } else if (protocol == cricket::SSLTCP_PROTOCOL_NAME) {
+  } else if (protocol == SSLTCP_PROTOCOL_NAME) {
     return webrtc::IceCandidatePairProtocol::kSsltcp;
-  } else if (protocol == cricket::TLS_PROTOCOL_NAME) {
+  } else if (protocol == TLS_PROTOCOL_NAME) {
     return webrtc::IceCandidatePairProtocol::kTls;
   }
   return webrtc::IceCandidatePairProtocol::kUnknown;
@@ -148,24 +149,22 @@
 constexpr int64_t kMinExtraPingDelayMs = 100;
 
 // Default field trials.
-const cricket::IceFieldTrials kDefaultFieldTrials;
+const IceFieldTrials kDefaultFieldTrials;
 
-constexpr int kSupportGoogPingVersionRequestIndex =
-    static_cast<int>(cricket::IceGoogMiscInfoBindingRequestAttributeIndex::
-                         SUPPORT_GOOG_PING_VERSION);
+constexpr int kSupportGoogPingVersionRequestIndex = static_cast<int>(
+    IceGoogMiscInfoBindingRequestAttributeIndex::SUPPORT_GOOG_PING_VERSION);
 
-constexpr int kSupportGoogPingVersionResponseIndex =
-    static_cast<int>(cricket::IceGoogMiscInfoBindingResponseAttributeIndex::
-                         SUPPORT_GOOG_PING_VERSION);
+constexpr int kSupportGoogPingVersionResponseIndex = static_cast<int>(
+    IceGoogMiscInfoBindingResponseAttributeIndex::SUPPORT_GOOG_PING_VERSION);
 
 }  // namespace
 
-namespace cricket {
-
+// A ConnectionRequest is a STUN binding used to determine writability.
 class Connection::ConnectionRequest : public StunRequest {
  public:
-  ConnectionRequest(StunRequestManager& manager, Connection* connection);
-  void Prepare(StunMessage* message) override;
+  ConnectionRequest(StunRequestManager& manager,
+                    Connection* connection,
+                    std::unique_ptr<IceMessage> message);
   void OnResponse(StunMessage* response) override;
   void OnErrorResponse(StunMessage* response) override;
   void OnTimeout() override;
@@ -176,101 +175,11 @@
   Connection* const connection_;
 };
 
-// A ConnectionRequest is a STUN binding used to determine writability.
-Connection::ConnectionRequest::ConnectionRequest(StunRequestManager& manager,
-                                                 Connection* connection)
-    : StunRequest(manager, std::make_unique<IceMessage>(STUN_BINDING_REQUEST)),
-      connection_(connection) {}
-
-void Connection::ConnectionRequest::Prepare(StunMessage* message) {
-  RTC_DCHECK_RUN_ON(connection_->network_thread_);
-  RTC_DCHECK_EQ(message->type(), STUN_BINDING_REQUEST);
-  std::string username;
-  connection_->port()->CreateStunUsername(
-      connection_->remote_candidate().username(), &username);
-  // Note that the order of attributes does not impact the parsing on the
-  // receiver side. The attribute is retrieved then by iterating and matching
-  // over all parsed attributes. See StunMessage::GetAttribute.
-  message->AddAttribute(
-      std::make_unique<StunByteStringAttribute>(STUN_ATTR_USERNAME, username));
-
-  // connection_ already holds this ping, so subtract one from count.
-  if (connection_->port()->send_retransmit_count_attribute()) {
-    message->AddAttribute(std::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();
-  message->AddAttribute(std::make_unique<StunUInt32Attribute>(
-      STUN_ATTR_GOOG_NETWORK_INFO, network_info));
-
-  if (connection_->field_trials_->piggyback_ice_check_acknowledgement &&
-      connection_->last_ping_id_received()) {
-    message->AddAttribute(std::make_unique<StunByteStringAttribute>(
-        STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED,
-        connection_->last_ping_id_received().value()));
-  }
-
-  // Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
-  if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
-    message->AddAttribute(std::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()) {
-      message->AddAttribute(
-          std::make_unique<StunByteStringAttribute>(STUN_ATTR_USE_CANDIDATE));
-    }
-    if (connection_->nomination_ &&
-        connection_->nomination_ != connection_->acked_nomination()) {
-      message->AddAttribute(std::make_unique<StunUInt32Attribute>(
-          STUN_ATTR_NOMINATION, connection_->nomination_));
-    }
-  } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) {
-    message->AddAttribute(std::make_unique<StunUInt64Attribute>(
-        STUN_ATTR_ICE_CONTROLLED, connection_->port()->IceTiebreaker()));
-  } else {
-    RTC_DCHECK_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);
-  message->AddAttribute(std::make_unique<StunUInt32Attribute>(
-      STUN_ATTR_PRIORITY, prflx_priority));
-
-  if (connection_->field_trials_->enable_goog_ping &&
-      !connection_->remote_support_goog_ping_.has_value()) {
-    // Check if remote supports GOOG PING by announcing which version we
-    // support. This is sent on all STUN_BINDING_REQUEST until we get a
-    // STUN_BINDING_RESPONSE.
-    auto list =
-        StunAttribute::CreateUInt16ListAttribute(STUN_ATTR_GOOG_MISC_INFO);
-    list->AddTypeAtIndex(kSupportGoogPingVersionRequestIndex, kGoogPingVersion);
-    message->AddAttribute(std::move(list));
-  }
-
-  if (connection_->ShouldSendGoogPing(message)) {
-    message->SetType(GOOG_PING_REQUEST);
-    message->ClearAttributes();
-    message->AddMessageIntegrity32(connection_->remote_candidate().password());
-  } else {
-    message->AddMessageIntegrity(connection_->remote_candidate().password());
-    message->AddFingerprint();
-  }
-}
+Connection::ConnectionRequest::ConnectionRequest(
+    StunRequestManager& manager,
+    Connection* connection,
+    std::unique_ptr<IceMessage> message)
+    : StunRequest(manager, std::move(message)), connection_(connection) {}
 
 void Connection::ConnectionRequest::OnResponse(StunMessage* response) {
   RTC_DCHECK_RUN_ON(connection_->network_thread_);
@@ -997,7 +906,7 @@
 void Connection::Ping(int64_t now) {
   RTC_DCHECK_RUN_ON(network_thread_);
   last_ping_sent_ = now;
-  ConnectionRequest* req = new ConnectionRequest(requests_, 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.
@@ -1005,15 +914,87 @@
   if (nomination_ > 0) {
     nomination = nomination_;
   }
+
+  auto req =
+      std::make_unique<ConnectionRequest>(requests_, this, BuildPingRequest());
+
+  if (ShouldSendGoogPing(req->msg())) {
+    auto message = std::make_unique<IceMessage>(GOOG_PING_REQUEST, req->id());
+    message->AddMessageIntegrity32(remote_candidate_.password());
+    req.reset(new ConnectionRequest(requests_, this, std::move(message)));
+  }
+
   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);
+  requests_.Send(req.release());
   state_ = IceCandidatePairState::IN_PROGRESS;
   num_pings_sent_++;
 }
 
+std::unique_ptr<IceMessage> Connection::BuildPingRequest() {
+  auto message = std::make_unique<IceMessage>(STUN_BINDING_REQUEST);
+  // Note that the order of attributes does not impact the parsing on the
+  // receiver side. The attribute is retrieved then by iterating and matching
+  // over all parsed attributes. See StunMessage::GetAttribute.
+  message->AddAttribute(std::make_unique<StunByteStringAttribute>(
+      STUN_ATTR_USERNAME,
+      port()->CreateStunUsername(remote_candidate_.username())));
+  message->AddAttribute(std::make_unique<StunUInt32Attribute>(
+      STUN_ATTR_GOOG_NETWORK_INFO,
+      (port_->Network()->id() << 16) | port_->network_cost()));
+
+  if (field_trials_->piggyback_ice_check_acknowledgement &&
+      last_ping_id_received_) {
+    message->AddAttribute(std::make_unique<StunByteStringAttribute>(
+        STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED, *last_ping_id_received_));
+  }
+
+  // Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
+  IceRole ice_role = port_->GetIceRole();
+  RTC_DCHECK(ice_role == ICEROLE_CONTROLLING || ice_role == ICEROLE_CONTROLLED);
+  message->AddAttribute(std::make_unique<StunUInt64Attribute>(
+      ice_role == ICEROLE_CONTROLLING ? STUN_ATTR_ICE_CONTROLLING
+                                      : STUN_ATTR_ICE_CONTROLLED,
+      port_->IceTiebreaker()));
+
+  if (ice_role == ICEROLE_CONTROLLING) {
+    // We should have either USE_CANDIDATE attribute or ICE_NOMINATION
+    // attribute but not both. That was enforced in p2ptransportchannel.
+    if (use_candidate_attr()) {
+      message->AddAttribute(
+          std::make_unique<StunByteStringAttribute>(STUN_ATTR_USE_CANDIDATE));
+    }
+    if (nomination_ && nomination_ != acked_nomination()) {
+      message->AddAttribute(std::make_unique<StunUInt32Attribute>(
+          STUN_ATTR_NOMINATION, nomination_));
+    }
+  }
+
+  message->AddAttribute(std::make_unique<StunUInt32Attribute>(
+      STUN_ATTR_PRIORITY, prflx_priority()));
+
+  if (port()->send_retransmit_count_attribute()) {
+    message->AddAttribute(std::make_unique<StunUInt32Attribute>(
+        STUN_ATTR_RETRANSMIT_COUNT, pings_since_last_response_.size()));
+  }
+  if (field_trials_->enable_goog_ping &&
+      !remote_support_goog_ping_.has_value()) {
+    // Check if remote supports GOOG PING by announcing which version we
+    // support. This is sent on all STUN_BINDING_REQUEST until we get a
+    // STUN_BINDING_RESPONSE.
+    auto list =
+        StunAttribute::CreateUInt16ListAttribute(STUN_ATTR_GOOG_MISC_INFO);
+    list->AddTypeAtIndex(kSupportGoogPingVersionRequestIndex, kGoogPingVersion);
+    message->AddAttribute(std::move(list));
+  }
+  message->AddMessageIntegrity(remote_candidate_.password());
+  message->AddFingerprint();
+
+  return message;
+}
+
 int64_t Connection::last_ping_response_received() const {
   RTC_DCHECK_RUN_ON(network_thread_);
   return last_ping_response_received_;
@@ -1051,7 +1032,8 @@
   const StunByteStringAttribute* last_ice_check_received_attr =
       msg->GetByteString(STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED);
   if (last_ice_check_received_attr) {
-    const std::string request_id = last_ice_check_received_attr->GetString();
+    const absl::string_view request_id =
+        last_ice_check_received_attr->string_view();
     auto iter = absl::c_find_if(
         pings_since_last_response_,
         [&request_id](const SentPing& ping) { return ping.id == request_id; });
@@ -1078,7 +1060,7 @@
 
 void Connection::ReceivedPingResponse(
     int rtt,
-    const std::string& request_id,
+    absl::string_view request_id,
     const absl::optional<uint32_t>& nomination) {
   RTC_DCHECK_RUN_ON(network_thread_);
   RTC_DCHECK_GE(rtt, 0);
@@ -1487,6 +1469,21 @@
   return receiving_unchanged_since_;
 }
 
+uint32_t Connection::prflx_priority() const {
+  RTC_DCHECK_RUN_ON(network_thread_);
+  // 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)
+  IcePriorityValue type_preference =
+      (local_candidate_.protocol() == TCP_PROTOCOL_NAME)
+          ? ICE_TYPE_PREFERENCE_PRFLX_TCP
+          : ICE_TYPE_PREFERENCE_PRFLX;
+  return type_preference << 24 | (local_candidate_.priority() & 0x00FFFFFF);
+}
+
 ConnectionInfo Connection::stats() {
   RTC_DCHECK_RUN_ON(network_thread_);
   stats_.recv_bytes_second = round(recv_rate_tracker_.ComputeRate());
diff --git a/p2p/base/connection.h b/p2p/base/connection.h
index 83077d2..c654e0d 100644
--- a/p2p/base/connection.h
+++ b/p2p/base/connection.h
@@ -202,8 +202,9 @@
   void Ping(int64_t now);
   void ReceivedPingResponse(
       int rtt,
-      const std::string& request_id,
+      absl::string_view request_id,
       const absl::optional<uint32_t>& nomination = absl::nullopt);
+  std::unique_ptr<IceMessage> BuildPingRequest() RTC_RUN_ON(network_thread_);
 
   int64_t last_ping_response_received() const;
   const absl::optional<std::string>& last_ping_id_received() const;
@@ -274,6 +275,10 @@
   // Returns the last time when the connection changed its receiving state.
   int64_t receiving_unchanged_since() const;
 
+  // Constructs the prflx priority as described in
+  // https://datatracker.ietf.org/doc/html/rfc5245#section-4.1.2.1
+  uint32_t prflx_priority() const;
+
   bool stable(int64_t now) const;
 
   // Check if we sent `val` pings without receving a response.
diff --git a/p2p/base/port.cc b/p2p/base/port.cc
index 9a9836d..fe1a98a 100644
--- a/p2p/base/port.cc
+++ b/p2p/base/port.cc
@@ -653,14 +653,14 @@
     return false;
 
   // RFRAG:LFRAG
-  const std::string username = username_attr->GetString();
+  const absl::string_view username = username_attr->string_view();
   size_t colon_pos = username.find(':');
-  if (colon_pos == std::string::npos) {
+  if (colon_pos == absl::string_view::npos) {
     return false;
   }
 
-  *local_ufrag = username.substr(0, colon_pos);
-  *remote_ufrag = username.substr(colon_pos + 1, username.size());
+  *local_ufrag = std::string(username.substr(0, colon_pos));
+  *remote_ufrag = std::string(username.substr(colon_pos + 1, username.size()));
   return true;
 }
 
@@ -725,12 +725,8 @@
   return ret;
 }
 
-void Port::CreateStunUsername(const std::string& remote_username,
-                              std::string* stun_username_attr_str) const {
-  stun_username_attr_str->clear();
-  *stun_username_attr_str = remote_username;
-  stun_username_attr_str->append(":");
-  stun_username_attr_str->append(username_fragment());
+std::string Port::CreateStunUsername(const std::string& remote_username) const {
+  return remote_username + ":" + username_fragment();
 }
 
 bool Port::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
diff --git a/p2p/base/port.h b/p2p/base/port.h
index 6a5bdfc..b3c3d85 100644
--- a/p2p/base/port.h
+++ b/p2p/base/port.h
@@ -62,7 +62,9 @@
 extern const char TCPTYPE_PASSIVE_STR[];
 extern const char TCPTYPE_SIMOPEN_STR[];
 
-enum IcePriorityValue {
+// The type preference MUST be an integer from 0 to 126 inclusive.
+// https://datatracker.ietf.org/doc/html/rfc5245#section-4.1.2.1
+enum IcePriorityValue : uint8_t {
   ICE_TYPE_PREFERENCE_RELAY_TLS = 0,
   ICE_TYPE_PREFERENCE_RELAY_TCP = 1,
   ICE_TYPE_PREFERENCE_RELAY_UDP = 2,
@@ -346,8 +348,7 @@
   bool ParseStunUsername(const StunMessage* stun_msg,
                          std::string* local_username,
                          std::string* remote_username) const;
-  void CreateStunUsername(const std::string& remote_username,
-                          std::string* stun_username_attr_str) const;
+  std::string CreateStunUsername(const std::string& remote_username) const;
 
   bool MaybeIceRoleConflict(const rtc::SocketAddress& addr,
                             IceMessage* stun_msg,
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index 43126b6..5ef191b 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -1472,7 +1472,7 @@
   const StunByteStringAttribute* username_attr =
       msg->GetByteString(STUN_ATTR_USERNAME);
   modified_req->AddAttribute(std::make_unique<StunByteStringAttribute>(
-      STUN_ATTR_USERNAME, username_attr->GetString()));
+      STUN_ATTR_USERNAME, username_attr->string_view()));
   // To make sure we receive error response, adding tiebreaker less than
   // what's present in request.
   modified_req->AddAttribute(std::make_unique<StunUInt64Attribute>(
@@ -1792,7 +1792,7 @@
   const StunUInt32Attribute* priority_attr = msg->GetUInt32(STUN_ATTR_PRIORITY);
   ASSERT_TRUE(priority_attr != NULL);
   EXPECT_EQ(kDefaultPrflxPriority, priority_attr->value());
-  EXPECT_EQ("rfrag:lfrag", username_attr->GetString());
+  EXPECT_EQ("rfrag:lfrag", username_attr->string_view());
   EXPECT_TRUE(msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
   EXPECT_EQ(StunMessage::IntegrityStatus::kIntegrityOk,
             msg->ValidateMessageIntegrity("rpass"));
diff --git a/p2p/base/turn_port.cc b/p2p/base/turn_port.cc
index ea5327b..148fc6b 100644
--- a/p2p/base/turn_port.cc
+++ b/p2p/base/turn_port.cc
@@ -1185,7 +1185,7 @@
                          "stale nonce error response.";
     return false;
   }
-  set_realm(realm_attr->GetString());
+  set_realm(realm_attr->string_view());
 
   const StunByteStringAttribute* nonce_attr =
       response->GetByteString(STUN_ATTR_NONCE);
@@ -1194,7 +1194,7 @@
                          "stale nonce error response.";
     return false;
   }
-  set_nonce(nonce_attr->GetString());
+  set_nonce(nonce_attr->string_view());
   return true;
 }
 
@@ -1499,7 +1499,7 @@
                            "allocate unauthorized response.";
     return;
   }
-  port_->set_realm(realm_attr->GetString());
+  port_->set_realm(realm_attr->string_view());
 
   const StunByteStringAttribute* nonce_attr =
       response->GetByteString(STUN_ATTR_NONCE);
@@ -1509,7 +1509,7 @@
                            "allocate unauthorized response.";
     return;
   }
-  port_->set_nonce(nonce_attr->GetString());
+  port_->set_nonce(nonce_attr->string_view());
 
   // Send another allocate request, with the received realm and nonce values.
   port_->SendRequest(new TurnAllocateRequest(port_), 0);
@@ -1544,7 +1544,7 @@
     RTC_LOG(LS_INFO) << port_->ToString()
                      << ": Applying STUN_ATTR_REALM attribute in "
                         "try alternate error response.";
-    port_->set_realm(realm_attr->GetString());
+    port_->set_realm(realm_attr->string_view());
   }
 
   const StunByteStringAttribute* nonce_attr =
@@ -1553,7 +1553,7 @@
     RTC_LOG(LS_INFO) << port_->ToString()
                      << ": Applying STUN_ATTR_NONCE attribute in "
                         "try alternate error response.";
-    port_->set_nonce(nonce_attr->GetString());
+    port_->set_nonce(nonce_attr->string_view());
   }
 
   // For TCP, we can't close the original Tcp socket during handling a 300 as
diff --git a/p2p/base/turn_port.h b/p2p/base/turn_port.h
index 74d2493..143208a 100644
--- a/p2p/base/turn_port.h
+++ b/p2p/base/turn_port.h
@@ -268,10 +268,10 @@
 
   bool CreateTurnClientSocket();
 
-  void set_nonce(const std::string& nonce) { nonce_ = nonce; }
-  void set_realm(const std::string& realm) {
+  void set_nonce(absl::string_view nonce) { nonce_ = std::string(nonce); }
+  void set_realm(absl::string_view realm) {
     if (realm != realm_) {
-      realm_ = realm;
+      realm_ = std::string(realm);
       UpdateHash();
     }
   }
diff --git a/p2p/base/turn_port_unittest.cc b/p2p/base/turn_port_unittest.cc
index 2244b9d..2d40e97 100644
--- a/p2p/base/turn_port_unittest.cc
+++ b/p2p/base/turn_port_unittest.cc
@@ -876,7 +876,7 @@
           msg->GetByteString(cricket::STUN_ATTR_TURN_LOGGING_ID);
       if (expect_val_) {
         ASSERT_NE(nullptr, attr);
-        ASSERT_EQ(expect_val_, attr->GetString());
+        ASSERT_EQ(expect_val_, attr->string_view());
       } else {
         EXPECT_EQ(nullptr, attr);
       }
diff --git a/p2p/base/turn_server.cc b/p2p/base/turn_server.cc
index 557137f..1e2d5df 100644
--- a/p2p/base/turn_server.cc
+++ b/p2p/base/turn_server.cc
@@ -286,7 +286,7 @@
     // This is a non-allocate request, or a retransmit of an allocate.
     // Check that the username matches the previous username used.
     if (IsStunRequestType(msg.type()) &&
-        msg.GetByteString(STUN_ATTR_USERNAME)->GetString() !=
+        msg.GetByteString(STUN_ATTR_USERNAME)->string_view() !=
             allocation->username()) {
       SendErrorResponse(conn, &msg, STUN_ERROR_WRONG_CREDENTIALS,
                         STUN_ERROR_REASON_WRONG_CREDENTIALS);
@@ -307,8 +307,9 @@
     return false;
   }
 
-  std::string username = username_attr->GetString();
-  return (auth_hook_ != NULL && auth_hook_->GetKey(username, realm_, key));
+  return (auth_hook_ != NULL &&
+          auth_hook_->GetKey(std::string(username_attr->string_view()), realm_,
+                             key));
 }
 
 bool TurnServer::CheckAuthorization(TurnServerConnection* conn,
@@ -342,7 +343,7 @@
   }
 
   // Fail if bad nonce.
-  if (!ValidateNonce(nonce_attr->GetString())) {
+  if (!ValidateNonce(nonce_attr->string_view())) {
     SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE,
                                        STUN_ERROR_REASON_STALE_NONCE);
     return false;
@@ -359,14 +360,14 @@
   // Fail if one-time-use nonce feature is enabled.
   TurnServerAllocation* allocation = FindAllocation(conn);
   if (enable_otu_nonce_ && allocation &&
-      allocation->last_nonce() == nonce_attr->GetString()) {
+      allocation->last_nonce() == nonce_attr->string_view()) {
     SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE,
                                        STUN_ERROR_REASON_STALE_NONCE);
     return false;
   }
 
   if (allocation) {
-    allocation->set_last_nonce(nonce_attr->GetString());
+    allocation->set_last_nonce(nonce_attr->string_view());
   }
   // Success.
   return true;
@@ -425,7 +426,7 @@
   return nonce;
 }
 
-bool TurnServer::ValidateNonce(const std::string& nonce) const {
+bool TurnServer::ValidateNonce(absl::string_view nonce) const {
   // Check the size.
   if (nonce.size() != kNonceSize) {
     return false;
@@ -662,7 +663,7 @@
   const StunByteStringAttribute* username_attr =
       msg->GetByteString(STUN_ATTR_USERNAME);
   RTC_DCHECK(username_attr != NULL);
-  username_ = username_attr->GetString();
+  username_ = std::string(username_attr->string_view());
 
   // Figure out the lifetime and start the allocation timer.
   int lifetime_secs = ComputeLifetime(*msg);
diff --git a/p2p/base/turn_server.h b/p2p/base/turn_server.h
index fd3639f..95c7a4d 100644
--- a/p2p/base/turn_server.h
+++ b/p2p/base/turn_server.h
@@ -81,7 +81,9 @@
   const std::string& transaction_id() const { return transaction_id_; }
   const std::string& username() const { return username_; }
   const std::string& last_nonce() const { return last_nonce_; }
-  void set_last_nonce(const std::string& nonce) { last_nonce_ = nonce; }
+  void set_last_nonce(absl::string_view nonce) {
+    last_nonce_ = std::string(nonce);
+  }
 
   std::string ToString() const;
 
@@ -288,7 +290,7 @@
                           const char* data,
                           size_t size,
                           const std::string& key) RTC_RUN_ON(thread_);
-  bool ValidateNonce(const std::string& nonce) const RTC_RUN_ON(thread_);
+  bool ValidateNonce(absl::string_view nonce) const RTC_RUN_ON(thread_);
 
   TurnServerAllocation* FindAllocation(TurnServerConnection* conn)
       RTC_RUN_ON(thread_);