1 // Copyright 2011 Google Inc. All Rights Reserved.
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"
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 kRelayHost[] = "relay.google.com";
27 static const char kRelayToken[] =
28 "CAESFwoOb2phQGdvb2dsZS5jb20Q043h47MmGhBTB1rbfIXkhuarDCZe+xF6";
29 static const char kBrowserAgent[] = "browser_test";
30 static const char kJid[] = "a.b@c";
31 static const char kUserName[] = "testuser";
32 static const char kPassword[] = "testpassword";
33 static const char kMagicCookie[] = "testcookie";
34 static const char kRelayUdpPort[] = "4444";
35 static const char kRelayTcpPort[] = "5555";
36 static const char kRelaySsltcpPort[] = "6666";
37 static const char kSessionId[] = "testsession";
38 static const char kConnection[] = "testconnection";
39 static const int kMinPort = 1000;
40 static const int kMaxPort = 2000;
42 // Fake implementation to mock away real network usage.
43 class FakeRelayPort : public RelayPort {
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,
54 // Just signal that we are done.
55 virtual void PrepareAddress() {
56 SignalPortComplete(this);
60 // Fake implementation to mock away real network usage.
61 class FakeStunPort : public StunPort {
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) {
74 // Just set external address and signal that we are done.
75 virtual void PrepareAddress() {
76 AddAddress(kExternalAddr, kExternalAddr, talk_base::SocketAddress(), "udp",
77 STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, true);
78 SignalPortComplete(this);
82 // Fake implementation to mock away real network usage by responding
83 // to http requests immediately.
84 class FakeHttpPortAllocatorSession : public TestHttpPortAllocatorSession {
86 FakeHttpPortAllocatorSession(
87 HttpPortAllocator* allocator,
88 const std::string& content_name,
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,
105 virtual void SendSessionRequest(const std::string& host, int port) {
106 FakeReceiveSessionResponse(host, port);
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);
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);
138 // Fake implementation for creating fake http sessions.
139 class FakeHttpPortAllocator : public HttpPortAllocator {
141 FakeHttpPortAllocator(talk_base::NetworkManager* network_manager,
142 const std::string& user_agent)
143 : HttpPortAllocator(network_manager, user_agent) {
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,
165 class ConnectivityCheckerForTest : public ConnectivityChecker {
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,
179 proxy_initiated_(false) {
182 talk_base::FakeNetworkManager* network_manager() const {
183 return network_manager_;
186 FakeHttpPortAllocator* port_allocator() const {
187 return fake_port_allocator_;
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_;
196 virtual talk_base::BasicPacketSocketFactory* CreateSocketFactory(
197 talk_base::Thread* thread) {
198 // Create socket factory, for simplicity, let it run on the current thread.
200 new talk_base::BasicPacketSocketFactory(talk_base::Thread::Current());
201 return socket_factory_;
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_;
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(),
218 config->stun_address);
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(),
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_);
237 virtual talk_base::ProxyInfo GetProxyInfo() const {
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_;
249 class ConnectivityCheckerTest : public testing::Test {
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);
256 // Verify that the stun server address has been set.
257 EXPECT_EQ(kStunAddr, info.stun_server_address);
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());
263 // Verify that local ip matches.
264 EXPECT_EQ(local_address.ipaddr(), info.ip);
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);
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);
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.
284 TEST_F(ConnectivityCheckerTest, TestStart) {
285 ConnectivityCheckerForTest connectivity_checker(talk_base::Thread::Current(),
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);
296 connectivity_checker.Start();
297 talk_base::Thread::Current()->ProcessMessages(1000);
299 NicMap nics = connectivity_checker.GetResults();
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());
305 // First verify interfaces without proxy.
306 talk_base::SocketAddress nilAddress;
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);
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());
318 VerifyNic(info, kClientAddr2);
320 // Now verify both interfaces with proxy.
321 i = nics.find(NicId(kClientAddr1.ipaddr(), kProxyAddr));
322 ASSERT(i != nics.end());
324 VerifyNic(info, kClientAddr1);
326 i = nics.find(NicId(kClientAddr2.ipaddr(), kProxyAddr));
327 ASSERT(i != nics.end());
329 VerifyNic(info, kClientAddr2);
332 // Tests that nothing bad happens if thera are no network interfaces
333 // available to check.
334 TEST_F(ConnectivityCheckerTest, TestStartNoNetwork) {
335 ConnectivityCheckerForTest connectivity_checker(talk_base::Thread::Current(),
341 connectivity_checker.Initialize();
342 connectivity_checker.Start();
343 talk_base::Thread::Current()->ProcessMessages(1000);
345 NicMap nics = connectivity_checker.GetResults();
347 // Verify that no nics where checked.
348 EXPECT_EQ(0U, nics.size());
351 } // namespace cricket