2 * Copyright 2011 The WebRTC Project Authors. All rights reserved.
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
13 #include "webrtc/p2p/base/basicpacketsocketfactory.h"
14 #include "webrtc/p2p/base/relayport.h"
15 #include "webrtc/p2p/base/stunport.h"
16 #include "webrtc/p2p/client/connectivitychecker.h"
17 #include "webrtc/p2p/client/httpportallocator.h"
18 #include "webrtc/base/asynchttprequest.h"
19 #include "webrtc/base/fakenetwork.h"
20 #include "webrtc/base/gunit.h"
21 #include "webrtc/base/scoped_ptr.h"
22 #include "webrtc/base/socketaddress.h"
26 static const rtc::SocketAddress kClientAddr1("11.11.11.11", 0);
27 static const rtc::SocketAddress kClientAddr2("22.22.22.22", 0);
28 static const rtc::SocketAddress kExternalAddr("33.33.33.33", 3333);
29 static const rtc::SocketAddress kStunAddr("44.44.44.44", 4444);
30 static const rtc::SocketAddress kRelayAddr("55.55.55.55", 5555);
31 static const rtc::SocketAddress kProxyAddr("66.66.66.66", 6666);
32 static const rtc::ProxyType kProxyType = rtc::PROXY_HTTPS;
33 static const char kRelayHost[] = "relay.google.com";
34 static const char kRelayToken[] =
35 "CAESFwoOb2phQGdvb2dsZS5jb20Q043h47MmGhBTB1rbfIXkhuarDCZe+xF6";
36 static const char kBrowserAgent[] = "browser_test";
37 static const char kJid[] = "a.b@c";
38 static const char kUserName[] = "testuser";
39 static const char kPassword[] = "testpassword";
40 static const char kMagicCookie[] = "testcookie";
41 static const char kRelayUdpPort[] = "4444";
42 static const char kRelayTcpPort[] = "5555";
43 static const char kRelaySsltcpPort[] = "6666";
44 static const char kSessionId[] = "testsession";
45 static const char kConnection[] = "testconnection";
46 static const int kMinPort = 1000;
47 static const int kMaxPort = 2000;
49 // Fake implementation to mock away real network usage.
50 class FakeRelayPort : public RelayPort {
52 FakeRelayPort(rtc::Thread* thread,
53 rtc::PacketSocketFactory* factory,
54 rtc::Network* network, const rtc::IPAddress& ip,
55 int min_port, int max_port,
56 const std::string& username, const std::string& password)
57 : RelayPort(thread, factory, network, ip, min_port, max_port,
61 // Just signal that we are done.
62 virtual void PrepareAddress() {
63 SignalPortComplete(this);
67 // Fake implementation to mock away real network usage.
68 class FakeStunPort : public StunPort {
70 FakeStunPort(rtc::Thread* thread,
71 rtc::PacketSocketFactory* factory,
72 rtc::Network* network,
73 const rtc::IPAddress& ip,
74 int min_port, int max_port,
75 const std::string& username, const std::string& password,
76 const ServerAddresses& server_addr)
77 : StunPort(thread, factory, network, ip, min_port, max_port,
78 username, password, server_addr) {
81 // Just set external address and signal that we are done.
82 virtual void PrepareAddress() {
83 AddAddress(kExternalAddr, kExternalAddr, rtc::SocketAddress(), "udp", "",
84 STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, 0, true);
85 SignalPortComplete(this);
89 // Fake implementation to mock away real network usage by responding
90 // to http requests immediately.
91 class FakeHttpPortAllocatorSession : public TestHttpPortAllocatorSession {
93 FakeHttpPortAllocatorSession(
94 HttpPortAllocator* allocator,
95 const std::string& content_name,
97 const std::string& ice_ufrag, const std::string& ice_pwd,
98 const std::vector<rtc::SocketAddress>& stun_hosts,
99 const std::vector<std::string>& relay_hosts,
100 const std::string& relay_token,
101 const std::string& agent)
102 : TestHttpPortAllocatorSession(allocator,
112 virtual void SendSessionRequest(const std::string& host, int port) {
113 FakeReceiveSessionResponse(host, port);
116 // Pass results to the real implementation.
117 void FakeReceiveSessionResponse(const std::string& host, int port) {
118 rtc::AsyncHttpRequest* response = CreateAsyncHttpResponse(port);
119 TestHttpPortAllocatorSession::OnRequestDone(response);
120 response->Destroy(true);
124 // Helper method for creating a response to a relay session request.
125 rtc::AsyncHttpRequest* CreateAsyncHttpResponse(int port) {
126 rtc::AsyncHttpRequest* request =
127 new rtc::AsyncHttpRequest(kBrowserAgent);
128 std::stringstream ss;
129 ss << "username=" << kUserName << std::endl
130 << "password=" << kPassword << std::endl
131 << "magic_cookie=" << kMagicCookie << std::endl
132 << "relay.ip=" << kRelayAddr.ipaddr().ToString() << std::endl
133 << "relay.udp_port=" << kRelayUdpPort << std::endl
134 << "relay.tcp_port=" << kRelayTcpPort << std::endl
135 << "relay.ssltcp_port=" << kRelaySsltcpPort << std::endl;
136 request->response().document.reset(
137 new rtc::MemoryStream(ss.str().c_str()));
138 request->response().set_success();
139 request->set_port(port);
140 request->set_secure(port == rtc::HTTP_SECURE_PORT);
145 // Fake implementation for creating fake http sessions.
146 class FakeHttpPortAllocator : public HttpPortAllocator {
148 FakeHttpPortAllocator(rtc::NetworkManager* network_manager,
149 const std::string& user_agent)
150 : HttpPortAllocator(network_manager, user_agent) {
153 virtual PortAllocatorSession* CreateSessionInternal(
154 const std::string& content_name, int component,
155 const std::string& ice_ufrag, const std::string& ice_pwd) {
156 std::vector<rtc::SocketAddress> stun_hosts;
157 stun_hosts.push_back(kStunAddr);
158 std::vector<std::string> relay_hosts;
159 relay_hosts.push_back(kRelayHost);
160 return new FakeHttpPortAllocatorSession(this,
172 class ConnectivityCheckerForTest : public ConnectivityChecker {
174 ConnectivityCheckerForTest(rtc::Thread* worker,
175 const std::string& jid,
176 const std::string& session_id,
177 const std::string& user_agent,
178 const std::string& relay_token,
179 const std::string& connection)
180 : ConnectivityChecker(worker,
186 proxy_initiated_(false) {
189 rtc::FakeNetworkManager* network_manager() const {
190 return network_manager_;
193 FakeHttpPortAllocator* port_allocator() const {
194 return fake_port_allocator_;
198 // Overridden methods for faking a real network.
199 virtual rtc::NetworkManager* CreateNetworkManager() {
200 network_manager_ = new rtc::FakeNetworkManager();
201 return network_manager_;
203 virtual rtc::BasicPacketSocketFactory* CreateSocketFactory(
204 rtc::Thread* thread) {
205 // Create socket factory, for simplicity, let it run on the current thread.
207 new rtc::BasicPacketSocketFactory(rtc::Thread::Current());
208 return socket_factory_;
210 virtual HttpPortAllocator* CreatePortAllocator(
211 rtc::NetworkManager* network_manager,
212 const std::string& user_agent,
213 const std::string& relay_token) {
214 fake_port_allocator_ =
215 new FakeHttpPortAllocator(network_manager, user_agent);
216 return fake_port_allocator_;
218 virtual StunPort* CreateStunPort(
219 const std::string& username, const std::string& password,
220 const PortConfiguration* config, rtc::Network* network) {
221 return new FakeStunPort(worker(),
224 #ifdef USE_WEBRTC_DEV_BRANCH
225 network->GetBestIP(),
226 #else // USE_WEBRTC_DEV_BRANCH
228 #endif // USE_WEBRTC_DEV_BRANCH
233 config->stun_servers);
235 virtual RelayPort* CreateRelayPort(
236 const std::string& username, const std::string& password,
237 const PortConfiguration* config, rtc::Network* network) {
238 return new FakeRelayPort(worker(),
241 #ifdef USE_WEBRTC_DEV_BRANCH
242 network->GetBestIP(),
243 #else // USE_WEBRTC_DEV_BRANCH
245 #endif // USE_WEBRTC_DEV_BRANCH
251 virtual void InitiateProxyDetection() {
252 if (!proxy_initiated_) {
253 proxy_initiated_ = true;
254 proxy_info_.address = kProxyAddr;
255 proxy_info_.type = kProxyType;
256 SetProxyInfo(proxy_info_);
260 virtual rtc::ProxyInfo GetProxyInfo() const {
265 rtc::BasicPacketSocketFactory* socket_factory_;
266 FakeHttpPortAllocator* fake_port_allocator_;
267 rtc::FakeNetworkManager* network_manager_;
268 rtc::ProxyInfo proxy_info_;
269 bool proxy_initiated_;
272 class ConnectivityCheckerTest : public testing::Test {
274 void VerifyNic(const NicInfo& info,
275 const rtc::SocketAddress& local_address) {
276 // Verify that the external address has been set.
277 EXPECT_EQ(kExternalAddr, info.external_address);
279 // Verify that the stun server address has been set.
280 EXPECT_EQ(1U, info.stun_server_addresses.size());
281 EXPECT_EQ(kStunAddr, *(info.stun_server_addresses.begin()));
283 // Verify that the media server address has been set. Don't care
284 // about port since it is different for different protocols.
285 EXPECT_EQ(kRelayAddr.ipaddr(), info.media_server_address.ipaddr());
287 // Verify that local ip matches.
288 EXPECT_EQ(local_address.ipaddr(), info.ip);
290 // Verify that we have received responses for our
291 // pings. Unsuccessful ping has rtt value -1, successful >= 0.
292 EXPECT_GE(info.stun.rtt, 0);
293 EXPECT_GE(info.udp.rtt, 0);
294 EXPECT_GE(info.tcp.rtt, 0);
295 EXPECT_GE(info.ssltcp.rtt, 0);
297 // If proxy has been set, verify address and type.
298 if (!info.proxy_info.address.IsNil()) {
299 EXPECT_EQ(kProxyAddr, info.proxy_info.address);
300 EXPECT_EQ(kProxyType, info.proxy_info.type);
305 // Tests a configuration with two network interfaces. Verifies that 4
306 // combinations of ip/proxy are created and that all protocols are
307 // tested on each combination.
308 TEST_F(ConnectivityCheckerTest, TestStart) {
309 ConnectivityCheckerForTest connectivity_checker(rtc::Thread::Current(),
315 connectivity_checker.Initialize();
316 connectivity_checker.set_stun_address(kStunAddr);
317 connectivity_checker.network_manager()->AddInterface(kClientAddr1);
318 connectivity_checker.network_manager()->AddInterface(kClientAddr2);
320 connectivity_checker.Start();
321 rtc::Thread::Current()->ProcessMessages(1000);
323 NicMap nics = connectivity_checker.GetResults();
325 // There should be 4 nics in our map. 2 for each interface added,
326 // one with proxy set and one without.
327 EXPECT_EQ(4U, nics.size());
329 // First verify interfaces without proxy.
330 rtc::SocketAddress nilAddress;
332 // First lookup the address of the first nic combined with no proxy.
333 NicMap::iterator i = nics.find(NicId(kClientAddr1.ipaddr(), nilAddress));
334 ASSERT(i != nics.end());
335 NicInfo info = i->second;
336 VerifyNic(info, kClientAddr1);
338 // Then make sure the second device has been tested without proxy.
339 i = nics.find(NicId(kClientAddr2.ipaddr(), nilAddress));
340 ASSERT(i != nics.end());
342 VerifyNic(info, kClientAddr2);
344 // Now verify both interfaces with proxy.
345 i = nics.find(NicId(kClientAddr1.ipaddr(), kProxyAddr));
346 ASSERT(i != nics.end());
348 VerifyNic(info, kClientAddr1);
350 i = nics.find(NicId(kClientAddr2.ipaddr(), kProxyAddr));
351 ASSERT(i != nics.end());
353 VerifyNic(info, kClientAddr2);
356 // Tests that nothing bad happens if thera are no network interfaces
357 // available to check.
358 TEST_F(ConnectivityCheckerTest, TestStartNoNetwork) {
359 ConnectivityCheckerForTest connectivity_checker(rtc::Thread::Current(),
365 connectivity_checker.Initialize();
366 connectivity_checker.Start();
367 rtc::Thread::Current()->ProcessMessages(1000);
369 NicMap nics = connectivity_checker.GetResults();
371 // Verify that no nics where checked.
372 EXPECT_EQ(0U, nics.size());
375 } // namespace cricket