• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
FakeRelayPort(talk_base::Thread * thread,talk_base::PacketSocketFactory * factory,talk_base::Network * network,const talk_base::IPAddress & ip,int min_port,int max_port,const std::string & username,const std::string & password)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.
PrepareAddress()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:
FakeStunPort(talk_base::Thread * thread,talk_base::PacketSocketFactory * factory,talk_base::Network * network,const talk_base::IPAddress & ip,int min_port,int max_port,const std::string & username,const std::string & password,const talk_base::SocketAddress & server_addr)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.
PrepareAddress()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:
FakeHttpPortAllocatorSession(HttpPortAllocator * allocator,const std::string & content_name,int component,const std::string & ice_ufrag,const std::string & ice_pwd,const std::vector<talk_base::SocketAddress> & stun_hosts,const std::vector<std::string> & relay_hosts,const std::string & relay_token,const std::string & agent)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   }
SendSessionRequest(const std::string & host,int port)107   virtual void SendSessionRequest(const std::string& host, int port) {
108     FakeReceiveSessionResponse(host, port);
109   }
110 
111   // Pass results to the real implementation.
FakeReceiveSessionResponse(const std::string & host,int port)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.
CreateAsyncHttpResponse(int port)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:
FakeHttpPortAllocator(talk_base::NetworkManager * network_manager,const std::string & user_agent)143   FakeHttpPortAllocator(talk_base::NetworkManager* network_manager,
144                         const std::string& user_agent)
145       : HttpPortAllocator(network_manager, user_agent) {
146   }
147 
CreateSessionInternal(const std::string & content_name,int component,const std::string & ice_ufrag,const std::string & ice_pwd)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:
ConnectivityCheckerForTest(talk_base::Thread * worker,const std::string & jid,const std::string & session_id,const std::string & user_agent,const std::string & relay_token,const std::string & connection)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 
network_manager() const184   talk_base::FakeNetworkManager* network_manager() const {
185     return network_manager_;
186   }
187 
port_allocator() const188   FakeHttpPortAllocator* port_allocator() const {
189     return fake_port_allocator_;
190   }
191 
192  protected:
193   // Overridden methods for faking a real network.
CreateNetworkManager()194   virtual talk_base::NetworkManager* CreateNetworkManager() {
195     network_manager_ = new talk_base::FakeNetworkManager();
196     return network_manager_;
197   }
CreateSocketFactory(talk_base::Thread * thread)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   }
CreatePortAllocator(talk_base::NetworkManager * network_manager,const std::string & user_agent,const std::string & relay_token)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   }
CreateStunPort(const std::string & username,const std::string & password,const PortConfiguration * config,talk_base::Network * network)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   }
CreateRelayPort(const std::string & username,const std::string & password,const PortConfiguration * config,talk_base::Network * network)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   }
InitiateProxyDetection()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 
GetProxyInfo() const239   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:
VerifyNic(const NicInfo & info,const talk_base::SocketAddress & local_address)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.
TEST_F(ConnectivityCheckerTest,TestStart)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.
TEST_F(ConnectivityCheckerTest,TestStartNoNetwork)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