diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc index 52260413f7..aba2d62b4a 100644 --- a/talk/app/webrtc/peerconnection.cc +++ b/talk/app/webrtc/peerconnection.cc @@ -166,9 +166,10 @@ typedef webrtc::PortAllocatorFactoryInterface::StunConfiguration typedef webrtc::PortAllocatorFactoryInterface::TurnConfiguration TurnConfiguration; -bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration, - std::vector* stun_config, - std::vector* turn_config) { +bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, + const std::string& url, + std::vector* stun_config, + std::vector* turn_config) { // draft-nandakumar-rtcweb-stun-uri-01 // stunURI = scheme ":" stun-host [ ":" stun-port ] // scheme = "stun" / "stuns" @@ -183,103 +184,119 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration, // transport-ext = 1*unreserved // turn-host = IP-literal / IPv4address / reg-name // turn-port = *DIGIT - for (size_t i = 0; i < configuration.size(); ++i) { - webrtc::PeerConnectionInterface::IceServer server = configuration[i]; - if (server.uri.empty()) { - LOG(WARNING) << "Empty uri."; - continue; - } - std::vector tokens; - std::string turn_transport_type = kUdpTransportType; - rtc::tokenize(server.uri, '?', &tokens); - std::string uri_without_transport = tokens[0]; - // Let's look into transport= param, if it exists. - if (tokens.size() == kTurnTransportTokensNum) { // ?transport= is present. - std::string uri_transport_param = tokens[1]; - rtc::tokenize(uri_transport_param, '=', &tokens); - 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]; + std::vector tokens; + std::string turn_transport_type = kUdpTransportType; + rtc::tokenize(url, '?', &tokens); + std::string uri_without_transport = tokens[0]; + // Let's look into transport= param, if it exists. + if (tokens.size() == kTurnTransportTokensNum) { // ?transport= is present. + std::string uri_transport_param = tokens[1]; + rtc::tokenize(uri_transport_param, '=', &tokens); + 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."; + return true; } + turn_transport_type = tokens[1]; } + } - std::string hoststring; - ServiceType service_type = INVALID; - if (!GetServiceTypeAndHostnameFromUri(uri_without_transport, - &service_type, - &hoststring)) { - LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " - << uri_without_transport; - continue; - } + std::string hoststring; + ServiceType service_type = INVALID; + if (!GetServiceTypeAndHostnameFromUri(uri_without_transport, + &service_type, + &hoststring)) { + LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " + << uri_without_transport; + return true; + } - ASSERT(!hoststring.empty()); + ASSERT(!hoststring.empty()); - // Let's break hostname. - tokens.clear(); - rtc::tokenize(hoststring, '@', &tokens); - ASSERT(!tokens.empty()); - // TODO(pthatcher): What's the right thing to do if tokens.size() is >2? - // E.g. a string like "foo@bar@bat". - if (tokens.size() >= kTurnHostTokensNum) { - server.username = rtc::s_url_decode(tokens[0]); - hoststring = tokens[1]; - } else { - hoststring = tokens[0]; - } + // Let's break hostname. + tokens.clear(); + rtc::tokenize(hoststring, '@', &tokens); + ASSERT(!tokens.empty()); + std::string username(server.username); + // TODO(pthatcher): What's the right thing to do if tokens.size() is >2? + // E.g. a string like "foo@bar@bat". + if (tokens.size() >= kTurnHostTokensNum) { + username.assign(rtc::s_url_decode(tokens[0])); + hoststring = tokens[1]; + } else { + hoststring = tokens[0]; + } - int port = kDefaultStunPort; - if (service_type == TURNS) { - port = kDefaultStunTlsPort; - turn_transport_type = kTcpTransportType; - } + int port = kDefaultStunPort; + if (service_type == TURNS) { + port = kDefaultStunTlsPort; + turn_transport_type = kTcpTransportType; + } - std::string address; - if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) { - LOG(WARNING) << "Invalid Hostname format: " << uri_without_transport; - continue; - } + std::string address; + if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) { + LOG(WARNING) << "Invalid Hostname format: " << uri_without_transport; + return true; + } - if (port <= 0 || port > 0xffff) { - LOG(WARNING) << "Invalid port: " << port; - continue; - } + if (port <= 0 || port > 0xffff) { + LOG(WARNING) << "Invalid port: " << port; + return true; + } - switch (service_type) { - case STUN: - case STUNS: - stun_config->push_back(StunConfiguration(address, port)); - break; - case TURN: - case TURNS: { - if (server.username.empty()) { - // Turn url example from the spec |url:"turn:user@turn.example.org"|. - std::vector turn_tokens; - rtc::tokenize(address, '@', &turn_tokens); - if (turn_tokens.size() == kTurnHostTokensNum) { - server.username = rtc::s_url_decode(turn_tokens[0]); - address = turn_tokens[1]; - } + switch (service_type) { + case STUN: + case STUNS: + stun_config->push_back(StunConfiguration(address, port)); + break; + case TURN: + case TURNS: { + if (username.empty()) { + // Turn url example from the spec |url:"turn:user@turn.example.org"|. + std::vector turn_tokens; + rtc::tokenize(address, '@', &turn_tokens); + if (turn_tokens.size() == kTurnHostTokensNum) { + username.assign(rtc::s_url_decode(turn_tokens[0])); + 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: - LOG(WARNING) << "Configuration not supported: " << server.uri; + + bool secure = (service_type == TURNS); + + 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* stun_config, + std::vector* 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; + } + } else { + LOG(WARNING) << "Empty uri."; } } return true; diff --git a/talk/app/webrtc/peerconnectionfactory_unittest.cc b/talk/app/webrtc/peerconnectionfactory_unittest.cc index 67a20331da..597a1740ca 100644 --- a/talk/app/webrtc/peerconnectionfactory_unittest.cc +++ b/talk/app/webrtc/peerconnectionfactory_unittest.cc @@ -198,6 +198,37 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) { 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 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 // configuration. Also verifies the URL's parsed correctly as expected. // This version doesn't use RTCConfiguration. diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h index 960e286f73..329137f973 100644 --- a/talk/app/webrtc/peerconnectioninterface.h +++ b/talk/app/webrtc/peerconnectioninterface.h @@ -175,7 +175,9 @@ class PeerConnectionInterface : public rtc::RefCountInterface { }; struct IceServer { + // TODO(jbauch): Remove uri when all code using it has switched to urls. std::string uri; + std::vector urls; std::string username; std::string password; };