release the turn allocation by sending a refresh request with lifetime 0

BUG=406578

Patch originally from philipp.hancke@googlemail.com

R=juberti@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/41449004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@8087 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/p2p/base/stunrequest.cc b/webrtc/p2p/base/stunrequest.cc
index a6f4c15..32b4694 100644
--- a/webrtc/p2p/base/stunrequest.cc
+++ b/webrtc/p2p/base/stunrequest.cc
@@ -44,7 +44,11 @@
   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 37f0c11..a3e355e 100644
--- a/webrtc/p2p/base/turnport.cc
+++ b/webrtc/p2p/base/turnport.cc
@@ -78,9 +78,11 @@
   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() {
   // 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 @@
   if (!connected_) {
     OnAllocateError();
   }
+  connected_ = false;
 }
 
 void TurnPort::OnAllocateMismatch() {
@@ -1020,13 +1032,19 @@
 
 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 f84d106..da2c6b9 100644
--- a/webrtc/p2p/base/turnport_unittest.cc
+++ b/webrtc/p2p/base/turnport_unittest.cc
@@ -550,6 +550,10 @@
   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 @@
   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 @@
   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 @@
   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)