Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / p2p / base / p2ptransportchannel.cc
index 38cc354..b1dc223 100644 (file)
 #include "talk/p2p/base/p2ptransportchannel.h"
 
 #include <set>
-#include "talk/base/common.h"
-#include "talk/base/crc32.h"
-#include "talk/base/logging.h"
-#include "talk/base/stringencode.h"
 #include "talk/p2p/base/common.h"
 #include "talk/p2p/base/relayport.h"  // For RELAY_PORT_TYPE.
 #include "talk/p2p/base/stunport.h"  // For STUN_PORT_TYPE.
+#include "webrtc/base/common.h"
+#include "webrtc/base/crc32.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/stringencode.h"
 
 namespace {
 
@@ -159,7 +159,7 @@ P2PTransportChannel::P2PTransportChannel(const std::string& content_name,
     TransportChannelImpl(content_name, component),
     transport_(transport),
     allocator_(allocator),
-    worker_thread_(talk_base::Thread::Current()),
+    worker_thread_(rtc::Thread::Current()),
     incoming_only_(false),
     waiting_for_signaling_(false),
     error_(0),
@@ -167,7 +167,7 @@ P2PTransportChannel::P2PTransportChannel(const std::string& content_name,
     pending_best_connection_(NULL),
     sort_dirty_(false),
     was_writable_(false),
-    protocol_type_(ICEPROTO_GOOGLE),
+    protocol_type_(ICEPROTO_HYBRID),
     remote_ice_mode_(ICEMODE_FULL),
     ice_role_(ICEROLE_UNKNOWN),
     tiebreaker_(0),
@@ -175,7 +175,7 @@ P2PTransportChannel::P2PTransportChannel(const std::string& content_name,
 }
 
 P2PTransportChannel::~P2PTransportChannel() {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   for (uint32 i = 0; i < allocator_sessions_.size(); ++i)
     delete allocator_sessions_[i];
@@ -216,7 +216,7 @@ void P2PTransportChannel::AddConnection(Connection* connection) {
 }
 
 void P2PTransportChannel::SetIceRole(IceRole ice_role) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   if (ice_role_ != ice_role) {
     ice_role_ = ice_role;
     for (std::vector<PortInterface *>::iterator it = ports_.begin();
@@ -227,7 +227,7 @@ void P2PTransportChannel::SetIceRole(IceRole ice_role) {
 }
 
 void P2PTransportChannel::SetIceTiebreaker(uint64 tiebreaker) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   if (!ports_.empty()) {
     LOG(LS_ERROR)
         << "Attempt to change tiebreaker after Port has been allocated.";
@@ -237,8 +237,13 @@ void P2PTransportChannel::SetIceTiebreaker(uint64 tiebreaker) {
   tiebreaker_ = tiebreaker;
 }
 
+bool P2PTransportChannel::GetIceProtocolType(IceProtocolType* type) const {
+  *type = protocol_type_;
+  return true;
+}
+
 void P2PTransportChannel::SetIceProtocolType(IceProtocolType type) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   protocol_type_ = type;
   for (std::vector<PortInterface *>::iterator it = ports_.begin();
@@ -249,12 +254,13 @@ void P2PTransportChannel::SetIceProtocolType(IceProtocolType type) {
 
 void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag,
                                             const std::string& ice_pwd) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   bool ice_restart = false;
   if (!ice_ufrag_.empty() && !ice_pwd_.empty()) {
     // Restart candidate allocation if there is any change in either
     // ice ufrag or password.
-    ice_restart = (ice_ufrag_ != ice_ufrag) || (ice_pwd_!= ice_pwd);
+    ice_restart =
+        IceCredentialsChanged(ice_ufrag_, ice_pwd_, ice_ufrag, ice_pwd);
   }
 
   ice_ufrag_ = ice_ufrag;
@@ -268,7 +274,7 @@ void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag,
 
 void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag,
                                                   const std::string& ice_pwd) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   bool ice_restart = false;
   if (!remote_ice_ufrag_.empty() && !remote_ice_pwd_.empty()) {
     ice_restart = (remote_ice_ufrag_ != ice_ufrag) ||
@@ -292,7 +298,7 @@ void P2PTransportChannel::SetRemoteIceMode(IceMode mode) {
 
 // Go into the state of processing candidates, and running in general
 void P2PTransportChannel::Connect() {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   if (ice_ufrag_.empty() || ice_pwd_.empty()) {
     ASSERT(false);
     LOG(LS_ERROR) << "P2PTransportChannel::Connect: The ice_ufrag_ and the "
@@ -309,7 +315,7 @@ void P2PTransportChannel::Connect() {
 
 // Reset the socket, clear up any previous allocations and start over
 void P2PTransportChannel::Reset() {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Get rid of all the old allocators.  This should clean up everything.
   for (uint32 i = 0; i < allocator_sessions_.size(); ++i)
@@ -343,7 +349,7 @@ void P2PTransportChannel::Reset() {
 // A new port is available, attempt to make connections for it
 void P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
                                       PortInterface* port) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Set in-effect options on the new port
   for (OptionMap::const_iterator it = options_.begin();
@@ -386,7 +392,7 @@ void P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
 // A new candidate is available, let listeners know
 void P2PTransportChannel::OnCandidatesReady(
     PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   for (size_t i = 0; i < candidates.size(); ++i) {
     SignalCandidateReady(this, candidates[i]);
   }
@@ -394,17 +400,17 @@ void P2PTransportChannel::OnCandidatesReady(
 
 void P2PTransportChannel::OnCandidatesAllocationDone(
     PortAllocatorSession* session) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   SignalCandidatesAllocationDone(this);
 }
 
 // Handle stun packets
 void P2PTransportChannel::OnUnknownAddress(
     PortInterface* port,
-    const talk_base::SocketAddress& address, ProtocolType proto,
+    const rtc::SocketAddress& address, ProtocolType proto,
     IceMessage* stun_msg, const std::string &remote_username,
     bool port_muxed) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Port has received a valid stun packet from an address that no Connection
   // is currently available for. See if we already have a candidate with the
@@ -465,9 +471,8 @@ void P2PTransportChannel::OnUnknownAddress(
     }
   } else {
     // Create a new candidate with this address.
-
     std::string type;
-    if (protocol_type_ == ICEPROTO_RFC5245) {
+    if (port->IceProtocol() == ICEPROTO_RFC5245) {
       type = PRFLX_PORT_TYPE;
     } else {
       // G-ICE doesn't support prflx candidate.
@@ -481,17 +486,18 @@ void P2PTransportChannel::OnUnknownAddress(
       }
     }
 
-    std::string id = talk_base::CreateRandomString(8);
+    std::string id = rtc::CreateRandomString(8);
     new_remote_candidate = Candidate(
         id, component(), ProtoToString(proto), address,
         0, remote_username, remote_password, type,
         port->Network()->name(), 0U,
-        talk_base::ToString<uint32>(talk_base::ComputeCrc32(id)));
+        rtc::ToString<uint32>(rtc::ComputeCrc32(id)));
     new_remote_candidate.set_priority(
-        new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX));
+        new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX,
+                                         port->Network()->preference(), 0));
   }
 
-  if (protocol_type_ == ICEPROTO_RFC5245) {
+  if (port->IceProtocol() == ICEPROTO_RFC5245) {
     // RFC 5245
     // If the source transport address of the request does not match any
     // existing remote candidates, it represents a new peer reflexive remote
@@ -585,7 +591,7 @@ void P2PTransportChannel::OnRoleConflict(PortInterface* port) {
 
 // When the signalling channel is ready, we can really kick off the allocator
 void P2PTransportChannel::OnSignalingReady() {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   if (waiting_for_signaling_) {
     waiting_for_signaling_ = false;
     AddAllocatorSession(allocator_->CreateSession(
@@ -594,7 +600,7 @@ void P2PTransportChannel::OnSignalingReady() {
 }
 
 void P2PTransportChannel::OnUseCandidate(Connection* conn) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   ASSERT(ice_role_ == ICEROLE_CONTROLLED);
   ASSERT(protocol_type_ == ICEPROTO_RFC5245);
   if (conn->write_state() == Connection::STATE_WRITABLE) {
@@ -611,7 +617,7 @@ void P2PTransportChannel::OnUseCandidate(Connection* conn) {
 }
 
 void P2PTransportChannel::OnCandidate(const Candidate& candidate) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Create connections to this remote candidate.
   CreateConnections(candidate, NULL, false);
@@ -623,10 +629,10 @@ void P2PTransportChannel::OnCandidate(const Candidate& candidate) {
 // Creates connections from all of the ports that we care about to the given
 // remote candidate.  The return value is true if we created a connection from
 // the origin port.
-bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate,
+bool P2PTransportChannel::CreateConnections(const Candidateremote_candidate,
                                             PortInterface* origin_port,
                                             bool readable) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   Candidate new_remote_candidate(remote_candidate);
   new_remote_candidate.set_generation(
@@ -642,14 +648,25 @@ bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate,
     new_remote_candidate.set_password(remote_ice_pwd_);
   }
 
+  // If we've already seen the new remote candidate (in the current candidate
+  // generation), then we shouldn't try creating connections for it.
+  // We either already have a connection for it, or we previously created one
+  // and then later pruned it. If we don't return, the channel will again
+  // re-create any connections that were previously pruned, which will then
+  // immediately be re-pruned, churning the network for no purpose.
+  // This only applies to candidates received over signaling (i.e. origin_port
+  // is NULL).
+  if (!origin_port && IsDuplicateRemoteCandidate(new_remote_candidate)) {
+    // return true to indicate success, without creating any new connections.
+    return true;
+  }
+
   // Add a new connection for this candidate to every port that allows such a
   // connection (i.e., if they have compatible protocols) and that does not
   // already have a connection to an equivalent candidate.  We must be careful
   // to make sure that the origin port is included, even if it was pruned,
   // since that may be the only port that can create this connection.
-
   bool created = false;
-
   std::vector<PortInterface *>::reverse_iterator it;
   for (it = ports_.rbegin(); it != ports_.rend(); ++it) {
     if (CreateConnection(*it, new_remote_candidate, origin_port, readable)) {
@@ -684,7 +701,11 @@ bool P2PTransportChannel::CreateConnection(PortInterface* port,
     // It is not legal to try to change any of the parameters of an existing
     // connection; however, the other side can send a duplicate candidate.
     if (!remote_candidate.IsEquivalent(connection->remote_candidate())) {
-      LOG(INFO) << "Attempt to change a remote candidate";
+      LOG(INFO) << "Attempt to change a remote candidate."
+                << " Existing remote candidate: "
+                << connection->remote_candidate().ToString()
+                << "New remote candidate: "
+                << remote_candidate.ToString();
       return false;
     }
   } else {
@@ -734,6 +755,17 @@ uint32 P2PTransportChannel::GetRemoteCandidateGeneration(
   return remote_candidate_generation_;
 }
 
+// Check if remote candidate is already cached.
+bool P2PTransportChannel::IsDuplicateRemoteCandidate(
+    const Candidate& candidate) {
+  for (uint32 i = 0; i < remote_candidates_.size(); ++i) {
+    if (remote_candidates_[i].IsEquivalent(candidate)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 // Maintain our remote candidate list, adding this new remote one.
 void P2PTransportChannel::RememberRemoteCandidate(
     const Candidate& remote_candidate, PortInterface* origin_port) {
@@ -751,12 +783,9 @@ void P2PTransportChannel::RememberRemoteCandidate(
   }
 
   // Make sure this candidate is not a duplicate.
-  for (uint32 i = 0; i < remote_candidates_.size(); ++i) {
-    if (remote_candidates_[i].IsEquivalent(remote_candidate)) {
-      LOG(INFO) << "Duplicate candidate: "
-                << remote_candidate.address().ToSensitiveString();
-      return;
-    }
+  if (IsDuplicateRemoteCandidate(remote_candidate)) {
+    LOG(INFO) << "Duplicate candidate: " << remote_candidate.ToString();
+    return;
   }
 
   // Try this candidate for all future ports.
@@ -765,7 +794,7 @@ void P2PTransportChannel::RememberRemoteCandidate(
 
 // Set options on ourselves is simply setting options on all of our available
 // port objects.
-int P2PTransportChannel::SetOption(talk_base::Socket::Option opt, int value) {
+int P2PTransportChannel::SetOption(rtc::Socket::Option opt, int value) {
   OptionMap::iterator it = options_.find(opt);
   if (it == options_.end()) {
     options_.insert(std::make_pair(opt, value));
@@ -789,9 +818,9 @@ int P2PTransportChannel::SetOption(talk_base::Socket::Option opt, int value) {
 
 // Send data to the other side, using our best connection.
 int P2PTransportChannel::SendPacket(const char *data, size_t len,
-                                    talk_base::DiffServCodePoint dscp,
+                                    const rtc::PacketOptions& options,
                                     int flags) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   if (flags != 0) {
     error_ = EINVAL;
     return -1;
@@ -801,7 +830,7 @@ int P2PTransportChannel::SendPacket(const char *data, size_t len,
     return -1;
   }
 
-  int sent = best_connection_->Send(data, len, dscp);
+  int sent = best_connection_->Send(data, len, options);
   if (sent <= 0) {
     ASSERT(sent < 0);
     error_ = best_connection_->GetError();
@@ -810,7 +839,7 @@ int P2PTransportChannel::SendPacket(const char *data, size_t len,
 }
 
 bool P2PTransportChannel::GetStats(ConnectionInfos *infos) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   // Gather connection infos.
   infos->clear();
 
@@ -841,12 +870,12 @@ bool P2PTransportChannel::GetStats(ConnectionInfos *infos) {
   return true;
 }
 
-talk_base::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const {
-  OptionMap::const_iterator it = options_.find(talk_base::Socket::OPT_DSCP);
+rtc::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const {
+  OptionMap::const_iterator it = options_.find(rtc::Socket::OPT_DSCP);
   if (it == options_.end()) {
-    return talk_base::DSCP_NO_CHANGE;
+    return rtc::DSCP_NO_CHANGE;
   }
-  return static_cast<talk_base::DiffServCodePoint> (it->second);
+  return static_cast<rtc::DiffServCodePoint> (it->second);
 }
 
 // Begin allocate (or immediately re-allocate, if MSG_ALLOCATE pending)
@@ -859,7 +888,7 @@ void P2PTransportChannel::Allocate() {
 
 // Monitor connection states.
 void P2PTransportChannel::UpdateConnectionStates() {
-  uint32 now = talk_base::Time();
+  uint32 now = rtc::Time();
 
   // We need to copy the list of connections since some may delete themselves
   // when we call UpdateState.
@@ -878,17 +907,26 @@ void P2PTransportChannel::RequestSort() {
 // Sort the available connections to find the best one.  We also monitor
 // the number of available connections and the current state.
 void P2PTransportChannel::SortConnections() {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Make sure the connection states are up-to-date since this affects how they
   // will be sorted.
   UpdateConnectionStates();
 
+  if (protocol_type_ == ICEPROTO_HYBRID) {
+    // If we are in hybrid mode, we are not sending any ping requests, so there
+    // is no point in sorting the connections. In hybrid state, ports can have
+    // different protocol than hybrid and protocol may differ from one another.
+    // Instead just update the state of this channel
+    UpdateChannelState();
+    return;
+  }
+
   // Any changes after this point will require a re-sort.
   sort_dirty_ = false;
 
   // Get a list of the networks that we are using.
-  std::set<talk_base::Network*> networks;
+  std::set<rtc::Network*> networks;
   for (uint32 i = 0; i < connections_.size(); ++i)
     networks.insert(connections_[i]->port()->Network());
 
@@ -924,7 +962,7 @@ void P2PTransportChannel::SortConnections() {
   // we would prune out the current best connection).  We leave connections on
   // other networks because they may not be using the same resources and they
   // may represent very distinct paths over which we can switch.
-  std::set<talk_base::Network*>::iterator network;
+  std::set<rtc::Network*>::iterator network;
   for (network = networks.begin(); network != networks.end(); ++network) {
     Connection* primier = GetBestConnectionOnNetwork(*network);
     if (!primier || (primier->write_state() != Connection::STATE_WRITABLE))
@@ -995,8 +1033,10 @@ void P2PTransportChannel::UpdateChannelState() {
 
   bool readable = false;
   for (uint32 i = 0; i < connections_.size(); ++i) {
-    if (connections_[i]->read_state() == Connection::STATE_READABLE)
+    if (connections_[i]->read_state() == Connection::STATE_READABLE) {
       readable = true;
+      break;
+    }
   }
   set_readable(readable);
 }
@@ -1004,7 +1044,7 @@ void P2PTransportChannel::UpdateChannelState() {
 // We checked the status of our connections and we had at least one that
 // was writable, go into the writable state.
 void P2PTransportChannel::HandleWritable() {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   if (!writable()) {
     for (uint32 i = 0; i < allocator_sessions_.size(); ++i) {
       if (allocator_sessions_[i]->IsGettingPorts()) {
@@ -1019,7 +1059,7 @@ void P2PTransportChannel::HandleWritable() {
 
 // Notify upper layer about channel not writable state, if it was before.
 void P2PTransportChannel::HandleNotWritable() {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   if (was_writable_) {
     was_writable_ = false;
     set_writable(false);
@@ -1034,7 +1074,7 @@ void P2PTransportChannel::HandleAllTimedOut() {
 // If we have a best connection, return it, otherwise return top one in the
 // list (later we will mark it best).
 Connection* P2PTransportChannel::GetBestConnectionOnNetwork(
-    talk_base::Network* network) {
+    rtc::Network* network) {
   // If the best connection is on this network, then it wins.
   if (best_connection_ && (best_connection_->port()->Network() == network))
     return best_connection_;
@@ -1049,7 +1089,7 @@ Connection* P2PTransportChannel::GetBestConnectionOnNetwork(
 }
 
 // Handle any queued up requests
-void P2PTransportChannel::OnMessage(talk_base::Message *pmsg) {
+void P2PTransportChannel::OnMessage(rtc::Message *pmsg) {
   switch (pmsg->message_id) {
     case MSG_SORT:
       OnSort();
@@ -1111,7 +1151,7 @@ bool P2PTransportChannel::IsPingable(Connection* conn) {
 // pingable connection unless we have a writable connection that is past the
 // maximum acceptable ping delay.
 Connection* P2PTransportChannel::FindNextPingableConnection() {
-  uint32 now = talk_base::Time();
+  uint32 now = rtc::Time();
   if (best_connection_ &&
       (best_connection_->write_state() == Connection::STATE_WRITABLE) &&
       (best_connection_->last_ping_sent()
@@ -1157,13 +1197,13 @@ void P2PTransportChannel::PingConnection(Connection* conn) {
     }
   }
   conn->set_use_candidate_attr(use_candidate);
-  conn->Ping(talk_base::Time());
+  conn->Ping(rtc::Time());
 }
 
 // When a connection's state changes, we need to figure out who to use as
 // the best connection again.  It could have become usable, or become unusable.
 void P2PTransportChannel::OnConnectionStateChange(Connection* connection) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Update the best connection if the state change is from pending best
   // connection and role is controlled.
@@ -1182,7 +1222,7 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) {
 // When a connection is removed, edit it out, and then update our best
 // connection.
 void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Note: the previous best_connection_ may be destroyed by now, so don't
   // use it.
@@ -1209,12 +1249,14 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
     SwitchBestConnectionTo(NULL);
     RequestSort();
   }
+
+  SignalConnectionRemoved(this);
 }
 
 // When a port is destroyed remove it from our list of ports to use for
 // connection attempts.
 void P2PTransportChannel::OnPortDestroyed(PortInterface* port) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Remove this port from the list (if we didn't drop it already).
   std::vector<PortInterface*>::iterator iter =
@@ -1229,8 +1271,8 @@ void P2PTransportChannel::OnPortDestroyed(PortInterface* port) {
 // We data is available, let listeners know
 void P2PTransportChannel::OnReadPacket(
     Connection *connection, const char *data, size_t len,
-    const talk_base::PacketTime& packet_time) {
-  ASSERT(worker_thread_ == talk_base::Thread::Current());
+    const rtc::PacketTime& packet_time) {
+  ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Do not deliver, if packet doesn't belong to the correct transport channel.
   if (!FindConnection(connection))