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:
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user