dcc1f0426b
git-svn-id: http://webrtc.googlecode.com/svn/trunk@7231 4adac7df-926f-26a2-2b94-8c16560cd09d
393 lines
15 KiB
C++
393 lines
15 KiB
C++
/*
|
|
* libjingle
|
|
* Copyright 2011, Google Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <string>
|
|
|
|
#include "talk/p2p/base/basicpacketsocketfactory.h"
|
|
#include "talk/p2p/base/relayport.h"
|
|
#include "talk/p2p/base/stunport.h"
|
|
#include "talk/p2p/client/connectivitychecker.h"
|
|
#include "talk/p2p/client/httpportallocator.h"
|
|
#include "webrtc/base/asynchttprequest.h"
|
|
#include "webrtc/base/fakenetwork.h"
|
|
#include "webrtc/base/gunit.h"
|
|
#include "webrtc/base/scoped_ptr.h"
|
|
#include "webrtc/base/socketaddress.h"
|
|
|
|
namespace cricket {
|
|
|
|
static const rtc::SocketAddress kClientAddr1("11.11.11.11", 0);
|
|
static const rtc::SocketAddress kClientAddr2("22.22.22.22", 0);
|
|
static const rtc::SocketAddress kExternalAddr("33.33.33.33", 3333);
|
|
static const rtc::SocketAddress kStunAddr("44.44.44.44", 4444);
|
|
static const rtc::SocketAddress kRelayAddr("55.55.55.55", 5555);
|
|
static const rtc::SocketAddress kProxyAddr("66.66.66.66", 6666);
|
|
static const rtc::ProxyType kProxyType = rtc::PROXY_HTTPS;
|
|
static const char kRelayHost[] = "relay.google.com";
|
|
static const char kRelayToken[] =
|
|
"CAESFwoOb2phQGdvb2dsZS5jb20Q043h47MmGhBTB1rbfIXkhuarDCZe+xF6";
|
|
static const char kBrowserAgent[] = "browser_test";
|
|
static const char kJid[] = "a.b@c";
|
|
static const char kUserName[] = "testuser";
|
|
static const char kPassword[] = "testpassword";
|
|
static const char kMagicCookie[] = "testcookie";
|
|
static const char kRelayUdpPort[] = "4444";
|
|
static const char kRelayTcpPort[] = "5555";
|
|
static const char kRelaySsltcpPort[] = "6666";
|
|
static const char kSessionId[] = "testsession";
|
|
static const char kConnection[] = "testconnection";
|
|
static const int kMinPort = 1000;
|
|
static const int kMaxPort = 2000;
|
|
|
|
// Fake implementation to mock away real network usage.
|
|
class FakeRelayPort : public RelayPort {
|
|
public:
|
|
FakeRelayPort(rtc::Thread* thread,
|
|
rtc::PacketSocketFactory* factory,
|
|
rtc::Network* network, const rtc::IPAddress& ip,
|
|
int min_port, int max_port,
|
|
const std::string& username, const std::string& password)
|
|
: RelayPort(thread, factory, network, ip, min_port, max_port,
|
|
username, password) {
|
|
}
|
|
|
|
// Just signal that we are done.
|
|
virtual void PrepareAddress() {
|
|
SignalPortComplete(this);
|
|
}
|
|
};
|
|
|
|
// Fake implementation to mock away real network usage.
|
|
class FakeStunPort : public StunPort {
|
|
public:
|
|
FakeStunPort(rtc::Thread* thread,
|
|
rtc::PacketSocketFactory* factory,
|
|
rtc::Network* network,
|
|
const rtc::IPAddress& ip,
|
|
int min_port, int max_port,
|
|
const std::string& username, const std::string& password,
|
|
const ServerAddresses& server_addr)
|
|
: StunPort(thread, factory, network, ip, min_port, max_port,
|
|
username, password, server_addr) {
|
|
}
|
|
|
|
// Just set external address and signal that we are done.
|
|
virtual void PrepareAddress() {
|
|
AddAddress(kExternalAddr, kExternalAddr, rtc::SocketAddress(), "udp", "",
|
|
STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, 0, true);
|
|
SignalPortComplete(this);
|
|
}
|
|
};
|
|
|
|
// Fake implementation to mock away real network usage by responding
|
|
// to http requests immediately.
|
|
class FakeHttpPortAllocatorSession : public TestHttpPortAllocatorSession {
|
|
public:
|
|
FakeHttpPortAllocatorSession(
|
|
HttpPortAllocator* allocator,
|
|
const std::string& content_name,
|
|
int component,
|
|
const std::string& ice_ufrag, const std::string& ice_pwd,
|
|
const std::vector<rtc::SocketAddress>& stun_hosts,
|
|
const std::vector<std::string>& relay_hosts,
|
|
const std::string& relay_token,
|
|
const std::string& agent)
|
|
: TestHttpPortAllocatorSession(allocator,
|
|
content_name,
|
|
component,
|
|
ice_ufrag,
|
|
ice_pwd,
|
|
stun_hosts,
|
|
relay_hosts,
|
|
relay_token,
|
|
agent) {
|
|
}
|
|
virtual void SendSessionRequest(const std::string& host, int port) {
|
|
FakeReceiveSessionResponse(host, port);
|
|
}
|
|
|
|
// Pass results to the real implementation.
|
|
void FakeReceiveSessionResponse(const std::string& host, int port) {
|
|
rtc::AsyncHttpRequest* response = CreateAsyncHttpResponse(port);
|
|
TestHttpPortAllocatorSession::OnRequestDone(response);
|
|
response->Destroy(true);
|
|
}
|
|
|
|
private:
|
|
// Helper method for creating a response to a relay session request.
|
|
rtc::AsyncHttpRequest* CreateAsyncHttpResponse(int port) {
|
|
rtc::AsyncHttpRequest* request =
|
|
new rtc::AsyncHttpRequest(kBrowserAgent);
|
|
std::stringstream ss;
|
|
ss << "username=" << kUserName << std::endl
|
|
<< "password=" << kPassword << std::endl
|
|
<< "magic_cookie=" << kMagicCookie << std::endl
|
|
<< "relay.ip=" << kRelayAddr.ipaddr().ToString() << std::endl
|
|
<< "relay.udp_port=" << kRelayUdpPort << std::endl
|
|
<< "relay.tcp_port=" << kRelayTcpPort << std::endl
|
|
<< "relay.ssltcp_port=" << kRelaySsltcpPort << std::endl;
|
|
request->response().document.reset(
|
|
new rtc::MemoryStream(ss.str().c_str()));
|
|
request->response().set_success();
|
|
request->set_port(port);
|
|
request->set_secure(port == rtc::HTTP_SECURE_PORT);
|
|
return request;
|
|
}
|
|
};
|
|
|
|
// Fake implementation for creating fake http sessions.
|
|
class FakeHttpPortAllocator : public HttpPortAllocator {
|
|
public:
|
|
FakeHttpPortAllocator(rtc::NetworkManager* network_manager,
|
|
const std::string& user_agent)
|
|
: HttpPortAllocator(network_manager, user_agent) {
|
|
}
|
|
|
|
virtual PortAllocatorSession* CreateSessionInternal(
|
|
const std::string& content_name, int component,
|
|
const std::string& ice_ufrag, const std::string& ice_pwd) {
|
|
std::vector<rtc::SocketAddress> stun_hosts;
|
|
stun_hosts.push_back(kStunAddr);
|
|
std::vector<std::string> relay_hosts;
|
|
relay_hosts.push_back(kRelayHost);
|
|
return new FakeHttpPortAllocatorSession(this,
|
|
content_name,
|
|
component,
|
|
ice_ufrag,
|
|
ice_pwd,
|
|
stun_hosts,
|
|
relay_hosts,
|
|
kRelayToken,
|
|
kBrowserAgent);
|
|
}
|
|
};
|
|
|
|
class ConnectivityCheckerForTest : public ConnectivityChecker {
|
|
public:
|
|
ConnectivityCheckerForTest(rtc::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)
|
|
: ConnectivityChecker(worker,
|
|
jid,
|
|
session_id,
|
|
user_agent,
|
|
relay_token,
|
|
connection),
|
|
proxy_initiated_(false) {
|
|
}
|
|
|
|
rtc::FakeNetworkManager* network_manager() const {
|
|
return network_manager_;
|
|
}
|
|
|
|
FakeHttpPortAllocator* port_allocator() const {
|
|
return fake_port_allocator_;
|
|
}
|
|
|
|
protected:
|
|
// Overridden methods for faking a real network.
|
|
virtual rtc::NetworkManager* CreateNetworkManager() {
|
|
network_manager_ = new rtc::FakeNetworkManager();
|
|
return network_manager_;
|
|
}
|
|
virtual rtc::BasicPacketSocketFactory* CreateSocketFactory(
|
|
rtc::Thread* thread) {
|
|
// Create socket factory, for simplicity, let it run on the current thread.
|
|
socket_factory_ =
|
|
new rtc::BasicPacketSocketFactory(rtc::Thread::Current());
|
|
return socket_factory_;
|
|
}
|
|
virtual HttpPortAllocator* CreatePortAllocator(
|
|
rtc::NetworkManager* network_manager,
|
|
const std::string& user_agent,
|
|
const std::string& relay_token) {
|
|
fake_port_allocator_ =
|
|
new FakeHttpPortAllocator(network_manager, user_agent);
|
|
return fake_port_allocator_;
|
|
}
|
|
virtual StunPort* CreateStunPort(
|
|
const std::string& username, const std::string& password,
|
|
const PortConfiguration* config, rtc::Network* network) {
|
|
return new FakeStunPort(worker(),
|
|
socket_factory_,
|
|
network,
|
|
#ifdef USE_WEBRTC_DEV_BRANCH
|
|
network->GetBestIP(),
|
|
#else // USE_WEBRTC_DEV_BRANCH
|
|
network->ip(),
|
|
#endif // USE_WEBRTC_DEV_BRANCH
|
|
kMinPort,
|
|
kMaxPort,
|
|
username,
|
|
password,
|
|
config->stun_servers);
|
|
}
|
|
virtual RelayPort* CreateRelayPort(
|
|
const std::string& username, const std::string& password,
|
|
const PortConfiguration* config, rtc::Network* network) {
|
|
return new FakeRelayPort(worker(),
|
|
socket_factory_,
|
|
network,
|
|
#ifdef USE_WEBRTC_DEV_BRANCH
|
|
network->GetBestIP(),
|
|
#else // USE_WEBRTC_DEV_BRANCH
|
|
network->ip(),
|
|
#endif // USE_WEBRTC_DEV_BRANCH
|
|
kMinPort,
|
|
kMaxPort,
|
|
username,
|
|
password);
|
|
}
|
|
virtual void InitiateProxyDetection() {
|
|
if (!proxy_initiated_) {
|
|
proxy_initiated_ = true;
|
|
proxy_info_.address = kProxyAddr;
|
|
proxy_info_.type = kProxyType;
|
|
SetProxyInfo(proxy_info_);
|
|
}
|
|
}
|
|
|
|
virtual rtc::ProxyInfo GetProxyInfo() const {
|
|
return proxy_info_;
|
|
}
|
|
|
|
private:
|
|
rtc::BasicPacketSocketFactory* socket_factory_;
|
|
FakeHttpPortAllocator* fake_port_allocator_;
|
|
rtc::FakeNetworkManager* network_manager_;
|
|
rtc::ProxyInfo proxy_info_;
|
|
bool proxy_initiated_;
|
|
};
|
|
|
|
class ConnectivityCheckerTest : public testing::Test {
|
|
protected:
|
|
void VerifyNic(const NicInfo& info,
|
|
const rtc::SocketAddress& local_address) {
|
|
// Verify that the external address has been set.
|
|
EXPECT_EQ(kExternalAddr, info.external_address);
|
|
|
|
// Verify that the stun server address has been set.
|
|
EXPECT_EQ(1U, info.stun_server_addresses.size());
|
|
EXPECT_EQ(kStunAddr, *(info.stun_server_addresses.begin()));
|
|
|
|
// Verify that the media server address has been set. Don't care
|
|
// about port since it is different for different protocols.
|
|
EXPECT_EQ(kRelayAddr.ipaddr(), info.media_server_address.ipaddr());
|
|
|
|
// Verify that local ip matches.
|
|
EXPECT_EQ(local_address.ipaddr(), info.ip);
|
|
|
|
// Verify that we have received responses for our
|
|
// pings. Unsuccessful ping has rtt value -1, successful >= 0.
|
|
EXPECT_GE(info.stun.rtt, 0);
|
|
EXPECT_GE(info.udp.rtt, 0);
|
|
EXPECT_GE(info.tcp.rtt, 0);
|
|
EXPECT_GE(info.ssltcp.rtt, 0);
|
|
|
|
// If proxy has been set, verify address and type.
|
|
if (!info.proxy_info.address.IsNil()) {
|
|
EXPECT_EQ(kProxyAddr, info.proxy_info.address);
|
|
EXPECT_EQ(kProxyType, info.proxy_info.type);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Tests a configuration with two network interfaces. Verifies that 4
|
|
// combinations of ip/proxy are created and that all protocols are
|
|
// tested on each combination.
|
|
TEST_F(ConnectivityCheckerTest, TestStart) {
|
|
ConnectivityCheckerForTest connectivity_checker(rtc::Thread::Current(),
|
|
kJid,
|
|
kSessionId,
|
|
kBrowserAgent,
|
|
kRelayToken,
|
|
kConnection);
|
|
connectivity_checker.Initialize();
|
|
connectivity_checker.set_stun_address(kStunAddr);
|
|
connectivity_checker.network_manager()->AddInterface(kClientAddr1);
|
|
connectivity_checker.network_manager()->AddInterface(kClientAddr2);
|
|
|
|
connectivity_checker.Start();
|
|
rtc::Thread::Current()->ProcessMessages(1000);
|
|
|
|
NicMap nics = connectivity_checker.GetResults();
|
|
|
|
// There should be 4 nics in our map. 2 for each interface added,
|
|
// one with proxy set and one without.
|
|
EXPECT_EQ(4U, nics.size());
|
|
|
|
// First verify interfaces without proxy.
|
|
rtc::SocketAddress nilAddress;
|
|
|
|
// First lookup the address of the first nic combined with no proxy.
|
|
NicMap::iterator i = nics.find(NicId(kClientAddr1.ipaddr(), nilAddress));
|
|
ASSERT(i != nics.end());
|
|
NicInfo info = i->second;
|
|
VerifyNic(info, kClientAddr1);
|
|
|
|
// Then make sure the second device has been tested without proxy.
|
|
i = nics.find(NicId(kClientAddr2.ipaddr(), nilAddress));
|
|
ASSERT(i != nics.end());
|
|
info = i->second;
|
|
VerifyNic(info, kClientAddr2);
|
|
|
|
// Now verify both interfaces with proxy.
|
|
i = nics.find(NicId(kClientAddr1.ipaddr(), kProxyAddr));
|
|
ASSERT(i != nics.end());
|
|
info = i->second;
|
|
VerifyNic(info, kClientAddr1);
|
|
|
|
i = nics.find(NicId(kClientAddr2.ipaddr(), kProxyAddr));
|
|
ASSERT(i != nics.end());
|
|
info = i->second;
|
|
VerifyNic(info, kClientAddr2);
|
|
};
|
|
|
|
// Tests that nothing bad happens if thera are no network interfaces
|
|
// available to check.
|
|
TEST_F(ConnectivityCheckerTest, TestStartNoNetwork) {
|
|
ConnectivityCheckerForTest connectivity_checker(rtc::Thread::Current(),
|
|
kJid,
|
|
kSessionId,
|
|
kBrowserAgent,
|
|
kRelayToken,
|
|
kConnection);
|
|
connectivity_checker.Initialize();
|
|
connectivity_checker.Start();
|
|
rtc::Thread::Current()->ProcessMessages(1000);
|
|
|
|
NicMap nics = connectivity_checker.GetResults();
|
|
|
|
// Verify that no nics where checked.
|
|
EXPECT_EQ(0U, nics.size());
|
|
}
|
|
|
|
} // namespace cricket
|