Home | History | Annotate | Download | only in client
      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