Add StunDictionary - patch 2
This patch hooks up the StunDictionary to Connection
and P2PTransportChannel.
Bug: webrtc:15392
Change-Id: Ibeea4d8706ebd42f2353d9d300631c02bf0d484d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/315100
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40519}
diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc
index c5e6993..1ef42cc 100644
--- a/p2p/base/connection.cc
+++ b/p2p/base/connection.cc
@@ -704,6 +704,28 @@
}
}
+ const StunByteStringAttribute* delta =
+ message->GetByteString(STUN_ATTR_GOOG_DELTA);
+ if (delta) {
+ if (field_trials_->answer_goog_delta && goog_delta_consumer_) {
+ auto ack = (*goog_delta_consumer_)(delta);
+ if (ack) {
+ RTC_LOG(LS_INFO) << "Sending GOOG_DELTA_ACK"
+ << " delta len: " << delta->length();
+ response.AddAttribute(std::move(ack));
+ } else {
+ RTC_LOG(LS_ERROR) << "GOOG_DELTA consumer did not return ack!";
+ }
+ } else {
+ RTC_LOG(LS_WARNING) << "Ignore GOOG_DELTA"
+ << " len: " << delta->length()
+ << " answer_goog_delta = "
+ << field_trials_->answer_goog_delta
+ << " goog_delta_consumer_ = "
+ << goog_delta_consumer_.has_value();
+ }
+ }
+
response.AddMessageIntegrity(local_candidate().password());
response.AddFingerprint();
@@ -933,7 +955,8 @@
return last_ping_sent_;
}
-void Connection::Ping(int64_t now) {
+void Connection::Ping(int64_t now,
+ std::unique_ptr<StunByteStringAttribute> delta) {
RTC_DCHECK_RUN_ON(network_thread_);
if (!port_)
return;
@@ -948,10 +971,11 @@
nomination = nomination_;
}
- auto req =
- std::make_unique<ConnectionRequest>(requests_, this, BuildPingRequest());
+ bool has_delta = delta != nullptr;
+ auto req = std::make_unique<ConnectionRequest>(
+ requests_, this, BuildPingRequest(std::move(delta)));
- if (ShouldSendGoogPing(req->msg())) {
+ if (!has_delta && 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)));
@@ -966,7 +990,8 @@
num_pings_sent_++;
}
-std::unique_ptr<IceMessage> Connection::BuildPingRequest() {
+std::unique_ptr<IceMessage> Connection::BuildPingRequest(
+ std::unique_ptr<StunByteStringAttribute> delta) {
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
@@ -1022,6 +1047,13 @@
list->AddTypeAtIndex(kSupportGoogPingVersionRequestIndex, kGoogPingVersion);
message->AddAttribute(std::move(list));
}
+
+ if (delta) {
+ RTC_DCHECK(delta->type() == STUN_ATTR_GOOG_DELTA);
+ RTC_LOG(LS_INFO) << "Sending GOOG_DELTA: len: " << delta->length();
+ message->AddAttribute(std::move(delta));
+ }
+
message->AddMessageIntegrity(remote_candidate_.password());
message->AddFingerprint();
@@ -1393,6 +1425,34 @@
cached_stun_binding_ = request->msg()->Clone();
}
}
+
+ // Did we send a delta ?
+ const bool sent_goog_delta =
+ request->msg()->GetByteString(STUN_ATTR_GOOG_DELTA) != nullptr;
+ // Did we get a GOOG_DELTA_ACK ?
+ const StunUInt64Attribute* delta_ack =
+ response->GetUInt64(STUN_ATTR_GOOG_DELTA_ACK);
+
+ if (goog_delta_ack_consumer_) {
+ if (sent_goog_delta && delta_ack) {
+ RTC_LOG(LS_VERBOSE) << "Got GOOG_DELTA_ACK len: " << delta_ack->length();
+ (*goog_delta_ack_consumer_)(delta_ack);
+ } else if (sent_goog_delta) {
+ // We sent DELTA but did not get a DELTA_ACK.
+ // This means that remote does not support GOOG_DELTA
+ RTC_LOG(LS_INFO) << "NO DELTA ACK => disable GOOG_DELTA";
+ (*goog_delta_ack_consumer_)(
+ webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION));
+ } else if (delta_ack) {
+ // We did NOT send DELTA but got a DELTA_ACK.
+ // That is internal error.
+ RTC_LOG(LS_ERROR) << "DELTA ACK w/o DELTA => disable GOOG_DELTA";
+ (*goog_delta_ack_consumer_)(
+ webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR));
+ }
+ } else if (delta_ack) {
+ RTC_LOG(LS_ERROR) << "Discard GOOG_DELTA_ACK, no consumer";
+ }
}
void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request,
diff --git a/p2p/base/connection.h b/p2p/base/connection.h
index 4e6c7d9..8e439855 100644
--- a/p2p/base/connection.h
+++ b/p2p/base/connection.h
@@ -13,6 +13,7 @@
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
@@ -205,12 +206,15 @@
// Called when this connection should try checking writability again.
int64_t last_ping_sent() const;
- void Ping(int64_t now);
+ void Ping(int64_t now,
+ std::unique_ptr<StunByteStringAttribute> delta = nullptr);
void ReceivedPingResponse(
int rtt,
absl::string_view request_id,
const absl::optional<uint32_t>& nomination = absl::nullopt);
- std::unique_ptr<IceMessage> BuildPingRequest() RTC_RUN_ON(network_thread_);
+ std::unique_ptr<IceMessage> BuildPingRequest(
+ std::unique_ptr<StunByteStringAttribute> delta)
+ RTC_RUN_ON(network_thread_);
int64_t last_ping_response_received() const;
const absl::optional<std::string>& last_ping_id_received() const;
@@ -319,7 +323,7 @@
std::unique_ptr<IceMessage> BuildPingRequestForTest() {
RTC_DCHECK_RUN_ON(network_thread_);
- return BuildPingRequest();
+ return BuildPingRequest(nullptr);
}
// Public for unit tests.
@@ -333,6 +337,20 @@
remote_candidate_.set_password(pwd);
}
+ void SetStunDictConsumer(
+ std::function<std::unique_ptr<StunAttribute>(
+ const StunByteStringAttribute*)> goog_delta_consumer,
+ std::function<void(webrtc::RTCErrorOr<const StunUInt64Attribute*>)>
+ goog_delta_ack_consumer) {
+ goog_delta_consumer_ = std::move(goog_delta_consumer);
+ goog_delta_ack_consumer_ = std::move(goog_delta_ack_consumer);
+ }
+
+ void ClearStunDictConsumer() {
+ goog_delta_consumer_ = absl::nullopt;
+ goog_delta_ack_consumer_ = absl::nullopt;
+ }
+
protected:
// A ConnectionRequest is a simple STUN ping used to determine writability.
class ConnectionRequest;
@@ -475,6 +493,13 @@
const IceFieldTrials* field_trials_;
rtc::EventBasedExponentialMovingAverage rtt_estimate_
RTC_GUARDED_BY(network_thread_);
+
+ absl::optional<std::function<std::unique_ptr<StunAttribute>(
+ const StunByteStringAttribute*)>>
+ goog_delta_consumer_;
+ absl::optional<
+ std::function<void(webrtc::RTCErrorOr<const StunUInt64Attribute*>)>>
+ goog_delta_ack_consumer_;
};
// ProxyConnection defers all the interesting work to the port.
diff --git a/p2p/base/ice_transport_internal.h b/p2p/base/ice_transport_internal.h
index 55f1238..98deb49 100644
--- a/p2p/base/ice_transport_internal.h
+++ b/p2p/base/ice_transport_internal.h
@@ -14,6 +14,7 @@
#include <stdint.h>
#include <string>
+#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
@@ -24,6 +25,7 @@
#include "p2p/base/connection.h"
#include "p2p/base/packet_transport_internal.h"
#include "p2p/base/port.h"
+#include "p2p/base/stun_dictionary.h"
#include "p2p/base/transport_description.h"
#include "rtc_base/network_constants.h"
#include "rtc_base/system/rtc_export.h"
@@ -293,6 +295,11 @@
virtual absl::optional<const CandidatePair> GetSelectedCandidatePair()
const = 0;
+ virtual absl::optional<std::reference_wrapper<StunDictionaryWriter>>
+ GetDictionaryWriter() {
+ return absl::nullopt;
+ }
+
sigslot::signal1<IceTransportInternal*> SignalGatheringState;
// Handles sending and receiving of candidates.
@@ -330,6 +337,37 @@
// Invoked when the transport is being destroyed.
sigslot::signal1<IceTransportInternal*> SignalDestroyed;
+
+ // Invoked when remote dictionary has been updated,
+ // i.e. modifications to attributes from remote ice agent has
+ // reflected in our StunDictionaryView.
+ template <typename F>
+ void AddDictionaryViewUpdatedCallback(const void* tag, F&& callback) {
+ dictionary_view_updated_callback_list_.AddReceiver(
+ tag, std::forward<F>(callback));
+ }
+ void RemoveDictionaryViewUpdatedCallback(const void* tag) {
+ dictionary_view_updated_callback_list_.RemoveReceivers(tag);
+ }
+
+ // Invoked when local dictionary has been synchronized,
+ // i.e. remote ice agent has reported acknowledged updates from us.
+ template <typename F>
+ void AddDictionaryWriterSyncedCallback(const void* tag, F&& callback) {
+ dictionary_writer_synced_callback_list_.AddReceiver(
+ tag, std::forward<F>(callback));
+ }
+ void RemoveDictionaryWriterSyncedCallback(const void* tag) {
+ dictionary_writer_synced_callback_list_.RemoveReceivers(tag);
+ }
+
+ protected:
+ webrtc::CallbackList<IceTransportInternal*,
+ const StunDictionaryView&,
+ rtc::ArrayView<uint16_t>>
+ dictionary_view_updated_callback_list_;
+ webrtc::CallbackList<IceTransportInternal*, const StunDictionaryWriter&>
+ dictionary_writer_synced_callback_list_;
};
} // namespace cricket
diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc
index af4b800..d916983 100644
--- a/p2p/base/p2p_transport_channel.cc
+++ b/p2p/base/p2p_transport_channel.cc
@@ -286,6 +286,13 @@
connection->set_ice_event_log(&ice_event_log_);
connection->SetIceFieldTrials(&ice_field_trials_);
+ connection->SetStunDictConsumer(
+ [this](const StunByteStringAttribute* delta) {
+ return GoogDeltaReceived(delta);
+ },
+ [this](webrtc::RTCErrorOr<const StunUInt64Attribute*> delta_ack) {
+ GoogDeltaAckReceived(std::move(delta_ack));
+ });
LogCandidatePairConfig(connection,
webrtc::IceCandidatePairConfigType::kAdded);
@@ -727,7 +734,10 @@
&ice_field_trials_.dead_connection_timeout_ms,
// Stop gathering on strongly connected.
"stop_gather_on_strongly_connected",
- &ice_field_trials_.stop_gather_on_strongly_connected)
+ &ice_field_trials_.stop_gather_on_strongly_connected,
+ // GOOG_DELTA
+ "enable_goog_delta", &ice_field_trials_.enable_goog_delta,
+ "answer_goog_delta", &ice_field_trials_.answer_goog_delta)
->Parse(field_trials->Lookup("WebRTC-IceFieldTrials"));
if (ice_field_trials_.dead_connection_timeout_ms < 30000) {
@@ -782,6 +792,10 @@
ice_field_trials_.extra_ice_ping =
field_trials->IsEnabled("WebRTC-ExtraICEPing");
+
+ if (!ice_field_trials_.enable_goog_delta) {
+ stun_dict_writer_.Disable();
+ }
}
const IceConfig& P2PTransportChannel::config() const {
@@ -2058,7 +2072,7 @@
conn->set_nomination(nomination);
conn->set_use_candidate_attr(use_candidate_attr);
last_ping_sent_ms_ = rtc::TimeMillis();
- conn->Ping(last_ping_sent_ms_);
+ conn->Ping(last_ping_sent_ms_, stun_dict_writer_.CreateDelta());
}
uint32_t P2PTransportChannel::GetNominationAttr(Connection* conn) const {
@@ -2129,11 +2143,12 @@
}
}
-void P2PTransportChannel::RemoveConnection(const Connection* connection) {
+void P2PTransportChannel::RemoveConnection(Connection* connection) {
RTC_DCHECK_RUN_ON(network_thread_);
auto it = absl::c_find(connections_, connection);
RTC_DCHECK(it != connections_.end());
connections_.erase(it);
+ connection->ClearStunDictConsumer();
ice_controller_->OnConnectionDestroyed(connection);
}
@@ -2307,4 +2322,34 @@
conn->ToLogDescription());
}
+std::unique_ptr<StunAttribute> P2PTransportChannel::GoogDeltaReceived(
+ const StunByteStringAttribute* delta) {
+ auto error = stun_dict_view_.ApplyDelta(*delta);
+ if (error.ok()) {
+ auto& result = error.value();
+ RTC_LOG(LS_INFO) << "Applied GOOG_DELTA";
+ dictionary_view_updated_callback_list_.Send(this, stun_dict_view_,
+ result.second);
+ return std::move(result.first);
+ } else {
+ RTC_LOG(LS_ERROR) << "Failed to apply GOOG_DELTA: "
+ << error.error().message();
+ }
+ return nullptr;
+}
+
+void P2PTransportChannel::GoogDeltaAckReceived(
+ webrtc::RTCErrorOr<const StunUInt64Attribute*> error_or_ack) {
+ if (error_or_ack.ok()) {
+ RTC_LOG(LS_ERROR) << "Applied GOOG_DELTA_ACK";
+ auto ack = error_or_ack.value();
+ stun_dict_writer_.ApplyDeltaAck(*ack);
+ dictionary_writer_synced_callback_list_.Send(this, stun_dict_writer_);
+ } else {
+ stun_dict_writer_.Disable();
+ RTC_LOG(LS_ERROR) << "Failed GOOG_DELTA_ACK: "
+ << error_or_ack.error().message();
+ }
+}
+
} // namespace cricket
diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h
index a0729c1..dc27b20 100644
--- a/p2p/base/p2p_transport_channel.h
+++ b/p2p/base/p2p_transport_channel.h
@@ -61,6 +61,7 @@
#include "p2p/base/port_allocator.h"
#include "p2p/base/port_interface.h"
#include "p2p/base/regathering_controller.h"
+#include "p2p/base/stun_dictionary.h"
#include "p2p/base/transport_description.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/checks.h"
@@ -216,7 +217,7 @@
int check_receiving_interval() const;
absl::optional<rtc::NetworkRoute> network_route() const override;
- void RemoveConnection(const Connection* connection);
+ void RemoveConnection(Connection* connection);
// Helper method used only in unittest.
rtc::DiffServCodePoint DefaultDscpValue() const;
@@ -254,6 +255,11 @@
return ss.Release();
}
+ absl::optional<std::reference_wrapper<StunDictionaryWriter>>
+ GetDictionaryWriter() override {
+ return stun_dict_writer_;
+ }
+
private:
P2PTransportChannel(
absl::string_view transport_name,
@@ -494,6 +500,10 @@
Candidate candidate,
const webrtc::AsyncDnsResolverResult& result);
+ std::unique_ptr<StunAttribute> GoogDeltaReceived(
+ const StunByteStringAttribute*);
+ void GoogDeltaAckReceived(webrtc::RTCErrorOr<const StunUInt64Attribute*>);
+
// Bytes/packets sent/received on this channel.
uint64_t bytes_sent_ = 0;
uint64_t bytes_received_ = 0;
@@ -509,6 +519,12 @@
// Parsed field trials.
IceFieldTrials ice_field_trials_;
+
+ // A dictionary of attributes that will be reflected to peer.
+ StunDictionaryWriter stun_dict_writer_;
+
+ // A dictionary that tracks attributes from peer.
+ StunDictionaryView stun_dict_view_;
};
} // namespace cricket
diff --git a/p2p/base/p2p_transport_channel_ice_field_trials.h b/p2p/base/p2p_transport_channel_ice_field_trials.h
index f19823b..96a7756 100644
--- a/p2p/base/p2p_transport_channel_ice_field_trials.h
+++ b/p2p/base/p2p_transport_channel_ice_field_trials.h
@@ -70,6 +70,10 @@
bool piggyback_ice_check_acknowledgement = false;
bool extra_ice_ping = false;
+
+ // Announce/enable GOOG_DELTA
+ bool enable_goog_delta = true; // send GOOG DELTA
+ bool answer_goog_delta = true; // answer GOOG DELTA
};
} // namespace cricket
diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc
index 02cc483..ca8ca8d 100644
--- a/p2p/base/p2p_transport_channel_unittest.cc
+++ b/p2p/base/p2p_transport_channel_unittest.cc
@@ -65,6 +65,7 @@
using ::testing::InSequence;
using ::testing::InvokeArgument;
using ::testing::InvokeWithoutArgs;
+using ::testing::MockFunction;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SaveArg;
@@ -3417,6 +3418,38 @@
kDefaultTimeout, clock);
}
+TEST_F(P2PTransportChannelMultihomedTest, StunDictionaryPerformsSync) {
+ rtc::ScopedFakeClock clock;
+ AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_CELLULAR);
+ AddAddress(0, kAlternateAddrs[0], "vpn0", rtc::ADAPTER_TYPE_VPN,
+ rtc::ADAPTER_TYPE_ETHERNET);
+ AddAddress(1, kPublicAddrs[1]);
+
+ // Create channels and let them go writable, as usual.
+ CreateChannels();
+
+ MockFunction<void(IceTransportInternal*, const StunDictionaryView&,
+ rtc::ArrayView<uint16_t>)>
+ view_updated_func;
+ ep2_ch1()->AddDictionaryViewUpdatedCallback(
+ "tag", view_updated_func.AsStdFunction());
+ MockFunction<void(IceTransportInternal*, const StunDictionaryWriter&)>
+ writer_synced_func;
+ ep1_ch1()->AddDictionaryWriterSyncedCallback(
+ "tag", writer_synced_func.AsStdFunction());
+ auto& dict_writer = ep1_ch1()->GetDictionaryWriter()->get();
+ dict_writer.SetByteString(12)->CopyBytes("keso");
+ EXPECT_CALL(view_updated_func, Call)
+ .WillOnce([&](auto* channel, auto& view, auto keys) {
+ EXPECT_EQ(keys.size(), 1u);
+ EXPECT_EQ(keys[0], 12);
+ EXPECT_EQ(view.GetByteString(12)->string_view(), "keso");
+ });
+ EXPECT_CALL(writer_synced_func, Call).Times(1);
+ EXPECT_TRUE_SIMULATED_WAIT(CheckConnected(ep1_ch1(), ep2_ch1()),
+ kMediumTimeout, clock);
+}
+
// A collection of tests which tests a single P2PTransportChannel by sending
// pings.
class P2PTransportChannelPingTest : public ::testing::Test,
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index 1b1c156..b27afe2 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -20,6 +20,7 @@
#include <utility>
#include <vector>
+#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/candidate.h"
@@ -3833,7 +3834,6 @@
void OnConnectionStateChange(Connection* connection) { num_state_changes_++; }
- private:
std::unique_ptr<TestPort> lport_;
std::unique_ptr<TestPort> rport_;
};
@@ -3922,4 +3922,93 @@
EXPECT_EQ(num_state_changes_, 2);
}
+// Test normal happy case.
+// Sending a delta and getting a delta ack in response.
+TEST_F(ConnectionTest, SendReceiveGoogDelta) {
+ constexpr int64_t ms = 10;
+ Connection* lconn = CreateConnection(ICEROLE_CONTROLLING);
+ Connection* rconn = CreateConnection(ICEROLE_CONTROLLED);
+
+ std::unique_ptr<StunByteStringAttribute> delta =
+ absl::WrapUnique(new StunByteStringAttribute(STUN_ATTR_GOOG_DELTA));
+ delta->CopyBytes("DELTA");
+
+ std::unique_ptr<StunAttribute> delta_ack =
+ absl::WrapUnique(new StunUInt64Attribute(STUN_ATTR_GOOG_DELTA_ACK, 133));
+
+ bool received_goog_delta = false;
+ bool received_goog_delta_ack = false;
+ lconn->SetStunDictConsumer(
+ // DeltaReceived
+ [](const StunByteStringAttribute* delta)
+ -> std::unique_ptr<StunAttribute> { return nullptr; },
+ // DeltaAckReceived
+ [&](webrtc::RTCErrorOr<const StunUInt64Attribute*> error_or_ack) {
+ received_goog_delta_ack = true;
+ EXPECT_TRUE(error_or_ack.ok());
+ EXPECT_EQ(error_or_ack.value()->value(), 133ull);
+ });
+
+ rconn->SetStunDictConsumer(
+ // DeltaReceived
+ [&](const StunByteStringAttribute* delta)
+ -> std::unique_ptr<StunAttribute> {
+ received_goog_delta = true;
+ EXPECT_EQ(delta->string_view(), "DELTA");
+ return std::move(delta_ack);
+ },
+ // DeltaAckReceived
+ [](webrtc::RTCErrorOr<const StunUInt64Attribute*> error_or__ack) {});
+
+ lconn->Ping(rtc::TimeMillis(), std::move(delta));
+ ASSERT_TRUE_WAIT(lport_->last_stun_msg(), kDefaultTimeout);
+ ASSERT_TRUE(lport_->last_stun_buf());
+ rconn->OnReadPacket(lport_->last_stun_buf()->data<char>(),
+ lport_->last_stun_buf()->size(), /* packet_time_us */ -1);
+ EXPECT_TRUE(received_goog_delta);
+
+ clock_.AdvanceTime(webrtc::TimeDelta::Millis(ms));
+ ASSERT_TRUE_WAIT(rport_->last_stun_msg(), kDefaultTimeout);
+ ASSERT_TRUE(rport_->last_stun_buf());
+ lconn->OnReadPacket(rport_->last_stun_buf()->data<char>(),
+ rport_->last_stun_buf()->size(), /* packet_time_us */ -1);
+ EXPECT_TRUE(received_goog_delta_ack);
+}
+
+// Test that sending a goog delta and not getting
+// a delta ack in reply gives an error callback.
+TEST_F(ConnectionTest, SendGoogDeltaNoReply) {
+ constexpr int64_t ms = 10;
+ Connection* lconn = CreateConnection(ICEROLE_CONTROLLING);
+ Connection* rconn = CreateConnection(ICEROLE_CONTROLLED);
+
+ std::unique_ptr<StunByteStringAttribute> delta =
+ absl::WrapUnique(new StunByteStringAttribute(STUN_ATTR_GOOG_DELTA));
+ delta->CopyBytes("DELTA");
+
+ bool received_goog_delta_ack_error = false;
+ lconn->SetStunDictConsumer(
+ // DeltaReceived
+ [](const StunByteStringAttribute* delta)
+ -> std::unique_ptr<StunAttribute> { return nullptr; },
+ // DeltaAckReceived
+ [&](webrtc::RTCErrorOr<const StunUInt64Attribute*> error_or_ack) {
+ received_goog_delta_ack_error = true;
+ EXPECT_FALSE(error_or_ack.ok());
+ });
+
+ lconn->Ping(rtc::TimeMillis(), std::move(delta));
+ ASSERT_TRUE_WAIT(lport_->last_stun_msg(), kDefaultTimeout);
+ ASSERT_TRUE(lport_->last_stun_buf());
+ rconn->OnReadPacket(lport_->last_stun_buf()->data<char>(),
+ lport_->last_stun_buf()->size(), /* packet_time_us */ -1);
+
+ clock_.AdvanceTime(webrtc::TimeDelta::Millis(ms));
+ ASSERT_TRUE_WAIT(rport_->last_stun_msg(), kDefaultTimeout);
+ ASSERT_TRUE(rport_->last_stun_buf());
+ lconn->OnReadPacket(rport_->last_stun_buf()->data<char>(),
+ rport_->last_stun_buf()->size(), /* packet_time_us */ -1);
+ EXPECT_TRUE(received_goog_delta_ack_error);
+}
+
} // namespace cricket
diff --git a/p2p/base/stun_dictionary.cc b/p2p/base/stun_dictionary.cc
index 7e27a37..bf6a1e4 100644
--- a/p2p/base/stun_dictionary.cc
+++ b/p2p/base/stun_dictionary.cc
@@ -233,7 +233,15 @@
return 0;
}
+void StunDictionaryWriter::Disable() {
+ disabled_ = true;
+}
+
void StunDictionaryWriter::Delete(int key) {
+ if (disabled_) {
+ return;
+ }
+
if (dictionary_) {
if (dictionary_->attrs_.find(key) == dictionary_->attrs_.end()) {
return;
@@ -262,6 +270,9 @@
}
void StunDictionaryWriter::Set(std::unique_ptr<StunAttribute> attr) {
+ if (disabled_) {
+ return;
+ }
int key = attr->type();
// remove any pending updates.
pending_.erase(
@@ -284,6 +295,9 @@
// Create an StunByteStringAttribute containing the pending (e.g not ack:ed)
// modifications.
std::unique_ptr<StunByteStringAttribute> StunDictionaryWriter::CreateDelta() {
+ if (disabled_) {
+ return nullptr;
+ }
if (pending_.empty()) {
return nullptr;
}
diff --git a/p2p/base/stun_dictionary.h b/p2p/base/stun_dictionary.h
index ae2ad75..f93a1f1 100644
--- a/p2p/base/stun_dictionary.h
+++ b/p2p/base/stun_dictionary.h
@@ -62,7 +62,7 @@
const StunUInt16ListAttribute* GetUInt16List(int key) const;
bool empty() const { return attrs_.empty(); }
- int size() const { return attrs_.size(); }
+ size_t size() const { return attrs_.size(); }
int bytes_stored() const { return bytes_stored_; }
void set_max_bytes_stored(int max_bytes_stored) {
max_bytes_stored_ = max_bytes_stored;
@@ -175,9 +175,17 @@
const StunDictionaryView* dictionary() { return dictionary_.get(); }
const StunDictionaryView* operator->() { return dictionary_.get(); }
+ // Disable writer,
+ // i.e CreateDelta always return null, and no modifications are made.
+ // This is called if remote peer does not support GOOG_DELTA.
+ void Disable();
+ bool disabled() const { return disabled_; }
+
private:
void Set(std::unique_ptr<StunAttribute> attr);
+ bool disabled_ = false;
+
// version of modification.
int64_t version_ = 1;