Bug 1231981 - Part 3: Set up TURN server for webrtc mochitests, when configured to. r=drno
MozReview-Commit-ID: CVbAYPrwpuB
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -332,19 +332,22 @@ function run_test(is_initiator,timeout)
s.src = "/test.js";
s.onload = () => setTestOptions(options);
document.head.appendChild(s);
}
function runTestWhenReady(testFunc) {
setupEnvironment();
return testConfigured.then(options => testFunc(options))
- .catch(e => ok(false, 'Error executing test: ' + e +
+ .catch(e => {
+ ok(false, 'Error executing test: ' + e +
((typeof e.stack === 'string') ?
- (' ' + e.stack.split('\n').join(' ... ')) : '')));
+ (' ' + e.stack.split('\n').join(' ... ')) : ''));
+ SimpleTest.finish();
+ });
}
/**
* Checks that the media stream tracks have the expected amount of tracks
* with the correct kind and id based on the type and constraints given.
*
* @param {Object} constraints specifies whether the stream should have
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -59,17 +59,23 @@ function PeerConnectionTest(options) {
options.is_local = "is_local" in options ? options.is_local : true;
options.is_remote = "is_remote" in options ? options.is_remote : true;
options.h264 = "h264" in options ? options.h264 : false;
options.bundle = "bundle" in options ? options.bundle : true;
options.rtcpmux = "rtcpmux" in options ? options.rtcpmux : true;
options.opus = "opus" in options ? options.opus : true;
- if (typeof turnServers !== "undefined") {
+ if (iceServersArray.length) {
+ options.config_remote = options.config_remote || {}
+ options.config_local = options.config_local || {}
+ options.config_remote.iceServers = iceServersArray;
+ options.config_local.iceServers = iceServersArray;
+ }
+ else if (typeof turnServers !== "undefined") {
if ((!options.turn_disabled_local) && (turnServers.local)) {
if (!options.hasOwnProperty("config_local")) {
options.config_local = {};
}
if (!options.config_local.hasOwnProperty("iceServers")) {
options.config_local.iceServers = turnServers.local.iceServers;
}
}
@@ -1674,17 +1680,17 @@ PeerConnectionWrapper.prototype = {
/**
* Compares the Ice server configured for this PeerConnectionWrapper
* with the ICE candidates received in the RTCP stats.
*
* @param {object} stats
* The stats to be verified for relayed vs. direct connection.
*/
- checkStatsIceConnectionType : function(stats) {
+ checkStatsIceConnectionType : function(stats, expectedLocalCandidateType) {
let lId;
let rId;
for (let stat of stats.values()) {
if (stat.type == "candidatepair" && stat.selected) {
lId = stat.localCandidateId;
rId = stat.remoteCandidateId;
break;
}
@@ -1693,31 +1699,35 @@ PeerConnectionWrapper.prototype = {
isnot(rId, undefined, "Got remote candidate ID " + rId + " for selected pair");
let lCand = stats.get(lId);
let rCand = stats.get(rId);
if (!lCand || !rCand) {
ok(false,
"failed to find candidatepair IDs or stats for local: "+ lId +" remote: "+ rId);
return;
}
+
info("checkStatsIceConnectionType verifying: local=" +
JSON.stringify(lCand) + " remote=" + JSON.stringify(rCand));
- if ((this.configuration) && (typeof this.configuration.iceServers !== 'undefined')) {
- info("Ice Server configured");
- // Note: the IP comparising is a workaround for bug 1097333
- // And this will fail if a TURN server address is a DNS name!
- var serverIp = this.configuration.iceServers[0].url.split(':')[1];
- ok(lCand.candidateType == "relayed" || rCand.candidateType == "relayed" ||
- lCand.ipAddress === serverIp || rCand.ipAddress === serverIp,
- "One peer uses a relay");
- } else {
- info("P2P configured");
- ok(lCand.candidateType != "relayed" && rCand.candidateType != "relayed",
- "Pure peer to peer call without a relay");
+ expectedLocalCandidateType = expectedLocalCandidateType || "host";
+ var candidateType = lCand.candidateType;
+ if ((lCand.mozLocalTransport === "tcp") && (candidateType === "relayed")) {
+ candidateType = "relayed-tcp";
}
+
+ if ((expectedLocalCandidateType === "serverreflexive") &&
+ (candidateType === "peerreflexive")) {
+ // Be forgiving of prflx when expecting srflx, since that can happen due
+ // to timing.
+ candidateType = "serverreflexive";
+ }
+
+ is(candidateType,
+ expectedLocalCandidateType,
+ "Local candidate type is what we expected for selected pair");
},
/**
* Compares amount of established ICE connection according to ICE candidate
* pairs in the stats reporting with the expected amount of connection based
* on the constraints.
*
* @param {object} stats
@@ -1840,16 +1850,65 @@ var scriptsReady = Promise.all([
document.head.appendChild(el);
return new Promise(r => { el.onload = r; el.onerror = r; });
}));
function createHTML(options) {
return scriptsReady.then(() => realCreateHTML(options));
}
-function runNetworkTest(testFunction) {
+var iceServerWebsocket;
+var iceServersArray = [];
+
+var setupIceServerConfig = useIceServer => {
+ // We disable ICE support for HTTP proxy when using a TURN server, because
+ // mochitest uses a fake HTTP proxy to serve content, which will eat our STUN
+ // packets for TURN TCP.
+ var enableHttpProxy = enable => new Promise(resolve => {
+ SpecialPowers.pushPrefEnv(
+ {'set': [['media.peerconnection.disable_http_proxy', !enable]]},
+ resolve);
+ });
+
+ var spawnIceServer = () => new Promise( (resolve, reject) => {
+ iceServerWebsocket = new WebSocket("ws://localhost:8191/");
+ iceServerWebsocket.onopen = (event) => {
+ info("websocket/process bridge open, starting ICE Server...");
+ iceServerWebsocket.send("iceserver");
+ }
+
+ iceServerWebsocket.onmessage = event => {
+ // The first message will contain the iceServers configuration, subsequent
+ // messages are just logging.
+ info("ICE Server: " + event.data);
+ resolve(event.data);
+ }
+
+ iceServerWebsocket.onerror = () => {
+ reject("ICE Server error: Is the ICE server websocket up?");
+ }
+
+ iceServerWebsocket.onclose = () => {
+ info("ICE Server websocket closed");
+ reject("ICE Server gone before getting configuration");
+ }
+ });
+
+ if (!useIceServer) {
+ info("Skipping ICE Server for this test");
+ return enableHttpProxy(true);
+ }
+
+ return enableHttpProxy(false)
+ .then(spawnIceServer)
+ .then(iceServersStr => { iceServersArray = JSON.parse(iceServersStr); });
+};
+
+function runNetworkTest(testFunction, fixtureOptions) {
+ fixtureOptions = fixtureOptions || {}
return scriptsReady.then(() =>
runTestWhenReady(options =>
startNetworkAndTest()
+ .then(() => setupIceServerConfig(fixtureOptions.useIceServer))
.then(() => testFunction(options))
)
);
}
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -436,23 +436,25 @@ var commandsPeerConnectionOfferAnswer =
function PC_REMOTE_CHECK_STATS(test) {
return test.pcRemote.getStats().then(stats => {
test.pcRemote.checkStats(stats, test.testOptions.steeplechase);
});
},
function PC_LOCAL_CHECK_ICE_CONNECTION_TYPE(test) {
return test.pcLocal.getStats().then(stats => {
- test.pcLocal.checkStatsIceConnectionType(stats);
+ test.pcLocal.checkStatsIceConnectionType(stats,
+ test.testOptions.expectedLocalCandidateType);
});
},
function PC_REMOTE_CHECK_ICE_CONNECTION_TYPE(test) {
return test.pcRemote.getStats().then(stats => {
- test.pcRemote.checkStatsIceConnectionType(stats);
+ test.pcRemote.checkStatsIceConnectionType(stats,
+ test.testOptions.expectedRemoteCandidateType);
});
},
function PC_LOCAL_CHECK_ICE_CONNECTIONS(test) {
return test.pcLocal.getStats().then(stats => {
test.pcLocal.checkStatsIceConnections(stats,
test._offer_constraints,
test._offer_options,
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
@@ -210,18 +210,18 @@ int nr_ice_candidate_create(nr_ice_ctx *
cand->u.relayed.turn_sock=osock;
/* Add the candidate to the isock list*/
TAILQ_INSERT_TAIL(&isock->candidates,cand,entry_sock);
nr_ice_candidate_compute_codeword(cand);
- r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): created candidate %s with type %s",
- ctx->label,cand->label,nr_ctype_name(ctype));
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): created candidate %s with type %s",
+ ctx->label,cand->codeword,cand->label,nr_ctype_name(ctype));
*candp=cand;
_status=0;
abort:
if (_status){
r_log(LOG_ICE,LOG_ERR,"ICE(%s): Failed to create candidate of type %s", ctx->label,nr_ctype_name(ctype));
nr_ice_candidate_destroy(&cand);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
@@ -512,16 +512,17 @@ void nr_ice_gather_finished_cb(NR_SOCKET
assert(cb_arg);
if (!cb_arg)
return;
ctx = cand->ctx;
ctx->uninitialized_candidates--;
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): initialized, %d remaining",ctx->label,cand->codeword,ctx->uninitialized_candidates);
/* Avoid the need for yet another initialization function */
if (cand->state == NR_ICE_CAND_STATE_INITIALIZING && cand->type == HOST)
cand->state = NR_ICE_CAND_STATE_INITIALIZED;
if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) {
int was_pruned = 0;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -238,20 +238,30 @@ PeerConnectionMedia::PeerConnectionMedia
mDNSResolver(new NrIceResolver()),
mUuidGen(MakeUnique<PCUuidGenerator>()),
mMainThread(mParent->GetMainThread()),
mSTSThread(mParent->GetSTSThread()),
mProxyResolveCompleted(false),
mIceRestartState(ICE_RESTART_NONE) {
}
-nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers,
- const std::vector<NrIceTurnServer>& turn_servers,
- NrIceCtx::Policy policy)
+nsresult
+PeerConnectionMedia::InitProxy()
{
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ // Allow mochitests to disable this, since mochitest configures a fake proxy
+ // that serves up content.
+ bool disable = Preferences::GetBool("media.peerconnection.disable_http_proxy",
+ false);
+ if (disable) {
+ mProxyResolveCompleted = true;
+ return NS_OK;
+ }
+#endif
+
nsresult rv;
nsCOMPtr<nsIProtocolProxyService> pps =
do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
CSFLogError(logTag, "%s: Failed to get proxy service: %d", __FUNCTION__, (int)rv);
return NS_ERROR_FAILURE;
}
@@ -299,16 +309,26 @@ nsresult PeerConnectionMedia::Init(const
nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
handler, getter_AddRefs(mProxyRequest));
if (NS_FAILED(rv)) {
CSFLogError(logTag, "%s: Failed to resolve protocol proxy: %d", __FUNCTION__, (int)rv);
return NS_ERROR_FAILURE;
}
+ return NS_OK;
+}
+
+nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers,
+ const std::vector<NrIceTurnServer>& turn_servers,
+ NrIceCtx::Policy policy)
+{
+ nsresult rv = InitProxy();
+ NS_ENSURE_SUCCESS(rv, rv);
+
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false);
#else
bool ice_tcp = false;
#endif
bool default_address_only = GetPrefDefaultAddressOnly();
// TODO(ekr@rtfm.com): need some way to set not offerer later
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -479,16 +479,17 @@ class PeerConnectionMedia : public sigsl
// This passes address, port, level of the default candidate.
sigslot::signal5<const std::string&, uint16_t,
const std::string&, uint16_t, uint16_t>
SignalUpdateDefaultCandidate;
sigslot::signal1<uint16_t>
SignalEndOfLocalCandidates;
private:
+ nsresult InitProxy();
class ProtocolProxyQueryHandler : public nsIProtocolProxyCallback {
public:
explicit ProtocolProxyQueryHandler(PeerConnectionMedia *pcm) :
pcm_(pcm) {}
NS_IMETHODIMP OnProxyAvailable(nsICancelable *request,
nsIChannel *aChannel,
nsIProxyInfo *proxyinfo,
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -895,20 +895,20 @@ class MochitestBase(object):
try:
self.log.info('Stopping ssltunnel')
self.sslTunnel.stop()
except Exception:
self.log.critical('Exception stopping ssltunnel')
if self.websocketProcessBridge is not None:
try:
- self.log.info('Stopping websocketProcessBridge')
self.websocketProcessBridge.kill()
+ self.log.info('Stopping websocket/process bridge')
except Exception:
- self.log.critical('Exception stopping websocketProcessBridge')
+ self.log.critical('Exception stopping websocket/process bridge')
def copyExtraFilesToProfile(self, options):
"Copy extra files or dirs specified on the command line to the testing profile."
for f in options.extraProfileFiles:
abspath = self.getFullPath(f)
if os.path.isfile(abspath):
shutil.copy2(abspath, options.profilePath)
elif os.path.isdir(abspath):