Add ability to release TURN allocation gracefully
This patch adds TurnPort::Release that release a TURN allocation
by sending a REFRESH with lifetime 0 without destroying the object.
This allows for graceful shutdown of a TurnPort that can e.g be used
for mobility.
Bug: webtrc:9067
Change-Id: I1e4d9232ae08d6fe14f5612f776a541c03c3beec
Reviewed-on: https://webrtc-review.googlesource.com/64722
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22666}
diff --git a/p2p/base/turnport_unittest.cc b/p2p/base/turnport_unittest.cc
index d68beaf..cf3bb1d 100644
--- a/p2p/base/turnport_unittest.cc
+++ b/p2p/base/turnport_unittest.cc
@@ -154,6 +154,8 @@
turn_error_(false),
turn_unknown_address_(false),
turn_create_permission_success_(false),
+ turn_port_closed_(false),
+ turn_port_destroyed_(false),
udp_ready_(false),
test_finish_(false) {
// Some code uses "last received time == 0" to represent "nothing received
@@ -210,6 +212,13 @@
turn_port_->HandleIncomingPacket(socket, data, size, remote_addr,
packet_time);
}
+ void OnTurnPortClosed(TurnPort* port) {
+ turn_port_closed_ = true;
+ }
+ void OnTurnPortDestroyed(PortInterface* port) {
+ turn_port_destroyed_ = true;
+ }
+
rtc::AsyncSocket* CreateServerSocket(const SocketAddress addr) {
rtc::AsyncSocket* socket = ss_->CreateAsyncSocket(SOCK_STREAM);
EXPECT_GE(socket->Bind(addr), 0);
@@ -316,6 +325,10 @@
&TurnPortTest::OnTurnCreatePermissionResult);
turn_port_->SignalTurnRefreshResult.connect(
this, &TurnPortTest::OnTurnRefreshResult);
+ turn_port_->SignalTurnPortClosed.connect(
+ this, &TurnPortTest::OnTurnPortClosed);
+ turn_port_->SignalDestroyed.connect(
+ this, &TurnPortTest::OnTurnPortDestroyed);
}
void CreateUdpPort() { CreateUdpPort(kLocalAddr2); }
@@ -677,6 +690,48 @@
kSimulatedRtt, fake_clock_);
}
+ // Test that the TURN allocation is released by sending a refresh request
+ // with lifetime 0 when Release is called.
+ void TestTurnGracefulReleaseAllocation(ProtocolType protocol_type) {
+ PrepareTurnAndUdpPorts(protocol_type);
+
+ // Create connections and send pings.
+ Connection* conn1 = turn_port_->CreateConnection(
+ udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE);
+ Connection* conn2 = udp_port_->CreateConnection(
+ turn_port_->Candidates()[0], Port::ORIGIN_MESSAGE);
+ ASSERT_TRUE(conn1 != NULL);
+ ASSERT_TRUE(conn2 != NULL);
+ conn1->SignalReadPacket.connect(static_cast<TurnPortTest*>(this),
+ &TurnPortTest::OnTurnReadPacket);
+ conn2->SignalReadPacket.connect(static_cast<TurnPortTest*>(this),
+ &TurnPortTest::OnUdpReadPacket);
+ conn1->Ping(0);
+ EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn1->write_state(),
+ kSimulatedRtt * 2, fake_clock_);
+ conn2->Ping(0);
+ EXPECT_EQ_SIMULATED_WAIT(Connection::STATE_WRITABLE, conn2->write_state(),
+ kSimulatedRtt * 2, fake_clock_);
+
+ // Send some data from Udp to TurnPort.
+ unsigned char buf[256] = { 0 };
+ conn2->Send(buf, sizeof(buf), options);
+
+ // Now release the TurnPort allocation.
+ // This will send a REFRESH with lifetime 0 to server.
+ turn_port_->Release();
+
+ // Wait for the TurnPort to signal closed.
+ ASSERT_TRUE_SIMULATED_WAIT(turn_port_closed_, kSimulatedRtt, fake_clock_);
+
+ // But the data should have arrived first.
+ ASSERT_EQ(1ul, turn_packets_.size());
+ EXPECT_EQ(sizeof(buf), turn_packets_[0].size());
+
+ // The allocation is released at server.
+ EXPECT_EQ(0U, turn_server_.server()->allocations().size());
+ }
+
protected:
rtc::ScopedFakeClock fake_clock_;
// When a "create port" helper method is called with an IP, we create a
@@ -694,6 +749,8 @@
bool turn_error_;
bool turn_unknown_address_;
bool turn_create_permission_success_;
+ bool turn_port_closed_;
+ bool turn_port_destroyed_;
bool udp_ready_;
bool test_finish_;
bool turn_refresh_success_ = false;
@@ -1451,6 +1508,24 @@
TestTurnReleaseAllocation(PROTO_TLS);
}
+TEST_F(TurnPortTest, TestTurnUDPGracefulReleaseAllocation) {
+ turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_UDP);
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+ TestTurnGracefulReleaseAllocation(PROTO_UDP);
+}
+
+TEST_F(TurnPortTest, TestTurnTCPGracefulReleaseAllocation) {
+ turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP);
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
+ TestTurnGracefulReleaseAllocation(PROTO_TCP);
+}
+
+TEST_F(TurnPortTest, TestTurnTLSGracefulReleaseAllocation) {
+ turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS);
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr);
+ TestTurnGracefulReleaseAllocation(PROTO_TLS);
+}
+
// Test that nothing bad happens if we try to create a connection to the same
// remote address twice. Previously there was a bug that caused this to hit a
// DCHECK.