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