Implement Connection::ForgetLearnedState()
This patch adds a new ForgetLearnedState() method on a Connection.
The method, puts the connection into a state similar to
when it was just created.
- write_state = STATE_WRITE_INIT
- receving = false
- throw away all pending request
- reset RttEstimate
All other state is kept unchanged.
Note: It does not trigger SignalStateChange
A subsequent patch will expose the method to the IceController.
BUG: webrtc:11463
Change-Id: I055e8cd067e1bc4fd5ad64dd10f458554dbc87e3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/171805
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30916}
diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc
index e503909..282599f 100644
--- a/p2p/base/connection.cc
+++ b/p2p/base/connection.cc
@@ -1334,6 +1334,15 @@
return false;
}
+void Connection::ForgetLearnedState() {
+ RTC_LOG(LS_INFO) << ToString() << ": Connection forget learned state";
+ requests_.Clear();
+ receiving_ = false;
+ write_state_ = STATE_WRITE_INIT;
+ rtt_estimate_.Reset();
+ pings_since_last_response_.clear();
+}
+
ProxyConnection::ProxyConnection(Port* port,
size_t index,
const Candidate& remote_candidate)
diff --git a/p2p/base/connection.h b/p2p/base/connection.h
index 0ce2b5d..4b71a7d 100644
--- a/p2p/base/connection.h
+++ b/p2p/base/connection.h
@@ -303,6 +303,20 @@
return rtt_estimate_;
}
+ // Reset the connection to a state of a newly connected.
+ // - STATE_WRITE_INIT
+ // - receving = false
+ // - throw away all pending request
+ // - reset RttEstimate
+ //
+ // Keep the following unchanged:
+ // - connected
+ // - remote_candidate
+ // - statistics
+ //
+ // Does not trigger SignalStateChange
+ void ForgetLearnedState();
+
void SendStunBindingResponse(const StunMessage* request);
void SendGoogPingResponse(const StunMessage* request);
void SendResponseMessage(const StunMessage& response);
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index c701da2..eaa2545 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -13,6 +13,7 @@
#include <string.h>
#include <cstdint>
+#include <limits>
#include <list>
#include <memory>
#include <string>
@@ -3363,4 +3364,158 @@
EXPECT_TRUE(port->GetConnection(address) != nullptr);
}
+// TODO(webrtc:11463) : Move Connection tests into separate unit test
+// splitting out shared test code as needed.
+
+class ConnectionTest : public PortTest {
+ public:
+ ConnectionTest() {
+ lport_ = CreateTestPort(kLocalAddr1, "lfrag", "lpass");
+ rport_ = CreateTestPort(kLocalAddr2, "rfrag", "rpass");
+ lport_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+ lport_->SetIceTiebreaker(kTiebreaker1);
+ rport_->SetIceRole(cricket::ICEROLE_CONTROLLED);
+ rport_->SetIceTiebreaker(kTiebreaker2);
+
+ lport_->PrepareAddress();
+ rport_->PrepareAddress();
+ }
+
+ rtc::ScopedFakeClock clock_;
+ int num_state_changes_ = 0;
+
+ Connection* CreateConnection(IceRole role) {
+ Connection* conn;
+ if (role == cricket::ICEROLE_CONTROLLING) {
+ conn = lport_->CreateConnection(rport_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ } else {
+ conn = rport_->CreateConnection(lport_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ }
+ conn->SignalStateChange.connect(this,
+ &ConnectionTest::OnConnectionStateChange);
+ return conn;
+ }
+
+ void SendPingAndCaptureReply(Connection* lconn,
+ Connection* rconn,
+ int64_t ms,
+ rtc::BufferT<uint8_t>* reply) {
+ TestPort* lport =
+ lconn->PortForTest() == lport_.get() ? lport_.get() : rport_.get();
+ TestPort* rport =
+ rconn->PortForTest() == rport_.get() ? rport_.get() : lport_.get();
+ lconn->Ping(rtc::TimeMillis());
+ 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());
+ *reply = std::move(*rport->last_stun_buf());
+ }
+
+ void SendPingAndReceiveResponse(Connection* lconn,
+ Connection* rconn,
+ int64_t ms) {
+ rtc::BufferT<uint8_t> reply;
+ SendPingAndCaptureReply(lconn, rconn, ms, &reply);
+ lconn->OnReadPacket(reply.data<char>(), reply.size(),
+ /* packet_time_us */ -1);
+ }
+
+ void OnConnectionStateChange(Connection* connection) { num_state_changes_++; }
+
+ private:
+ std::unique_ptr<TestPort> lport_;
+ std::unique_ptr<TestPort> rport_;
+};
+
+TEST_F(ConnectionTest, ConnectionForgetLearnedState) {
+ Connection* lconn = CreateConnection(ICEROLE_CONTROLLING);
+ Connection* rconn = CreateConnection(ICEROLE_CONTROLLED);
+
+ EXPECT_FALSE(lconn->writable());
+ EXPECT_FALSE(lconn->receiving());
+ EXPECT_TRUE(std::isnan(lconn->GetRttEstimate().GetAverage()));
+ EXPECT_EQ(lconn->GetRttEstimate().GetVariance(),
+ std::numeric_limits<double>::infinity());
+
+ SendPingAndReceiveResponse(lconn, rconn, 10);
+
+ EXPECT_TRUE(lconn->writable());
+ EXPECT_TRUE(lconn->receiving());
+ EXPECT_EQ(lconn->GetRttEstimate().GetAverage(), 10);
+ EXPECT_EQ(lconn->GetRttEstimate().GetVariance(),
+ std::numeric_limits<double>::infinity());
+
+ SendPingAndReceiveResponse(lconn, rconn, 11);
+
+ EXPECT_TRUE(lconn->writable());
+ EXPECT_TRUE(lconn->receiving());
+ EXPECT_NEAR(lconn->GetRttEstimate().GetAverage(), 10, 0.5);
+ EXPECT_LT(lconn->GetRttEstimate().GetVariance(),
+ std::numeric_limits<double>::infinity());
+
+ lconn->ForgetLearnedState();
+
+ EXPECT_FALSE(lconn->writable());
+ EXPECT_FALSE(lconn->receiving());
+ EXPECT_TRUE(std::isnan(lconn->GetRttEstimate().GetAverage()));
+ EXPECT_EQ(lconn->GetRttEstimate().GetVariance(),
+ std::numeric_limits<double>::infinity());
+}
+
+TEST_F(ConnectionTest, ConnectionForgetLearnedStateDiscardsPendingPings) {
+ Connection* lconn = CreateConnection(ICEROLE_CONTROLLING);
+ Connection* rconn = CreateConnection(ICEROLE_CONTROLLED);
+
+ SendPingAndReceiveResponse(lconn, rconn, 10);
+
+ EXPECT_TRUE(lconn->writable());
+ EXPECT_TRUE(lconn->receiving());
+
+ rtc::BufferT<uint8_t> reply;
+ SendPingAndCaptureReply(lconn, rconn, 10, &reply);
+
+ lconn->ForgetLearnedState();
+
+ EXPECT_FALSE(lconn->writable());
+ EXPECT_FALSE(lconn->receiving());
+
+ lconn->OnReadPacket(reply.data<char>(), reply.size(),
+ /* packet_time_us */ -1);
+
+ // That reply was discarded due to the ForgetLearnedState() while it was
+ // outstanding.
+ EXPECT_FALSE(lconn->writable());
+ EXPECT_FALSE(lconn->receiving());
+
+ // But sending a new ping and getting a reply works.
+ SendPingAndReceiveResponse(lconn, rconn, 11);
+ EXPECT_TRUE(lconn->writable());
+ EXPECT_TRUE(lconn->receiving());
+}
+
+TEST_F(ConnectionTest, ConnectionForgetLearnedStateDoesNotTriggerStateChange) {
+ Connection* lconn = CreateConnection(ICEROLE_CONTROLLING);
+ Connection* rconn = CreateConnection(ICEROLE_CONTROLLED);
+
+ EXPECT_EQ(num_state_changes_, 0);
+ SendPingAndReceiveResponse(lconn, rconn, 10);
+
+ EXPECT_TRUE(lconn->writable());
+ EXPECT_TRUE(lconn->receiving());
+ EXPECT_EQ(num_state_changes_, 2);
+
+ lconn->ForgetLearnedState();
+
+ EXPECT_FALSE(lconn->writable());
+ EXPECT_FALSE(lconn->receiving());
+ EXPECT_EQ(num_state_changes_, 2);
+}
+
} // namespace cricket