1 // Copyright 2011 Google Inc. All Rights Reserved. 2 3 4 #include <string> 5 6 #include "talk/base/asynchttprequest.h" 7 #include "talk/base/gunit.h" 8 #include "talk/base/fakenetwork.h" 9 #include "talk/base/scoped_ptr.h" 10 #include "talk/base/socketaddress.h" 11 #include "talk/p2p/base/basicpacketsocketfactory.h" 12 #include "talk/p2p/base/relayport.h" 13 #include "talk/p2p/base/stunport.h" 14 #include "talk/p2p/client/connectivitychecker.h" 15 #include "talk/p2p/client/httpportallocator.h" 16 17 namespace cricket { 18 19 static const talk_base::SocketAddress kClientAddr1("11.11.11.11", 0); 20 static const talk_base::SocketAddress kClientAddr2("22.22.22.22", 0); 21 static const talk_base::SocketAddress kExternalAddr("33.33.33.33", 3333); 22 static const talk_base::SocketAddress kStunAddr("44.44.44.44", 4444); 23 static const talk_base::SocketAddress kRelayAddr("55.55.55.55", 5555); 24 static const talk_base::SocketAddress kProxyAddr("66.66.66.66", 6666); 25 static const talk_base::ProxyType kProxyType = talk_base::PROXY_HTTPS; 26 static const char kChannelName[] = "rtp_test"; 27 static const int kComponent = 1; 28 static const char kRelayHost[] = "relay.google.com"; 29 static const char kRelayToken[] = 30 "CAESFwoOb2phQGdvb2dsZS5jb20Q043h47MmGhBTB1rbfIXkhuarDCZe+xF6"; 31 static const char kBrowserAgent[] = "browser_test"; 32 static const char kJid[] = "a.b@c"; 33 static const char kUserName[] = "testuser"; 34 static const char kPassword[] = "testpassword"; 35 static const char kMagicCookie[] = "testcookie"; 36 static const char kRelayUdpPort[] = "4444"; 37 static const char kRelayTcpPort[] = "5555"; 38 static const char kRelaySsltcpPort[] = "6666"; 39 static const char kSessionId[] = "testsession"; 40 static const char kConnection[] = "testconnection"; 41 static const int kMinPort = 1000; 42 static const int kMaxPort = 2000; 43 44 // Fake implementation to mock away real network usage. 45 class FakeRelayPort : public RelayPort { 46 public: 47 FakeRelayPort(talk_base::Thread* thread, 48 talk_base::PacketSocketFactory* factory, 49 talk_base::Network* network, const talk_base::IPAddress& ip, 50 int min_port, int max_port, 51 const std::string& username, const std::string& password) 52 : RelayPort(thread, factory, network, ip, min_port, max_port, 53 username, password) { 54 } 55 56 // Just signal that we are done. 57 virtual void PrepareAddress() { 58 SignalPortComplete(this); 59 } 60 }; 61 62 // Fake implementation to mock away real network usage. 63 class FakeStunPort : public StunPort { 64 public: 65 FakeStunPort(talk_base::Thread* thread, 66 talk_base::PacketSocketFactory* factory, 67 talk_base::Network* network, 68 const talk_base::IPAddress& ip, 69 int min_port, int max_port, 70 const std::string& username, const std::string& password, 71 const talk_base::SocketAddress& server_addr) 72 : StunPort(thread, factory, network, ip, min_port, max_port, 73 username, password, server_addr) { 74 } 75 76 // Just set external address and signal that we are done. 77 virtual void PrepareAddress() { 78 AddAddress(kExternalAddr, kExternalAddr, "udp", 79 STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, true); 80 SignalPortComplete(this); 81 } 82 }; 83 84 // Fake implementation to mock away real network usage by responding 85 // to http requests immediately. 86 class FakeHttpPortAllocatorSession : public TestHttpPortAllocatorSession { 87 public: 88 FakeHttpPortAllocatorSession( 89 HttpPortAllocator* allocator, 90 const std::string& content_name, 91 int component, 92 const std::string& ice_ufrag, const std::string& ice_pwd, 93 const std::vector<talk_base::SocketAddress>& stun_hosts, 94 const std::vector<std::string>& relay_hosts, 95 const std::string& relay_token, 96 const std::string& agent) 97 : TestHttpPortAllocatorSession(allocator, 98 content_name, 99 component, 100 ice_ufrag, 101 ice_pwd, 102 stun_hosts, 103 relay_hosts, 104 relay_token, 105 agent) { 106 } 107 virtual void SendSessionRequest(const std::string& host, int port) { 108 FakeReceiveSessionResponse(host, port); 109 } 110 111 // Pass results to the real implementation. 112 void FakeReceiveSessionResponse(const std::string& host, int port) { 113 talk_base::AsyncHttpRequest* response = CreateAsyncHttpResponse(port); 114 TestHttpPortAllocatorSession::OnRequestDone(response); 115 response->Destroy(true); 116 } 117 118 private: 119 // Helper method for creating a response to a relay session request. 120 talk_base::AsyncHttpRequest* CreateAsyncHttpResponse(int port) { 121 talk_base::AsyncHttpRequest* request = 122 new talk_base::AsyncHttpRequest(kBrowserAgent); 123 std::stringstream ss; 124 ss << "username=" << kUserName << std::endl 125 << "password=" << kPassword << std::endl 126 << "magic_cookie=" << kMagicCookie << std::endl 127 << "relay.ip=" << kRelayAddr.ipaddr().ToString() << std::endl 128 << "relay.udp_port=" << kRelayUdpPort << std::endl 129 << "relay.tcp_port=" << kRelayTcpPort << std::endl 130 << "relay.ssltcp_port=" << kRelaySsltcpPort << std::endl; 131 request->response().document.reset( 132 new talk_base::MemoryStream(ss.str().c_str())); 133 request->response().set_success(); 134 request->set_port(port); 135 request->set_secure(port == talk_base::HTTP_SECURE_PORT); 136 return request; 137 } 138 }; 139 140 // Fake implementation for creating fake http sessions. 141 class FakeHttpPortAllocator : public HttpPortAllocator { 142 public: 143 FakeHttpPortAllocator(talk_base::NetworkManager* network_manager, 144 const std::string& user_agent) 145 : HttpPortAllocator(network_manager, user_agent) { 146 } 147 148 virtual PortAllocatorSession* CreateSessionInternal( 149 const std::string& content_name, int component, 150 const std::string& ice_ufrag, const std::string& ice_pwd) { 151 std::vector<talk_base::SocketAddress> stun_hosts; 152 stun_hosts.push_back(kStunAddr); 153 std::vector<std::string> relay_hosts; 154 relay_hosts.push_back(kRelayHost); 155 return new FakeHttpPortAllocatorSession(this, 156 content_name, 157 component, 158 ice_ufrag, 159 ice_pwd, 160 stun_hosts, 161 relay_hosts, 162 kRelayToken, 163 kBrowserAgent); 164 } 165 }; 166 167 class ConnectivityCheckerForTest : public ConnectivityChecker { 168 public: 169 ConnectivityCheckerForTest(talk_base::Thread* worker, 170 const std::string& jid, 171 const std::string& session_id, 172 const std::string& user_agent, 173 const std::string& relay_token, 174 const std::string& connection) 175 : ConnectivityChecker(worker, 176 jid, 177 session_id, 178 user_agent, 179 relay_token, 180 connection), 181 proxy_initiated_(false) { 182 } 183 184 talk_base::FakeNetworkManager* network_manager() const { 185 return network_manager_; 186 } 187 188 FakeHttpPortAllocator* port_allocator() const { 189 return fake_port_allocator_; 190 } 191 192 protected: 193 // Overridden methods for faking a real network. 194 virtual talk_base::NetworkManager* CreateNetworkManager() { 195 network_manager_ = new talk_base::FakeNetworkManager(); 196 return network_manager_; 197 } 198 virtual talk_base::BasicPacketSocketFactory* CreateSocketFactory( 199 talk_base::Thread* thread) { 200 // Create socket factory, for simplicity, let it run on the current thread. 201 socket_factory_ = 202 new talk_base::BasicPacketSocketFactory(talk_base::Thread::Current()); 203 return socket_factory_; 204 } 205 virtual HttpPortAllocator* CreatePortAllocator( 206 talk_base::NetworkManager* network_manager, 207 const std::string& user_agent, 208 const std::string& relay_token) { 209 fake_port_allocator_ = 210 new FakeHttpPortAllocator(network_manager, user_agent); 211 return fake_port_allocator_; 212 } 213 virtual StunPort* CreateStunPort( 214 const std::string& username, const std::string& password, 215 const PortConfiguration* config, talk_base::Network* network) { 216 return new FakeStunPort(worker(), socket_factory_, 217 network, network->ip(), 218 kMinPort, kMaxPort, 219 username, password, 220 config->stun_address); 221 } 222 virtual RelayPort* CreateRelayPort( 223 const std::string& username, const std::string& password, 224 const PortConfiguration* config, talk_base::Network* network) { 225 return new FakeRelayPort(worker(), socket_factory_, 226 network, network->ip(), 227 kMinPort, kMaxPort, 228 username, password); 229 } 230 virtual void InitiateProxyDetection() { 231 if (!proxy_initiated_) { 232 proxy_initiated_ = true; 233 proxy_info_.address = kProxyAddr; 234 proxy_info_.type = kProxyType; 235 SetProxyInfo(proxy_info_); 236 } 237 } 238 239 virtual talk_base::ProxyInfo GetProxyInfo() const { 240 return proxy_info_; 241 } 242 243 private: 244 talk_base::BasicPacketSocketFactory* socket_factory_; 245 FakeHttpPortAllocator* fake_port_allocator_; 246 talk_base::FakeNetworkManager* network_manager_; 247 talk_base::ProxyInfo proxy_info_; 248 bool proxy_initiated_; 249 }; 250 251 class ConnectivityCheckerTest : public testing::Test { 252 protected: 253 void VerifyNic(const NicInfo& info, 254 const talk_base::SocketAddress& local_address) { 255 // Verify that the external address has been set. 256 EXPECT_EQ(kExternalAddr, info.external_address); 257 258 // Verify that the stun server address has been set. 259 EXPECT_EQ(kStunAddr, info.stun_server_address); 260 261 // Verify that the media server address has been set. Don't care 262 // about port since it is different for different protocols. 263 EXPECT_EQ(kRelayAddr.ipaddr(), info.media_server_address.ipaddr()); 264 265 // Verify that local ip matches. 266 EXPECT_EQ(local_address.ipaddr(), info.ip); 267 268 // Verify that we have received responses for our 269 // pings. Unsuccessful ping has rtt value -1, successful >= 0. 270 EXPECT_GE(info.stun.rtt, 0); 271 EXPECT_GE(info.udp.rtt, 0); 272 EXPECT_GE(info.tcp.rtt, 0); 273 EXPECT_GE(info.ssltcp.rtt, 0); 274 275 // If proxy has been set, verify address and type. 276 if (!info.proxy_info.address.IsNil()) { 277 EXPECT_EQ(kProxyAddr, info.proxy_info.address); 278 EXPECT_EQ(kProxyType, info.proxy_info.type); 279 } 280 } 281 }; 282 283 // Tests a configuration with two network interfaces. Verifies that 4 284 // combinations of ip/proxy are created and that all protocols are 285 // tested on each combination. 286 TEST_F(ConnectivityCheckerTest, TestStart) { 287 ConnectivityCheckerForTest connectivity_checker(talk_base::Thread::Current(), 288 kJid, 289 kSessionId, 290 kBrowserAgent, 291 kRelayToken, 292 kConnection); 293 connectivity_checker.Initialize(); 294 connectivity_checker.set_stun_address(kStunAddr); 295 connectivity_checker.network_manager()->AddInterface(kClientAddr1); 296 connectivity_checker.network_manager()->AddInterface(kClientAddr2); 297 298 connectivity_checker.Start(); 299 talk_base::Thread::Current()->ProcessMessages(1000); 300 301 NicMap nics = connectivity_checker.GetResults(); 302 303 // There should be 4 nics in our map. 2 for each interface added, 304 // one with proxy set and one without. 305 EXPECT_EQ(4U, nics.size()); 306 307 // First verify interfaces without proxy. 308 talk_base::SocketAddress nilAddress; 309 310 // First lookup the address of the first nic combined with no proxy. 311 NicMap::iterator i = nics.find(NicId(kClientAddr1.ipaddr(), nilAddress)); 312 ASSERT(i != nics.end()); 313 NicInfo info = i->second; 314 VerifyNic(info, kClientAddr1); 315 316 // Then make sure the second device has been tested without proxy. 317 i = nics.find(NicId(kClientAddr2.ipaddr(), nilAddress)); 318 ASSERT(i != nics.end()); 319 info = i->second; 320 VerifyNic(info, kClientAddr2); 321 322 // Now verify both interfaces with proxy. 323 i = nics.find(NicId(kClientAddr1.ipaddr(), kProxyAddr)); 324 ASSERT(i != nics.end()); 325 info = i->second; 326 VerifyNic(info, kClientAddr1); 327 328 i = nics.find(NicId(kClientAddr2.ipaddr(), kProxyAddr)); 329 ASSERT(i != nics.end()); 330 info = i->second; 331 VerifyNic(info, kClientAddr2); 332 }; 333 334 // Tests that nothing bad happens if thera are no network interfaces 335 // available to check. 336 TEST_F(ConnectivityCheckerTest, TestStartNoNetwork) { 337 ConnectivityCheckerForTest connectivity_checker(talk_base::Thread::Current(), 338 kJid, 339 kSessionId, 340 kBrowserAgent, 341 kRelayToken, 342 kConnection); 343 connectivity_checker.Initialize(); 344 connectivity_checker.Start(); 345 talk_base::Thread::Current()->ProcessMessages(1000); 346 347 NicMap nics = connectivity_checker.GetResults(); 348 349 // Verify that no nics where checked. 350 EXPECT_EQ(0U, nics.size()); 351 } 352 353 } // namespace cricket 354