diff --git a/webrtc/p2p/base/stunrequest.cc b/webrtc/p2p/base/stunrequest.cc index a6f4c1536..32b46941e 100644 --- a/webrtc/p2p/base/stunrequest.cc +++ b/webrtc/p2p/base/stunrequest.cc @@ -44,7 +44,11 @@ void StunRequestManager::SendDelayed(StunRequest* request, int delay) { request->set_origin(origin_); request->Construct(); requests_[request->id()] = request; - thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL); + if (delay > 0) { + thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL); + } else { + thread_->Send(request, MSG_STUN_SEND, NULL); + } } void StunRequestManager::Remove(StunRequest* request) { diff --git a/webrtc/p2p/base/turnport.cc b/webrtc/p2p/base/turnport.cc index 37f0c1111..a3e355e60 100644 --- a/webrtc/p2p/base/turnport.cc +++ b/webrtc/p2p/base/turnport.cc @@ -78,9 +78,11 @@ class TurnRefreshRequest : public StunRequest { virtual void OnResponse(StunMessage* response); virtual void OnErrorResponse(StunMessage* response); virtual void OnTimeout(); + void set_lifetime(int lifetime) { lifetime_ = lifetime; } private: TurnPort* port_; + int lifetime_; }; class TurnCreatePermissionRequest : public StunRequest, @@ -212,6 +214,15 @@ TurnPort::TurnPort(rtc::Thread* thread, TurnPort::~TurnPort() { // TODO(juberti): Should this even be necessary? + + // release the allocation by sending a refresh with + // lifetime 0. + if (connected_) { + TurnRefreshRequest bye(this); + bye.set_lifetime(0); + SendRequest(&bye, 0); + } + while (!entries_.empty()) { DestroyEntry(entries_.front()->address()); } @@ -352,6 +363,7 @@ void TurnPort::OnSocketClose(rtc::AsyncPacketSocket* socket, int error) { if (!connected_) { OnAllocateError(); } + connected_ = false; } void TurnPort::OnAllocateMismatch() { @@ -1020,13 +1032,19 @@ void TurnAllocateRequest::OnTryAlternate(StunMessage* response, int code) { TurnRefreshRequest::TurnRefreshRequest(TurnPort* port) : StunRequest(new TurnMessage()), - port_(port) { + port_(port), + lifetime_(-1) { } void TurnRefreshRequest::Prepare(StunMessage* request) { // Create the request as indicated in RFC 5766, Section 7.1. // No attributes need to be included. request->SetType(TURN_REFRESH_REQUEST); + if (lifetime_ > -1) { + VERIFY(request->AddAttribute(new StunUInt32Attribute( + STUN_ATTR_LIFETIME, lifetime_))); + } + port_->AddRequestAuthInfo(request); } diff --git a/webrtc/p2p/base/turnport_unittest.cc b/webrtc/p2p/base/turnport_unittest.cc index f84d106a2..da2c6b94a 100644 --- a/webrtc/p2p/base/turnport_unittest.cc +++ b/webrtc/p2p/base/turnport_unittest.cc @@ -550,6 +550,10 @@ TEST_F(TurnPortTest, TestTurnAllocateMismatch) { EXPECT_TRUE_WAIT(turn_ready_, kTimeout); rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); + // Clear connected_ flag on turnport to suppress the release of + // the allocation. + turn_port_->OnSocketClose(turn_port_->socket(), 0); + // Forces the socket server to assign the same port. ss_->SetNextPortForTesting(first_addr.port()); @@ -575,6 +579,10 @@ TEST_F(TurnPortTest, TestSharedSocketAllocateMismatch) { EXPECT_TRUE_WAIT(turn_ready_, kTimeout); rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); + // Clear connected_ flag on turnport to suppress the release of + // the allocation. + turn_port_->OnSocketClose(turn_port_->socket(), 0); + turn_ready_ = false; CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); @@ -599,6 +607,10 @@ TEST_F(TurnPortTest, TestTurnTcpAllocateMismatch) { EXPECT_TRUE_WAIT(turn_ready_, kTimeout); rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); + // Clear connected_ flag on turnport to suppress the release of + // the allocation. + turn_port_->OnSocketClose(turn_port_->socket(), 0); + // Forces the socket server to assign the same port. ss_->SetNextPortForTesting(first_addr.port()); @@ -747,6 +759,29 @@ TEST_F(TurnPortTest, TestOriginHeader) { EXPECT_EQ(kTestOrigin, turn_server_.FindAllocation(local_address)->origin()); } +// Test that a TURN allocation is released when the port is closed. +TEST_F(TurnPortTest, TestTurnReleaseAllocation) { + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + + ASSERT_GT(turn_server_.server()->allocations().size(), 0U); + turn_port_.reset(); + EXPECT_EQ_WAIT(0U, turn_server_.server()->allocations().size(), kTimeout); +} + +// Test that a TURN TCP allocation is released when the port is closed. +TEST_F(TurnPortTest, DISABLED_TestTurnTCPReleaseAllocation) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + + ASSERT_GT(turn_server_.server()->allocations().size(), 0U); + turn_port_.reset(); + EXPECT_EQ_WAIT(0U, turn_server_.server()->allocations().size(), kTimeout); +} + // This test verifies any FD's are not leaked after TurnPort is destroyed. // https://code.google.com/p/webrtc/issues/detail?id=2651 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)