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}
This commit is contained in:
Jonas Oreland 2020-03-26 15:10:39 +01:00 committed by Commit Bot
parent 749dff12b5
commit ef60c2b0ad
3 changed files with 178 additions and 0 deletions

View File

@ -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)

View File

@ -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);

View File

@ -13,6 +13,7 @@
#include <string.h>
#include <cstdint>
#include <limits>
#include <list>
#include <memory>
#include <string>
@ -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<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