From ef60c2b0adcbda349193e87c8e6523536fc1fa08 Mon Sep 17 00:00:00 2001 From: Jonas Oreland Date: Thu, 26 Mar 2020 15:10:39 +0100 Subject: [PATCH] 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 Commit-Queue: Jonas Oreland Cr-Commit-Position: refs/heads/master@{#30916} --- p2p/base/connection.cc | 9 +++ p2p/base/connection.h | 14 ++++ p2p/base/port_unittest.cc | 155 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc index e50390901f..282599f0ab 100644 --- a/p2p/base/connection.cc +++ b/p2p/base/connection.cc @@ -1334,6 +1334,15 @@ bool Connection::ShouldSendGoogPing(const StunMessage* message) { 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 0ce2b5d615..4b71a7da55 100644 --- a/p2p/base/connection.h +++ b/p2p/base/connection.h @@ -303,6 +303,20 @@ class Connection : public CandidatePairInterface, 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 c701da229b..eaa2545ee9 100644 --- a/p2p/base/port_unittest.cc +++ b/p2p/base/port_unittest.cc @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -3363,4 +3364,158 @@ TEST_F(PortTest, TestAddConnectionWithSameAddress) { 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* 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(), + 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 reply; + SendPingAndCaptureReply(lconn, rconn, ms, &reply); + lconn->OnReadPacket(reply.data(), reply.size(), + /* packet_time_us */ -1); + } + + void OnConnectionStateChange(Connection* connection) { num_state_changes_++; } + + private: + std::unique_ptr lport_; + std::unique_ptr 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::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::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::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::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 reply; + SendPingAndCaptureReply(lconn, rconn, 10, &reply); + + lconn->ForgetLearnedState(); + + EXPECT_FALSE(lconn->writable()); + EXPECT_FALSE(lconn->receiving()); + + lconn->OnReadPacket(reply.data(), 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