Skip to content

Commit

Permalink
Implement Connection::ForgetLearnedState()
Browse files Browse the repository at this point in the history
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 <[email protected]>
Commit-Queue: Jonas Oreland <[email protected]>
Cr-Commit-Position: refs/heads/master@{#30916}
  • Loading branch information
Jonas Oreland authored and Commit Bot committed Mar 27, 2020
1 parent 749dff1 commit ef60c2b
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
9 changes: 9 additions & 0 deletions p2p/base/connection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions p2p/base/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
155 changes: 155 additions & 0 deletions p2p/base/port_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <string.h>

#include <cstdint>
#include <limits>
#include <list>
#include <memory>
#include <string>
Expand Down Expand Up @@ -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

0 comments on commit ef60c2b

Please sign in to comment.