Rewrite PeerConnection integration tests using better testing practices.
Also renames "peerconnection_unittests" to "peerconnection_integrationtests", and moves the ICE URL parsing code to separate files. The main problem previously was that the test assertions occurred in various places in the main test class, and this shared test code was overly complex and stateful. As a result, it was difficult to tell what a test even does, let alone what assertions it's meant to be making. And writing a new test that does what you want can be a frustrating ordeal. The new code still uses helper methods, but they have intuitive names and a smaller role; all of the important parts of the test's logic are in the test case itself. We're planning on merging PeerConnection and WebRtcSession at some point soon, so it seemed valuable to do this, so that the WebRtcSession tests can be rewritten as PeerConnection tests using better patterns. BUG=None Review-Url: https://codereview.webrtc.org/2738353003 Cr-Commit-Position: refs/heads/master@{#17458}
This commit is contained in:
parent
7735b1e932
commit
1dcb16409a
@ -1,7 +1,7 @@
|
||||
# Tests that are failing when run under memcheck.
|
||||
# https://code.google.com/p/webrtc/issues/detail?id=4387
|
||||
DtmfSenderTest.*
|
||||
P2PTestConductor.*
|
||||
PeerConnectionIntegrationTest.*
|
||||
PeerConnectionEndToEndTest.*
|
||||
PeerConnectionInterfaceTest.*
|
||||
RTCStatsIntegrationTest.*
|
||||
|
||||
@ -648,12 +648,14 @@ class PeerConnectionInterface : public rtc::RefCountInterface {
|
||||
const MediaConstraintsInterface* constraints) {}
|
||||
|
||||
// Sets the local session description.
|
||||
// JsepInterface takes the ownership of |desc| even if it fails.
|
||||
// The PeerConnection takes the ownership of |desc| even if it fails.
|
||||
// The |observer| callback will be called when done.
|
||||
// TODO(deadbeef): Change |desc| to be a unique_ptr, to make it clear
|
||||
// that this method always takes ownership of it.
|
||||
virtual void SetLocalDescription(SetSessionDescriptionObserver* observer,
|
||||
SessionDescriptionInterface* desc) = 0;
|
||||
// Sets the remote session description.
|
||||
// JsepInterface takes the ownership of |desc| even if it fails.
|
||||
// The PeerConnection takes the ownership of |desc| even if it fails.
|
||||
// The |observer| callback will be called when done.
|
||||
virtual void SetRemoteDescription(SetSessionDescriptionObserver* observer,
|
||||
SessionDescriptionInterface* desc) = 0;
|
||||
|
||||
@ -35,7 +35,7 @@ class FakeVideoRenderer : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
|
||||
// tolerance on Y values. Some unit tests produce Y values close
|
||||
// to 16 rather than close to zero, for supposedly black frames.
|
||||
// Largest value observed is 34, e.g., running
|
||||
// P2PTestConductor.LocalP2PTest16To9 (peerconnection_unittests).
|
||||
// PeerConnectionIntegrationTest.SendAndReceive16To9AspectRatio.
|
||||
black_frame_ = CheckFrameColorYuv(0, 48, 128, 128, 128, 128, &frame);
|
||||
// Treat unexpected frame size as error.
|
||||
++num_rendered_frames_;
|
||||
|
||||
@ -88,6 +88,8 @@ rtc_static_library("libjingle_peerconnection") {
|
||||
"datachannel.h",
|
||||
"dtmfsender.cc",
|
||||
"dtmfsender.h",
|
||||
"iceserverparsing.cc",
|
||||
"iceserverparsing.h",
|
||||
"jsepicecandidate.cc",
|
||||
"jsepsessiondescription.cc",
|
||||
"localaudiosource.cc",
|
||||
@ -278,11 +280,12 @@ if (rtc_include_tests) {
|
||||
"datachannel_unittest.cc",
|
||||
"dtmfsender_unittest.cc",
|
||||
"fakemediacontroller.h",
|
||||
"iceserverparsing_unittest.cc",
|
||||
"jsepsessiondescription_unittest.cc",
|
||||
"localaudiosource_unittest.cc",
|
||||
"mediaconstraintsinterface_unittest.cc",
|
||||
"mediastream_unittest.cc",
|
||||
"peerconnection_unittest.cc",
|
||||
"peerconnection_integrationtest.cc",
|
||||
"peerconnectionendtoend_unittest.cc",
|
||||
"peerconnectionfactory_unittest.cc",
|
||||
"peerconnectioninterface_unittest.cc",
|
||||
|
||||
294
webrtc/pc/iceserverparsing.cc
Normal file
294
webrtc/pc/iceserverparsing.cc
Normal file
@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/pc/iceserverparsing.h"
|
||||
|
||||
#include <cctype> // For std::isdigit.
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/base/arraysize.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The min number of tokens must present in Turn host uri.
|
||||
// e.g. user@turn.example.org
|
||||
static const size_t kTurnHostTokensNum = 2;
|
||||
// Number of tokens must be preset when TURN uri has transport param.
|
||||
static const size_t kTurnTransportTokensNum = 2;
|
||||
// The default stun port.
|
||||
static const int kDefaultStunPort = 3478;
|
||||
static const int kDefaultStunTlsPort = 5349;
|
||||
static const char kTransport[] = "transport";
|
||||
|
||||
// NOTE: Must be in the same order as the ServiceType enum.
|
||||
static const char* kValidIceServiceTypes[] = {"stun", "stuns", "turn", "turns"};
|
||||
|
||||
// NOTE: A loop below assumes that the first value of this enum is 0 and all
|
||||
// other values are incremental.
|
||||
enum ServiceType {
|
||||
STUN = 0, // Indicates a STUN server.
|
||||
STUNS, // Indicates a STUN server used with a TLS session.
|
||||
TURN, // Indicates a TURN server
|
||||
TURNS, // Indicates a TURN server used with a TLS session.
|
||||
INVALID, // Unknown.
|
||||
};
|
||||
static_assert(INVALID == arraysize(kValidIceServiceTypes),
|
||||
"kValidIceServiceTypes must have as many strings as ServiceType "
|
||||
"has values.");
|
||||
|
||||
// |in_str| should be of format
|
||||
// stunURI = scheme ":" stun-host [ ":" stun-port ]
|
||||
// scheme = "stun" / "stuns"
|
||||
// stun-host = IP-literal / IPv4address / reg-name
|
||||
// stun-port = *DIGIT
|
||||
//
|
||||
// draft-petithuguenin-behave-turn-uris-01
|
||||
// turnURI = scheme ":" turn-host [ ":" turn-port ]
|
||||
// turn-host = username@IP-literal / IPv4address / reg-name
|
||||
static bool GetServiceTypeAndHostnameFromUri(const std::string& in_str,
|
||||
ServiceType* service_type,
|
||||
std::string* hostname) {
|
||||
const std::string::size_type colonpos = in_str.find(':');
|
||||
if (colonpos == std::string::npos) {
|
||||
LOG(LS_WARNING) << "Missing ':' in ICE URI: " << in_str;
|
||||
return false;
|
||||
}
|
||||
if ((colonpos + 1) == in_str.length()) {
|
||||
LOG(LS_WARNING) << "Empty hostname in ICE URI: " << in_str;
|
||||
return false;
|
||||
}
|
||||
*service_type = INVALID;
|
||||
for (size_t i = 0; i < arraysize(kValidIceServiceTypes); ++i) {
|
||||
if (in_str.compare(0, colonpos, kValidIceServiceTypes[i]) == 0) {
|
||||
*service_type = static_cast<ServiceType>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*service_type == INVALID) {
|
||||
return false;
|
||||
}
|
||||
*hostname = in_str.substr(colonpos + 1, std::string::npos);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParsePort(const std::string& in_str, int* port) {
|
||||
// Make sure port only contains digits. FromString doesn't check this.
|
||||
for (const char& c : in_str) {
|
||||
if (!std::isdigit(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return rtc::FromString(in_str, port);
|
||||
}
|
||||
|
||||
// This method parses IPv6 and IPv4 literal strings, along with hostnames in
|
||||
// standard hostname:port format.
|
||||
// Consider following formats as correct.
|
||||
// |hostname:port|, |[IPV6 address]:port|, |IPv4 address|:port,
|
||||
// |hostname|, |[IPv6 address]|, |IPv4 address|.
|
||||
static bool ParseHostnameAndPortFromString(const std::string& in_str,
|
||||
std::string* host,
|
||||
int* port) {
|
||||
RTC_DCHECK(host->empty());
|
||||
if (in_str.at(0) == '[') {
|
||||
std::string::size_type closebracket = in_str.rfind(']');
|
||||
if (closebracket != std::string::npos) {
|
||||
std::string::size_type colonpos = in_str.find(':', closebracket);
|
||||
if (std::string::npos != colonpos) {
|
||||
if (!ParsePort(in_str.substr(closebracket + 2, std::string::npos),
|
||||
port)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*host = in_str.substr(1, closebracket - 1);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
std::string::size_type colonpos = in_str.find(':');
|
||||
if (std::string::npos != colonpos) {
|
||||
if (!ParsePort(in_str.substr(colonpos + 1, std::string::npos), port)) {
|
||||
return false;
|
||||
}
|
||||
*host = in_str.substr(0, colonpos);
|
||||
} else {
|
||||
*host = in_str;
|
||||
}
|
||||
}
|
||||
return !host->empty();
|
||||
}
|
||||
|
||||
// Adds a STUN or TURN server to the appropriate list,
|
||||
// by parsing |url| and using the username/password in |server|.
|
||||
static RTCErrorType ParseIceServerUrl(
|
||||
const PeerConnectionInterface::IceServer& server,
|
||||
const std::string& url,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers) {
|
||||
// draft-nandakumar-rtcweb-stun-uri-01
|
||||
// stunURI = scheme ":" stun-host [ ":" stun-port ]
|
||||
// scheme = "stun" / "stuns"
|
||||
// stun-host = IP-literal / IPv4address / reg-name
|
||||
// stun-port = *DIGIT
|
||||
|
||||
// draft-petithuguenin-behave-turn-uris-01
|
||||
// turnURI = scheme ":" turn-host [ ":" turn-port ]
|
||||
// [ "?transport=" transport ]
|
||||
// scheme = "turn" / "turns"
|
||||
// transport = "udp" / "tcp" / transport-ext
|
||||
// transport-ext = 1*unreserved
|
||||
// turn-host = IP-literal / IPv4address / reg-name
|
||||
// turn-port = *DIGIT
|
||||
RTC_DCHECK(stun_servers != nullptr);
|
||||
RTC_DCHECK(turn_servers != nullptr);
|
||||
std::vector<std::string> tokens;
|
||||
cricket::ProtocolType turn_transport_type = cricket::PROTO_UDP;
|
||||
RTC_DCHECK(!url.empty());
|
||||
rtc::tokenize_with_empty_tokens(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_with_empty_tokens(uri_transport_param, '=', &tokens);
|
||||
if (tokens[0] != kTransport) {
|
||||
LOG(LS_WARNING) << "Invalid transport parameter key.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
if (tokens.size() < 2) {
|
||||
LOG(LS_WARNING) << "Transport parameter missing value.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
if (!cricket::StringToProto(tokens[1].c_str(), &turn_transport_type) ||
|
||||
(turn_transport_type != cricket::PROTO_UDP &&
|
||||
turn_transport_type != cricket::PROTO_TCP)) {
|
||||
LOG(LS_WARNING) << "Transport parameter should always be udp or tcp.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
std::string hoststring;
|
||||
ServiceType service_type;
|
||||
if (!GetServiceTypeAndHostnameFromUri(uri_without_transport, &service_type,
|
||||
&hoststring)) {
|
||||
LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " << url;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
// GetServiceTypeAndHostnameFromUri should never give an empty hoststring
|
||||
RTC_DCHECK(!hoststring.empty());
|
||||
|
||||
// Let's break hostname.
|
||||
tokens.clear();
|
||||
rtc::tokenize_with_empty_tokens(hoststring, '@', &tokens);
|
||||
|
||||
std::string username(server.username);
|
||||
if (tokens.size() > kTurnHostTokensNum) {
|
||||
LOG(LS_WARNING) << "Invalid user@hostname format: " << hoststring;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
if (tokens.size() == kTurnHostTokensNum) {
|
||||
if (tokens[0].empty() || tokens[1].empty()) {
|
||||
LOG(LS_WARNING) << "Invalid user@hostname format: " << hoststring;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
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 = cricket::PROTO_TLS;
|
||||
}
|
||||
|
||||
std::string address;
|
||||
if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) {
|
||||
LOG(WARNING) << "Invalid hostname format: " << uri_without_transport;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (port <= 0 || port > 0xffff) {
|
||||
LOG(WARNING) << "Invalid port: " << port;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
switch (service_type) {
|
||||
case STUN:
|
||||
case STUNS:
|
||||
stun_servers->insert(rtc::SocketAddress(address, port));
|
||||
break;
|
||||
case TURN:
|
||||
case TURNS: {
|
||||
if (username.empty() || server.password.empty()) {
|
||||
// The WebRTC spec requires throwing an InvalidAccessError when username
|
||||
// or credential are ommitted; this is the native equivalent.
|
||||
return RTCErrorType::INVALID_PARAMETER;
|
||||
}
|
||||
cricket::RelayServerConfig config = cricket::RelayServerConfig(
|
||||
address, port, username, server.password, turn_transport_type);
|
||||
if (server.tls_cert_policy ==
|
||||
PeerConnectionInterface::kTlsCertPolicyInsecureNoCheck) {
|
||||
config.tls_cert_policy =
|
||||
cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK;
|
||||
}
|
||||
turn_servers->push_back(config);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// We shouldn't get to this point with an invalid service_type, we should
|
||||
// have returned an error already.
|
||||
RTC_NOTREACHED() << "Unexpected service type";
|
||||
return RTCErrorType::INTERNAL_ERROR;
|
||||
}
|
||||
return RTCErrorType::NONE;
|
||||
}
|
||||
|
||||
RTCErrorType ParseIceServers(
|
||||
const PeerConnectionInterface::IceServers& servers,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers) {
|
||||
for (const PeerConnectionInterface::IceServer& server : servers) {
|
||||
if (!server.urls.empty()) {
|
||||
for (const std::string& url : server.urls) {
|
||||
if (url.empty()) {
|
||||
LOG(LS_ERROR) << "Empty uri.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
RTCErrorType err =
|
||||
ParseIceServerUrl(server, url, stun_servers, turn_servers);
|
||||
if (err != RTCErrorType::NONE) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else if (!server.uri.empty()) {
|
||||
// Fallback to old .uri if new .urls isn't present.
|
||||
RTCErrorType err =
|
||||
ParseIceServerUrl(server, server.uri, stun_servers, turn_servers);
|
||||
if (err != RTCErrorType::NONE) {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
LOG(LS_ERROR) << "Empty uri.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
// Candidates must have unique priorities, so that connectivity checks
|
||||
// are performed in a well-defined order.
|
||||
int priority = static_cast<int>(turn_servers->size() - 1);
|
||||
for (cricket::RelayServerConfig& turn_server : *turn_servers) {
|
||||
// First in the list gets highest priority.
|
||||
turn_server.priority = priority--;
|
||||
}
|
||||
return RTCErrorType::NONE;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
34
webrtc/pc/iceserverparsing.h
Normal file
34
webrtc/pc/iceserverparsing.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_PC_ICESERVERPARSING_H_
|
||||
#define WEBRTC_PC_ICESERVERPARSING_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/api/peerconnectioninterface.h"
|
||||
#include "webrtc/api/rtcerror.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Parses the URLs for each server in |servers| to build |stun_servers| and
|
||||
// |turn_servers|. Can return SYNTAX_ERROR if the URL is malformed, or
|
||||
// INVALID_PARAMETER if a TURN server is missing |username| or |password|.
|
||||
//
|
||||
// Intended to be used to convert/validate the servers passed into a
|
||||
// PeerConnection through RTCConfiguration.
|
||||
RTCErrorType ParseIceServers(
|
||||
const PeerConnectionInterface::IceServers& servers,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_PC_ICESERVERPARSING_H_
|
||||
231
webrtc/pc/iceserverparsing_unittest.cc
Normal file
231
webrtc/pc/iceserverparsing_unittest.cc
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/pc/iceserverparsing.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class IceServerParsingTest : public testing::Test {
|
||||
public:
|
||||
// Convenience functions for parsing a single URL. Result is stored in
|
||||
// |stun_servers_| and |turn_servers_|.
|
||||
bool ParseUrl(const std::string& url) {
|
||||
return ParseUrl(url, std::string(), std::string());
|
||||
}
|
||||
|
||||
bool ParseTurnUrl(const std::string& url) {
|
||||
return ParseUrl(url, "username", "password");
|
||||
}
|
||||
|
||||
bool ParseUrl(const std::string& url,
|
||||
const std::string& username,
|
||||
const std::string& password) {
|
||||
return ParseUrl(
|
||||
url, username, password,
|
||||
PeerConnectionInterface::TlsCertPolicy::kTlsCertPolicySecure);
|
||||
}
|
||||
|
||||
bool ParseUrl(const std::string& url,
|
||||
const std::string& username,
|
||||
const std::string& password,
|
||||
PeerConnectionInterface::TlsCertPolicy tls_certificate_policy) {
|
||||
stun_servers_.clear();
|
||||
turn_servers_.clear();
|
||||
PeerConnectionInterface::IceServers servers;
|
||||
PeerConnectionInterface::IceServer server;
|
||||
server.urls.push_back(url);
|
||||
server.username = username;
|
||||
server.password = password;
|
||||
server.tls_cert_policy = tls_certificate_policy;
|
||||
servers.push_back(server);
|
||||
return webrtc::ParseIceServers(servers, &stun_servers_, &turn_servers_) ==
|
||||
webrtc::RTCErrorType::NONE;
|
||||
}
|
||||
|
||||
protected:
|
||||
cricket::ServerAddresses stun_servers_;
|
||||
std::vector<cricket::RelayServerConfig> turn_servers_;
|
||||
};
|
||||
|
||||
// Make sure all STUN/TURN prefixes are parsed correctly.
|
||||
TEST_F(IceServerParsingTest, ParseStunPrefixes) {
|
||||
EXPECT_TRUE(ParseUrl("stun:hostname"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ(0U, turn_servers_.size());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stuns:hostname"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ(0U, turn_servers_.size());
|
||||
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:hostname"));
|
||||
EXPECT_EQ(0U, stun_servers_.size());
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(cricket::PROTO_UDP, turn_servers_[0].ports[0].proto);
|
||||
|
||||
EXPECT_TRUE(ParseTurnUrl("turns:hostname"));
|
||||
EXPECT_EQ(0U, stun_servers_.size());
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto);
|
||||
EXPECT_TRUE(turn_servers_[0].tls_cert_policy ==
|
||||
cricket::TlsCertPolicy::TLS_CERT_POLICY_SECURE);
|
||||
|
||||
EXPECT_TRUE(ParseUrl(
|
||||
"turns:hostname", "username", "password",
|
||||
PeerConnectionInterface::TlsCertPolicy::kTlsCertPolicyInsecureNoCheck));
|
||||
EXPECT_EQ(0U, stun_servers_.size());
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_TRUE(turn_servers_[0].tls_cert_policy ==
|
||||
cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK);
|
||||
EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto);
|
||||
|
||||
// invalid prefixes
|
||||
EXPECT_FALSE(ParseUrl("stunn:hostname"));
|
||||
EXPECT_FALSE(ParseUrl(":hostname"));
|
||||
EXPECT_FALSE(ParseUrl(":"));
|
||||
EXPECT_FALSE(ParseUrl(""));
|
||||
}
|
||||
|
||||
TEST_F(IceServerParsingTest, VerifyDefaults) {
|
||||
// TURNS defaults
|
||||
EXPECT_TRUE(ParseTurnUrl("turns:hostname"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(5349, turn_servers_[0].ports[0].address.port());
|
||||
EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto);
|
||||
|
||||
// TURN defaults
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:hostname"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(3478, turn_servers_[0].ports[0].address.port());
|
||||
EXPECT_EQ(cricket::PROTO_UDP, turn_servers_[0].ports[0].proto);
|
||||
|
||||
// STUN defaults
|
||||
EXPECT_TRUE(ParseUrl("stun:hostname"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ(3478, stun_servers_.begin()->port());
|
||||
}
|
||||
|
||||
// Check that the 6 combinations of IPv4/IPv6/hostname and with/without port
|
||||
// can be parsed correctly.
|
||||
TEST_F(IceServerParsingTest, ParseHostnameAndPort) {
|
||||
EXPECT_TRUE(ParseUrl("stun:1.2.3.4:1234"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("1.2.3.4", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(1234, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:[1:2:3:4:5:6:7:8]:4321"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("1:2:3:4:5:6:7:8", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(4321, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:hostname:9999"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("hostname", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(9999, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:1.2.3.4"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("1.2.3.4", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(3478, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:[1:2:3:4:5:6:7:8]"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("1:2:3:4:5:6:7:8", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(3478, stun_servers_.begin()->port());
|
||||
|
||||
EXPECT_TRUE(ParseUrl("stun:hostname"));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ("hostname", stun_servers_.begin()->hostname());
|
||||
EXPECT_EQ(3478, stun_servers_.begin()->port());
|
||||
|
||||
// Try some invalid hostname:port strings.
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:99a99"));
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:-1"));
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:port:more"));
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:port more"));
|
||||
EXPECT_FALSE(ParseUrl("stun:hostname:"));
|
||||
EXPECT_FALSE(ParseUrl("stun:[1:2:3:4:5:6:7:8]junk:1000"));
|
||||
EXPECT_FALSE(ParseUrl("stun::5555"));
|
||||
EXPECT_FALSE(ParseUrl("stun:"));
|
||||
}
|
||||
|
||||
// Test parsing the "?transport=xxx" part of the URL.
|
||||
TEST_F(IceServerParsingTest, ParseTransport) {
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:hostname:1234?transport=tcp"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(cricket::PROTO_TCP, turn_servers_[0].ports[0].proto);
|
||||
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:hostname?transport=udp"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ(cricket::PROTO_UDP, turn_servers_[0].ports[0].proto);
|
||||
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:hostname?transport=invalid"));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:hostname?transport="));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:hostname?="));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:hostname?"));
|
||||
EXPECT_FALSE(ParseTurnUrl("?"));
|
||||
}
|
||||
|
||||
// Test parsing ICE username contained in URL.
|
||||
TEST_F(IceServerParsingTest, ParseUsername) {
|
||||
EXPECT_TRUE(ParseTurnUrl("turn:user@hostname"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ("user", turn_servers_[0].credentials.username);
|
||||
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:@hostname"));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:username@"));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:@"));
|
||||
EXPECT_FALSE(ParseTurnUrl("turn:user@name@hostname"));
|
||||
}
|
||||
|
||||
// Test that username and password from IceServer is copied into the resulting
|
||||
// RelayServerConfig.
|
||||
TEST_F(IceServerParsingTest, CopyUsernameAndPasswordFromIceServer) {
|
||||
EXPECT_TRUE(ParseUrl("turn:hostname", "username", "password"));
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
EXPECT_EQ("username", turn_servers_[0].credentials.username);
|
||||
EXPECT_EQ("password", turn_servers_[0].credentials.password);
|
||||
}
|
||||
|
||||
// Ensure that if a server has multiple URLs, each one is parsed.
|
||||
TEST_F(IceServerParsingTest, ParseMultipleUrls) {
|
||||
PeerConnectionInterface::IceServers servers;
|
||||
PeerConnectionInterface::IceServer server;
|
||||
server.urls.push_back("stun:hostname");
|
||||
server.urls.push_back("turn:hostname");
|
||||
server.username = "foo";
|
||||
server.password = "bar";
|
||||
servers.push_back(server);
|
||||
EXPECT_EQ(webrtc::RTCErrorType::NONE,
|
||||
webrtc::ParseIceServers(servers, &stun_servers_, &turn_servers_));
|
||||
EXPECT_EQ(1U, stun_servers_.size());
|
||||
EXPECT_EQ(1U, turn_servers_.size());
|
||||
}
|
||||
|
||||
// Ensure that TURN servers are given unique priorities,
|
||||
// so that their resulting candidates have unique priorities.
|
||||
TEST_F(IceServerParsingTest, TurnServerPrioritiesUnique) {
|
||||
PeerConnectionInterface::IceServers servers;
|
||||
PeerConnectionInterface::IceServer server;
|
||||
server.urls.push_back("turn:hostname");
|
||||
server.urls.push_back("turn:hostname2");
|
||||
server.username = "foo";
|
||||
server.password = "bar";
|
||||
servers.push_back(server);
|
||||
EXPECT_EQ(webrtc::RTCErrorType::NONE,
|
||||
webrtc::ParseIceServers(servers, &stun_servers_, &turn_servers_));
|
||||
EXPECT_EQ(2U, turn_servers_.size());
|
||||
EXPECT_NE(turn_servers_[0].priority, turn_servers_[1].priority);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -11,7 +11,6 @@
|
||||
#include "webrtc/pc/peerconnection.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype> // for isdigit
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@ -20,7 +19,6 @@
|
||||
#include "webrtc/api/mediaconstraintsinterface.h"
|
||||
#include "webrtc/api/mediastreamproxy.h"
|
||||
#include "webrtc/api/mediastreamtrackproxy.h"
|
||||
#include "webrtc/base/arraysize.h"
|
||||
#include "webrtc/base/bind.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
@ -62,35 +60,9 @@ static const char kDefaultStreamLabel[] = "default";
|
||||
static const char kDefaultAudioTrackLabel[] = "defaulta0";
|
||||
static const char kDefaultVideoTrackLabel[] = "defaultv0";
|
||||
|
||||
// The min number of tokens must present in Turn host uri.
|
||||
// e.g. user@turn.example.org
|
||||
static const size_t kTurnHostTokensNum = 2;
|
||||
// Number of tokens must be preset when TURN uri has transport param.
|
||||
static const size_t kTurnTransportTokensNum = 2;
|
||||
// The default stun port.
|
||||
static const int kDefaultStunPort = 3478;
|
||||
static const int kDefaultStunTlsPort = 5349;
|
||||
static const char kTransport[] = "transport";
|
||||
|
||||
// NOTE: Must be in the same order as the ServiceType enum.
|
||||
static const char* kValidIceServiceTypes[] = {"stun", "stuns", "turn", "turns"};
|
||||
|
||||
// The length of RTCP CNAMEs.
|
||||
static const int kRtcpCnameLength = 16;
|
||||
|
||||
// NOTE: A loop below assumes that the first value of this enum is 0 and all
|
||||
// other values are incremental.
|
||||
enum ServiceType {
|
||||
STUN = 0, // Indicates a STUN server.
|
||||
STUNS, // Indicates a STUN server used with a TLS session.
|
||||
TURN, // Indicates a TURN server
|
||||
TURNS, // Indicates a TURN server used with a TLS session.
|
||||
INVALID, // Unknown.
|
||||
};
|
||||
static_assert(INVALID == arraysize(kValidIceServiceTypes),
|
||||
"kValidIceServiceTypes must have as many strings as ServiceType "
|
||||
"has values.");
|
||||
|
||||
enum {
|
||||
MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0,
|
||||
MSG_SET_SESSIONDESCRIPTION_FAILED,
|
||||
@ -127,216 +99,6 @@ struct GetStatsMsg : public rtc::MessageData {
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track;
|
||||
};
|
||||
|
||||
// |in_str| should be of format
|
||||
// stunURI = scheme ":" stun-host [ ":" stun-port ]
|
||||
// scheme = "stun" / "stuns"
|
||||
// stun-host = IP-literal / IPv4address / reg-name
|
||||
// stun-port = *DIGIT
|
||||
//
|
||||
// draft-petithuguenin-behave-turn-uris-01
|
||||
// turnURI = scheme ":" turn-host [ ":" turn-port ]
|
||||
// turn-host = username@IP-literal / IPv4address / reg-name
|
||||
bool GetServiceTypeAndHostnameFromUri(const std::string& in_str,
|
||||
ServiceType* service_type,
|
||||
std::string* hostname) {
|
||||
const std::string::size_type colonpos = in_str.find(':');
|
||||
if (colonpos == std::string::npos) {
|
||||
LOG(LS_WARNING) << "Missing ':' in ICE URI: " << in_str;
|
||||
return false;
|
||||
}
|
||||
if ((colonpos + 1) == in_str.length()) {
|
||||
LOG(LS_WARNING) << "Empty hostname in ICE URI: " << in_str;
|
||||
return false;
|
||||
}
|
||||
*service_type = INVALID;
|
||||
for (size_t i = 0; i < arraysize(kValidIceServiceTypes); ++i) {
|
||||
if (in_str.compare(0, colonpos, kValidIceServiceTypes[i]) == 0) {
|
||||
*service_type = static_cast<ServiceType>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*service_type == INVALID) {
|
||||
return false;
|
||||
}
|
||||
*hostname = in_str.substr(colonpos + 1, std::string::npos);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParsePort(const std::string& in_str, int* port) {
|
||||
// Make sure port only contains digits. FromString doesn't check this.
|
||||
for (const char& c : in_str) {
|
||||
if (!std::isdigit(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return rtc::FromString(in_str, port);
|
||||
}
|
||||
|
||||
// This method parses IPv6 and IPv4 literal strings, along with hostnames in
|
||||
// standard hostname:port format.
|
||||
// Consider following formats as correct.
|
||||
// |hostname:port|, |[IPV6 address]:port|, |IPv4 address|:port,
|
||||
// |hostname|, |[IPv6 address]|, |IPv4 address|.
|
||||
bool ParseHostnameAndPortFromString(const std::string& in_str,
|
||||
std::string* host,
|
||||
int* port) {
|
||||
RTC_DCHECK(host->empty());
|
||||
if (in_str.at(0) == '[') {
|
||||
std::string::size_type closebracket = in_str.rfind(']');
|
||||
if (closebracket != std::string::npos) {
|
||||
std::string::size_type colonpos = in_str.find(':', closebracket);
|
||||
if (std::string::npos != colonpos) {
|
||||
if (!ParsePort(in_str.substr(closebracket + 2, std::string::npos),
|
||||
port)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*host = in_str.substr(1, closebracket - 1);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
std::string::size_type colonpos = in_str.find(':');
|
||||
if (std::string::npos != colonpos) {
|
||||
if (!ParsePort(in_str.substr(colonpos + 1, std::string::npos), port)) {
|
||||
return false;
|
||||
}
|
||||
*host = in_str.substr(0, colonpos);
|
||||
} else {
|
||||
*host = in_str;
|
||||
}
|
||||
}
|
||||
return !host->empty();
|
||||
}
|
||||
|
||||
// Adds a STUN or TURN server to the appropriate list,
|
||||
// by parsing |url| and using the username/password in |server|.
|
||||
RTCErrorType ParseIceServerUrl(
|
||||
const PeerConnectionInterface::IceServer& server,
|
||||
const std::string& url,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers) {
|
||||
// draft-nandakumar-rtcweb-stun-uri-01
|
||||
// stunURI = scheme ":" stun-host [ ":" stun-port ]
|
||||
// scheme = "stun" / "stuns"
|
||||
// stun-host = IP-literal / IPv4address / reg-name
|
||||
// stun-port = *DIGIT
|
||||
|
||||
// draft-petithuguenin-behave-turn-uris-01
|
||||
// turnURI = scheme ":" turn-host [ ":" turn-port ]
|
||||
// [ "?transport=" transport ]
|
||||
// scheme = "turn" / "turns"
|
||||
// transport = "udp" / "tcp" / transport-ext
|
||||
// transport-ext = 1*unreserved
|
||||
// turn-host = IP-literal / IPv4address / reg-name
|
||||
// turn-port = *DIGIT
|
||||
RTC_DCHECK(stun_servers != nullptr);
|
||||
RTC_DCHECK(turn_servers != nullptr);
|
||||
std::vector<std::string> tokens;
|
||||
cricket::ProtocolType turn_transport_type = cricket::PROTO_UDP;
|
||||
RTC_DCHECK(!url.empty());
|
||||
rtc::tokenize_with_empty_tokens(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_with_empty_tokens(uri_transport_param, '=', &tokens);
|
||||
if (tokens[0] != kTransport) {
|
||||
LOG(LS_WARNING) << "Invalid transport parameter key.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
if (tokens.size() < 2) {
|
||||
LOG(LS_WARNING) << "Transport parameter missing value.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
if (!cricket::StringToProto(tokens[1].c_str(), &turn_transport_type) ||
|
||||
(turn_transport_type != cricket::PROTO_UDP &&
|
||||
turn_transport_type != cricket::PROTO_TCP)) {
|
||||
LOG(LS_WARNING) << "Transport parameter should always be udp or tcp.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
std::string hoststring;
|
||||
ServiceType service_type;
|
||||
if (!GetServiceTypeAndHostnameFromUri(uri_without_transport,
|
||||
&service_type,
|
||||
&hoststring)) {
|
||||
LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " << url;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
// GetServiceTypeAndHostnameFromUri should never give an empty hoststring
|
||||
RTC_DCHECK(!hoststring.empty());
|
||||
|
||||
// Let's break hostname.
|
||||
tokens.clear();
|
||||
rtc::tokenize_with_empty_tokens(hoststring, '@', &tokens);
|
||||
|
||||
std::string username(server.username);
|
||||
if (tokens.size() > kTurnHostTokensNum) {
|
||||
LOG(LS_WARNING) << "Invalid user@hostname format: " << hoststring;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
if (tokens.size() == kTurnHostTokensNum) {
|
||||
if (tokens[0].empty() || tokens[1].empty()) {
|
||||
LOG(LS_WARNING) << "Invalid user@hostname format: " << hoststring;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
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 = cricket::PROTO_TLS;
|
||||
}
|
||||
|
||||
std::string address;
|
||||
if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) {
|
||||
LOG(WARNING) << "Invalid hostname format: " << uri_without_transport;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (port <= 0 || port > 0xffff) {
|
||||
LOG(WARNING) << "Invalid port: " << port;
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
switch (service_type) {
|
||||
case STUN:
|
||||
case STUNS:
|
||||
stun_servers->insert(rtc::SocketAddress(address, port));
|
||||
break;
|
||||
case TURN:
|
||||
case TURNS: {
|
||||
if (username.empty() || server.password.empty()) {
|
||||
// The WebRTC spec requires throwing an InvalidAccessError when username
|
||||
// or credential are ommitted; this is the native equivalent.
|
||||
return RTCErrorType::INVALID_PARAMETER;
|
||||
}
|
||||
cricket::RelayServerConfig config = cricket::RelayServerConfig(
|
||||
address, port, username, server.password, turn_transport_type);
|
||||
if (server.tls_cert_policy ==
|
||||
PeerConnectionInterface::kTlsCertPolicyInsecureNoCheck) {
|
||||
config.tls_cert_policy =
|
||||
cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK;
|
||||
}
|
||||
turn_servers->push_back(config);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// We shouldn't get to this point with an invalid service_type, we should
|
||||
// have returned an error already.
|
||||
RTC_NOTREACHED() << "Unexpected service type";
|
||||
return RTCErrorType::INTERNAL_ERROR;
|
||||
}
|
||||
return RTCErrorType::NONE;
|
||||
}
|
||||
|
||||
// Check if we can send |new_stream| on a PeerConnection.
|
||||
bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams,
|
||||
webrtc::MediaStreamInterface* new_stream) {
|
||||
@ -629,45 +391,6 @@ bool ParseConstraintsForAnswer(const MediaConstraintsInterface* constraints,
|
||||
return mandatory_constraints_satisfied == constraints->GetMandatory().size();
|
||||
}
|
||||
|
||||
RTCErrorType ParseIceServers(
|
||||
const PeerConnectionInterface::IceServers& servers,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers) {
|
||||
for (const webrtc::PeerConnectionInterface::IceServer& server : servers) {
|
||||
if (!server.urls.empty()) {
|
||||
for (const std::string& url : server.urls) {
|
||||
if (url.empty()) {
|
||||
LOG(LS_ERROR) << "Empty uri.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
RTCErrorType err =
|
||||
ParseIceServerUrl(server, url, stun_servers, turn_servers);
|
||||
if (err != RTCErrorType::NONE) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else if (!server.uri.empty()) {
|
||||
// Fallback to old .uri if new .urls isn't present.
|
||||
RTCErrorType err =
|
||||
ParseIceServerUrl(server, server.uri, stun_servers, turn_servers);
|
||||
if (err != RTCErrorType::NONE) {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
LOG(LS_ERROR) << "Empty uri.";
|
||||
return RTCErrorType::SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
// Candidates must have unique priorities, so that connectivity checks
|
||||
// are performed in a well-defined order.
|
||||
int priority = static_cast<int>(turn_servers->size() - 1);
|
||||
for (cricket::RelayServerConfig& turn_server : *turn_servers) {
|
||||
// First in the list gets highest priority.
|
||||
turn_server.priority = priority--;
|
||||
}
|
||||
return RTCErrorType::NONE;
|
||||
}
|
||||
|
||||
PeerConnection::PeerConnection(PeerConnectionFactory* factory)
|
||||
: factory_(factory),
|
||||
observer_(NULL),
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/api/peerconnectioninterface.h"
|
||||
#include "webrtc/pc/iceserverparsing.h"
|
||||
#include "webrtc/pc/peerconnectionfactory.h"
|
||||
#include "webrtc/pc/rtcstatscollector.h"
|
||||
#include "webrtc/pc/rtpreceiver.h"
|
||||
@ -53,14 +54,6 @@ bool ExtractMediaSessionOptions(
|
||||
bool ParseConstraintsForAnswer(const MediaConstraintsInterface* constraints,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
|
||||
// Parses the URLs for each server in |servers| to build |stun_servers| and
|
||||
// |turn_servers|. Can return SYNTAX_ERROR if the URL is malformed, or
|
||||
// INVALID_PARAMETER if a TURN server is missing |username| or |password|.
|
||||
RTCErrorType ParseIceServers(
|
||||
const PeerConnectionInterface::IceServers& servers,
|
||||
cricket::ServerAddresses* stun_servers,
|
||||
std::vector<cricket::RelayServerConfig>* turn_servers);
|
||||
|
||||
// PeerConnection implements the PeerConnectionInterface interface.
|
||||
// It uses WebRtcSession to implement the PeerConnection functionality.
|
||||
class PeerConnection : public PeerConnectionInterface,
|
||||
|
||||
2706
webrtc/pc/peerconnection_integrationtest.cc
Normal file
2706
webrtc/pc/peerconnection_integrationtest.cc
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -732,6 +732,8 @@ class PeerConnectionInterfaceTest : public testing::Test {
|
||||
new cricket::FakePortAllocator(rtc::Thread::Current(), nullptr));
|
||||
port_allocator_ = port_allocator.get();
|
||||
|
||||
// Create certificate generator unless DTLS constraint is explicitly set to
|
||||
// false.
|
||||
std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator;
|
||||
bool dtls;
|
||||
if (FindConstraint(constraints,
|
||||
@ -850,7 +852,7 @@ class PeerConnectionInterfaceTest : public testing::Test {
|
||||
pc_->CreateAnswer(observer, constraints);
|
||||
}
|
||||
EXPECT_EQ_WAIT(true, observer->called(), kTimeout);
|
||||
desc->reset(observer->release_desc());
|
||||
*desc = observer->MoveDescription();
|
||||
return observer->result();
|
||||
}
|
||||
|
||||
@ -1649,6 +1651,18 @@ TEST_F(PeerConnectionInterfaceTest, RemoveTrackAfterAddStream) {
|
||||
EXPECT_TRUE(video_desc == nullptr);
|
||||
}
|
||||
|
||||
// Verify that CreateDtmfSender only succeeds if called with a valid local
|
||||
// track. Other aspects of DtmfSenders are tested in
|
||||
// peerconnection_integrationtest.cc.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateDtmfSenderWithInvalidParams) {
|
||||
CreatePeerConnection();
|
||||
AddAudioVideoStream(kStreamLabel1, "audio_label", "video_label");
|
||||
EXPECT_EQ(nullptr, pc_->CreateDtmfSender(nullptr));
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface> non_localtrack(
|
||||
pc_factory_->CreateAudioTrack("dummy_track", nullptr));
|
||||
EXPECT_EQ(nullptr, pc_->CreateDtmfSender(non_localtrack));
|
||||
}
|
||||
|
||||
// Test creating a sender with a stream ID, and ensure the ID is populated
|
||||
// in the offer.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateSenderWithStream) {
|
||||
@ -3150,6 +3164,128 @@ TEST_F(PeerConnectionInterfaceTest,
|
||||
pc_->StopRtcEventLog();
|
||||
}
|
||||
|
||||
// Test that ICE renomination isn't offered if it's not enabled in the PC's
|
||||
// RTCConfiguration.
|
||||
TEST_F(PeerConnectionInterfaceTest, IceRenominationNotOffered) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.enable_ice_renomination = false;
|
||||
CreatePeerConnection(config, nullptr);
|
||||
AddVoiceStream("foo");
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
ASSERT_TRUE(DoCreateOffer(&offer, nullptr));
|
||||
cricket::SessionDescription* desc = offer->description();
|
||||
EXPECT_EQ(1u, desc->transport_infos().size());
|
||||
EXPECT_FALSE(
|
||||
desc->transport_infos()[0].description.GetIceParameters().renomination);
|
||||
}
|
||||
|
||||
// Test that the ICE renomination option is present in generated offers/answers
|
||||
// if it's enabled in the PC's RTCConfiguration.
|
||||
TEST_F(PeerConnectionInterfaceTest, IceRenominationOptionInOfferAndAnswer) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.enable_ice_renomination = true;
|
||||
CreatePeerConnection(config, nullptr);
|
||||
AddVoiceStream("foo");
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
ASSERT_TRUE(DoCreateOffer(&offer, nullptr));
|
||||
cricket::SessionDescription* desc = offer->description();
|
||||
EXPECT_EQ(1u, desc->transport_infos().size());
|
||||
EXPECT_TRUE(
|
||||
desc->transport_infos()[0].description.GetIceParameters().renomination);
|
||||
|
||||
// Set the offer as a remote description, then create an answer and ensure it
|
||||
// has the renomination flag too.
|
||||
EXPECT_TRUE(DoSetRemoteDescription(offer.release()));
|
||||
std::unique_ptr<SessionDescriptionInterface> answer;
|
||||
ASSERT_TRUE(DoCreateAnswer(&answer, nullptr));
|
||||
desc = answer->description();
|
||||
EXPECT_EQ(1u, desc->transport_infos().size());
|
||||
EXPECT_TRUE(
|
||||
desc->transport_infos()[0].description.GetIceParameters().renomination);
|
||||
}
|
||||
|
||||
// Test that if CreateOffer is called with the deprecated "offer to receive
|
||||
// audio/video" constraints, they're processed and result in an offer with
|
||||
// audio/video sections just as if RTCOfferAnswerOptions had been used.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithOfferToReceiveConstraints) {
|
||||
CreatePeerConnection();
|
||||
|
||||
FakeConstraints constraints;
|
||||
constraints.SetMandatoryReceiveAudio(true);
|
||||
constraints.SetMandatoryReceiveVideo(true);
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
ASSERT_TRUE(DoCreateOffer(&offer, &constraints));
|
||||
|
||||
cricket::SessionDescription* desc = offer->description();
|
||||
const cricket::ContentInfo* audio = cricket::GetFirstAudioContent(desc);
|
||||
const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
|
||||
ASSERT_NE(nullptr, audio);
|
||||
ASSERT_NE(nullptr, video);
|
||||
EXPECT_FALSE(audio->rejected);
|
||||
EXPECT_FALSE(video->rejected);
|
||||
}
|
||||
|
||||
// Test that if CreateAnswer is called with the deprecated "offer to receive
|
||||
// audio/video" constraints, they're processed and can be used to reject an
|
||||
// offered m= section just as can be done with RTCOfferAnswerOptions;
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateAnswerWithOfferToReceiveConstraints) {
|
||||
CreatePeerConnection();
|
||||
|
||||
// First, create an offer with audio/video and apply it as a remote
|
||||
// description.
|
||||
FakeConstraints constraints;
|
||||
constraints.SetMandatoryReceiveAudio(true);
|
||||
constraints.SetMandatoryReceiveVideo(true);
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
ASSERT_TRUE(DoCreateOffer(&offer, &constraints));
|
||||
EXPECT_TRUE(DoSetRemoteDescription(offer.release()));
|
||||
|
||||
// Now create answer that rejects audio/video.
|
||||
constraints.SetMandatoryReceiveAudio(false);
|
||||
constraints.SetMandatoryReceiveVideo(false);
|
||||
std::unique_ptr<SessionDescriptionInterface> answer;
|
||||
ASSERT_TRUE(DoCreateAnswer(&answer, &constraints));
|
||||
|
||||
cricket::SessionDescription* desc = answer->description();
|
||||
const cricket::ContentInfo* audio = cricket::GetFirstAudioContent(desc);
|
||||
const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
|
||||
ASSERT_NE(nullptr, audio);
|
||||
ASSERT_NE(nullptr, video);
|
||||
EXPECT_TRUE(audio->rejected);
|
||||
EXPECT_TRUE(video->rejected);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SCTP
|
||||
#define MAYBE_DataChannelOnlyOfferWithMaxBundlePolicy \
|
||||
DataChannelOnlyOfferWithMaxBundlePolicy
|
||||
#else
|
||||
#define MAYBE_DataChannelOnlyOfferWithMaxBundlePolicy \
|
||||
DISABLED_DataChannelOnlyOfferWithMaxBundlePolicy
|
||||
#endif
|
||||
|
||||
// Test that negotiation can succeed with a data channel only, and with the max
|
||||
// bundle policy. Previously there was a bug that prevented this.
|
||||
TEST_F(PeerConnectionInterfaceTest,
|
||||
MAYBE_DataChannelOnlyOfferWithMaxBundlePolicy) {
|
||||
PeerConnectionInterface::RTCConfiguration config;
|
||||
config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
|
||||
CreatePeerConnection(config, nullptr);
|
||||
|
||||
// First, create an offer with only a data channel and apply it as a remote
|
||||
// description.
|
||||
pc_->CreateDataChannel("test", nullptr);
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
ASSERT_TRUE(DoCreateOffer(&offer, nullptr));
|
||||
EXPECT_TRUE(DoSetRemoteDescription(offer.release()));
|
||||
|
||||
// Create and set answer as well.
|
||||
std::unique_ptr<SessionDescriptionInterface> answer;
|
||||
ASSERT_TRUE(DoCreateAnswer(&answer, nullptr));
|
||||
EXPECT_TRUE(DoSetLocalDescription(answer.release()));
|
||||
}
|
||||
|
||||
class PeerConnectionMediaConfigTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
|
||||
@ -39,8 +39,8 @@ class MockCreateSessionDescriptionObserver
|
||||
}
|
||||
bool called() const { return called_; }
|
||||
bool result() const { return result_; }
|
||||
SessionDescriptionInterface* release_desc() {
|
||||
return desc_.release();
|
||||
std::unique_ptr<SessionDescriptionInterface> MoveDescription() {
|
||||
return std::move(desc_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user