From c12053953f4f6bc6b55a248239ff1650b51ec374 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Sun, 10 Nov 2019 17:11:23 +0000 Subject: [PATCH] Bug 1552176 - Unit test for TRRMode for individual channels r=dragana Differential Revision: https://phabricator.services.mozilla.com/D48362 --HG-- extra : moz-landing-system : lando --- netwerk/test/unit/test_trr.js | 166 +++++++++++++++++++++++++++++----- 1 file changed, 143 insertions(+), 23 deletions(-) diff --git a/netwerk/test/unit/test_trr.js b/netwerk/test/unit/test_trr.js index 85cb4c69a211..3a0ad0fd877f 100644 --- a/netwerk/test/unit/test_trr.js +++ b/netwerk/test/unit/test_trr.js @@ -1,6 +1,8 @@ "use strict"; -const { NodeServer } = ChromeUtils.import("resource://testing-common/httpd.js"); +const { NodeServer, HttpServer } = ChromeUtils.import( + "resource://testing-common/httpd.js" +); const dns = Cc["@mozilla.org/network/dns-service;1"].getService( Ci.nsIDNSService ); @@ -99,6 +101,15 @@ registerCleanupFunction(() => { Services.prefs.clearUserPref("network.dns.native-is-localhost"); }); +// This is an IP that is local, so we don't crash when connecting to it, +// but also connecting to it fails, so we attempt to retry with regular DNS. +const BAD_IP = (() => { + if (mozinfo.os == "linux") { + return "127.9.9.9"; + } + return "0.0.0.0"; +})(); + class DNSListener { constructor(name, expectedAnswer, expectedSuccess = true) { this.name = name; @@ -144,27 +155,139 @@ class DNSListener { return this.promise.then.apply(this.promise, arguments); } } - -Cu.importGlobalProperties(["fetch"]); - add_task(async function test0_nodeExecute() { // This test checks that moz-http2.js running in node is working. // This should always be the first test in this file (except for setup) // otherwise we may encounter random failures when the http2 server is down. - let env = Cc["@mozilla.org/process/environment;1"].getService( - Ci.nsIEnvironment - ); - let execPort = env.get("MOZNODE_EXEC_PORT"); - await fetch(`http://127.0.0.1:${execPort}/test`) - .then(response => { - ok(true, "NodeServer is working"); - }) - .catch(e => { - ok(false, `There was an error ${e}`); - }); + await NodeServer.execute("bad_id", `"hello"`) + .then(() => ok(false, "expecting to throw")) + .catch(e => equal(e.message, "Error: could not find id")); }); +function makeChan(url, mode) { + let chan = NetUtil.newChannel({ + uri: url, + loadUsingSystemPrincipal: true, + }).QueryInterface(Ci.nsIHttpChannel); + chan.setTRRMode(mode); + return chan; +} + +add_task( + { skip_if: () => mozinfo.os == "mac" }, + async function test_trr_flags() { + let httpserv = new HttpServer(); + httpserv.registerPathHandler("/", function handler(metadata, response) { + let content = "ok"; + response.setHeader("Content-Length", `${content.length}`); + response.bodyOutputStream.write(content, content.length); + }); + httpserv.start(-1); + const URL = `http://example.com:${httpserv.identity.primaryPort}/`; + + dns.clearCache(true); + Services.prefs.setCharPref( + "network.trr.uri", + `https://localhost:${h2Port}/doh?responseIP=${BAD_IP}` + ); + + Services.prefs.setIntPref("network.trr.mode", 0); + dns.clearCache(true); + let chan = makeChan(URL, Ci.nsIRequest.TRR_DEFAULT_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + equal(chan.getTRRMode(), Ci.nsIRequest.TRR_DEFAULT_MODE); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_DISABLED_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + equal(chan.getTRRMode(), Ci.nsIRequest.TRR_DISABLED_MODE); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_FIRST_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + equal(chan.getTRRMode(), Ci.nsIRequest.TRR_FIRST_MODE); + dns.clearCache(true); + chan = makeChan( + `http://example.com:${httpserv.identity.primaryPort}/`, + Ci.nsIRequest.TRR_ONLY_MODE + ); + // Should fail as it tries to connect to local but unavailable IP + await new Promise(resolve => + chan.asyncOpen(new ChannelListener(resolve, null, CL_EXPECT_FAILURE)) + ); + equal(chan.getTRRMode(), Ci.nsIRequest.TRR_ONLY_MODE); + + dns.clearCache(true); + Services.prefs.setCharPref( + "network.trr.uri", + `https://localhost:${h2Port}/doh?responseIP=${BAD_IP}` + ); + Services.prefs.setIntPref("network.trr.mode", 2); + + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_DEFAULT_MODE); + // Does get the IP from TRR, but failure means it falls back to DNS. + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_DISABLED_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + dns.clearCache(true); + // Does get the IP from TRR, but failure means it falls back to DNS. + chan = makeChan(URL, Ci.nsIRequest.TRR_FIRST_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_ONLY_MODE); + await new Promise(resolve => + chan.asyncOpen(new ChannelListener(resolve, null, CL_EXPECT_FAILURE)) + ); + + dns.clearCache(true); + Services.prefs.setCharPref( + "network.trr.uri", + `https://localhost:${h2Port}/doh?responseIP=${BAD_IP}` + ); + Services.prefs.setIntPref("network.trr.mode", 3); + + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_DEFAULT_MODE); + await new Promise(resolve => + chan.asyncOpen(new ChannelListener(resolve, null, CL_EXPECT_FAILURE)) + ); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_DISABLED_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_FIRST_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_ONLY_MODE); + await new Promise(resolve => + chan.asyncOpen(new ChannelListener(resolve, null, CL_EXPECT_FAILURE)) + ); + + dns.clearCache(true); + Services.prefs.setIntPref("network.trr.mode", 5); + Services.prefs.setCharPref( + "network.trr.uri", + `https://localhost:${h2Port}/doh?responseIP=1.1.1.1` + ); + + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_DEFAULT_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_DISABLED_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_FIRST_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + dns.clearCache(true); + chan = makeChan(URL, Ci.nsIRequest.TRR_ONLY_MODE); + await new Promise(resolve => chan.asyncOpen(new ChannelListener(resolve))); + + await new Promise(resolve => httpserv.stop(resolve)); + } +); + // verify basic A record add_task(async function test1() { dns.clearCache(true); @@ -1018,9 +1141,9 @@ add_task(async function test_connection_closed_no_bootstrap_localhost() { }); add_task(async function test_connection_closed_no_bootstrap_no_excluded() { - // This test exists to document what happens when we're in TRR only mode - // and we don't set a bootstrap address. We use DNS to resolve the - // initial URI, but if the connection fails, we don't fallback to DNS + // This test makes sure that even in mode 3 without a bootstrap address + // we are able to restart the TRR connection if it drops - the TRR service + // channel will use regular DNS to resolve the TRR address. dns.clearCache(true); Services.prefs.setIntPref("network.trr.mode", 3); // TRR-only Services.prefs.setCharPref("network.trr.excluded-domains", ""); @@ -1040,11 +1163,8 @@ add_task(async function test_connection_closed_no_bootstrap_no_excluded() { !Components.isSuccessCode(inStatus), `${inStatus} should be an error code` ); - [, , inStatus] = await new DNSListener("bar2.example.com", undefined, false); - Assert.ok( - !Components.isSuccessCode(inStatus), - `${inStatus} should be an error code` - ); + dns.clearCache(true); + await new DNSListener("bar2.example.com", "3.3.3.3"); }); add_task(async function test_connection_closed_trr_first() {