Support multiple URLs in PeerConnectionInterface::IceServer

This adds support for multiple URLs in a IceServer configuration as
defined in http://w3c.github.io/webrtc-pc/#idl-def-RTCIceServer.

BUG=2096
R=pthatcher@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/57489004

Cr-Commit-Position: refs/heads/master@{#9320}
This commit is contained in:
Joachim Bauch
2015-05-28 23:06:30 +02:00
parent 45b229cc89
commit 7c4e7458b5
3 changed files with 138 additions and 88 deletions

View File

@ -166,9 +166,10 @@ typedef webrtc::PortAllocatorFactoryInterface::StunConfiguration
typedef webrtc::PortAllocatorFactoryInterface::TurnConfiguration typedef webrtc::PortAllocatorFactoryInterface::TurnConfiguration
TurnConfiguration; TurnConfiguration;
bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration, bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server,
std::vector<StunConfiguration>* stun_config, const std::string& url,
std::vector<TurnConfiguration>* turn_config) { std::vector<StunConfiguration>* stun_config,
std::vector<TurnConfiguration>* turn_config) {
// draft-nandakumar-rtcweb-stun-uri-01 // draft-nandakumar-rtcweb-stun-uri-01
// stunURI = scheme ":" stun-host [ ":" stun-port ] // stunURI = scheme ":" stun-host [ ":" stun-port ]
// scheme = "stun" / "stuns" // scheme = "stun" / "stuns"
@ -183,103 +184,119 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
// transport-ext = 1*unreserved // transport-ext = 1*unreserved
// turn-host = IP-literal / IPv4address / reg-name // turn-host = IP-literal / IPv4address / reg-name
// turn-port = *DIGIT // turn-port = *DIGIT
for (size_t i = 0; i < configuration.size(); ++i) { std::vector<std::string> tokens;
webrtc::PeerConnectionInterface::IceServer server = configuration[i]; std::string turn_transport_type = kUdpTransportType;
if (server.uri.empty()) { rtc::tokenize(url, '?', &tokens);
LOG(WARNING) << "Empty uri."; std::string uri_without_transport = tokens[0];
continue; // Let's look into transport= param, if it exists.
} if (tokens.size() == kTurnTransportTokensNum) { // ?transport= is present.
std::vector<std::string> tokens; std::string uri_transport_param = tokens[1];
std::string turn_transport_type = kUdpTransportType; rtc::tokenize(uri_transport_param, '=', &tokens);
rtc::tokenize(server.uri, '?', &tokens); if (tokens[0] == kTransport) {
std::string uri_without_transport = tokens[0]; // As per above grammar transport param will be consist of lower case
// Let's look into transport= param, if it exists. // letters.
if (tokens.size() == kTurnTransportTokensNum) { // ?transport= is present. if (tokens[1] != kUdpTransportType && tokens[1] != kTcpTransportType) {
std::string uri_transport_param = tokens[1]; LOG(LS_WARNING) << "Transport param should always be udp or tcp.";
rtc::tokenize(uri_transport_param, '=', &tokens); return true;
if (tokens[0] == kTransport) {
// As per above grammar transport param will be consist of lower case
// letters.
if (tokens[1] != kUdpTransportType && tokens[1] != kTcpTransportType) {
LOG(LS_WARNING) << "Transport param should always be udp or tcp.";
continue;
}
turn_transport_type = tokens[1];
} }
turn_transport_type = tokens[1];
} }
}
std::string hoststring; std::string hoststring;
ServiceType service_type = INVALID; ServiceType service_type = INVALID;
if (!GetServiceTypeAndHostnameFromUri(uri_without_transport, if (!GetServiceTypeAndHostnameFromUri(uri_without_transport,
&service_type, &service_type,
&hoststring)) { &hoststring)) {
LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: "
<< uri_without_transport; << uri_without_transport;
continue; return true;
} }
ASSERT(!hoststring.empty()); ASSERT(!hoststring.empty());
// Let's break hostname. // Let's break hostname.
tokens.clear(); tokens.clear();
rtc::tokenize(hoststring, '@', &tokens); rtc::tokenize(hoststring, '@', &tokens);
ASSERT(!tokens.empty()); ASSERT(!tokens.empty());
// TODO(pthatcher): What's the right thing to do if tokens.size() is >2? std::string username(server.username);
// E.g. a string like "foo@bar@bat". // TODO(pthatcher): What's the right thing to do if tokens.size() is >2?
if (tokens.size() >= kTurnHostTokensNum) { // E.g. a string like "foo@bar@bat".
server.username = rtc::s_url_decode(tokens[0]); if (tokens.size() >= kTurnHostTokensNum) {
hoststring = tokens[1]; username.assign(rtc::s_url_decode(tokens[0]));
} else { hoststring = tokens[1];
hoststring = tokens[0]; } else {
} hoststring = tokens[0];
}
int port = kDefaultStunPort; int port = kDefaultStunPort;
if (service_type == TURNS) { if (service_type == TURNS) {
port = kDefaultStunTlsPort; port = kDefaultStunTlsPort;
turn_transport_type = kTcpTransportType; turn_transport_type = kTcpTransportType;
} }
std::string address; std::string address;
if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) { if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) {
LOG(WARNING) << "Invalid Hostname format: " << uri_without_transport; LOG(WARNING) << "Invalid Hostname format: " << uri_without_transport;
continue; return true;
} }
if (port <= 0 || port > 0xffff) { if (port <= 0 || port > 0xffff) {
LOG(WARNING) << "Invalid port: " << port; LOG(WARNING) << "Invalid port: " << port;
continue; return true;
} }
switch (service_type) { switch (service_type) {
case STUN: case STUN:
case STUNS: case STUNS:
stun_config->push_back(StunConfiguration(address, port)); stun_config->push_back(StunConfiguration(address, port));
break; break;
case TURN: case TURN:
case TURNS: { case TURNS: {
if (server.username.empty()) { if (username.empty()) {
// Turn url example from the spec |url:"turn:user@turn.example.org"|. // Turn url example from the spec |url:"turn:user@turn.example.org"|.
std::vector<std::string> turn_tokens; std::vector<std::string> turn_tokens;
rtc::tokenize(address, '@', &turn_tokens); rtc::tokenize(address, '@', &turn_tokens);
if (turn_tokens.size() == kTurnHostTokensNum) { if (turn_tokens.size() == kTurnHostTokensNum) {
server.username = rtc::s_url_decode(turn_tokens[0]); username.assign(rtc::s_url_decode(turn_tokens[0]));
address = turn_tokens[1]; address = turn_tokens[1];
}
} }
bool secure = (service_type == TURNS);
turn_config->push_back(TurnConfiguration(address, port,
server.username,
server.password,
turn_transport_type,
secure));
break;
} }
case INVALID:
default: bool secure = (service_type == TURNS);
LOG(WARNING) << "Configuration not supported: " << server.uri;
turn_config->push_back(TurnConfiguration(address, port,
username,
server.password,
turn_transport_type,
secure));
break;
}
case INVALID:
default:
LOG(WARNING) << "Configuration not supported: " << url;
return false;
}
return true;
}
bool ParseIceServers(const PeerConnectionInterface::IceServers& servers,
std::vector<StunConfiguration>* stun_config,
std::vector<TurnConfiguration>* turn_config) {
for (const webrtc::PeerConnectionInterface::IceServer& server : servers) {
if (!server.urls.empty()) {
for (const std::string& url : server.urls) {
if (!ParseIceServerUrl(server, url, stun_config, turn_config)) {
return false;
}
}
} else if (!server.uri.empty()) {
// Fallback to old .uri if new .urls isn't present.
if (!ParseIceServerUrl(server, server.uri, stun_config, turn_config)) {
return false; return false;
}
} else {
LOG(WARNING) << "Empty uri.";
} }
} }
return true; return true;

View File

@ -198,6 +198,37 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) {
VerifyTurnConfigurations(turn_configs); VerifyTurnConfigurations(turn_configs);
} }
// This test verifies creation of PeerConnection with valid STUN and TURN
// configuration. Also verifies the list of URL's parsed correctly as expected.
TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersUrls) {
PeerConnectionInterface::RTCConfiguration config;
webrtc::PeerConnectionInterface::IceServer ice_server;
ice_server.urls.push_back(kStunIceServer);
ice_server.urls.push_back(kTurnIceServer);
ice_server.urls.push_back(kTurnIceServerWithTransport);
ice_server.password = kTurnPassword;
config.servers.push_back(ice_server);
rtc::scoped_refptr<PeerConnectionInterface> pc(
factory_->CreatePeerConnection(config, NULL,
allocator_factory_.get(),
new FakeIdentityService(),
&observer_));
EXPECT_TRUE(pc.get() != NULL);
StunConfigurations stun_configs;
webrtc::PortAllocatorFactoryInterface::StunConfiguration stun1(
"stun.l.google.com", 19302);
stun_configs.push_back(stun1);
VerifyStunConfigurations(stun_configs);
TurnConfigurations turn_configs;
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1(
"test.com", 1234, "test@hello.com", kTurnPassword, "udp", false);
turn_configs.push_back(turn1);
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2(
"hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false);
turn_configs.push_back(turn2);
VerifyTurnConfigurations(turn_configs);
}
// This test verifies creation of PeerConnection with valid STUN and TURN // This test verifies creation of PeerConnection with valid STUN and TURN
// configuration. Also verifies the URL's parsed correctly as expected. // configuration. Also verifies the URL's parsed correctly as expected.
// This version doesn't use RTCConfiguration. // This version doesn't use RTCConfiguration.

View File

@ -175,7 +175,9 @@ class PeerConnectionInterface : public rtc::RefCountInterface {
}; };
struct IceServer { struct IceServer {
// TODO(jbauch): Remove uri when all code using it has switched to urls.
std::string uri; std::string uri;
std::vector<std::string> urls;
std::string username; std::string username;
std::string password; std::string password;
}; };