Bug 1552176 - Unit test for TRRMode for individual channels r=dragana
☠☠ backed out by 4fb5404dcbef ☠ ☠
authorValentin Gosu <valentin.gosu@gmail.com>
Sun, 10 Nov 2019 17:11:23 +0000
changeset 501425 692e5e51e19ed3ca140656f90acdb96107f01947
parent 501424 e7d2e0642ae035d0bc81a7eab75dbf75511702d8
child 501426 68882d1eccac58bde4e2eba08b799c8ea2fbebfd
push id114170
push usermalexandru@mozilla.com
push dateTue, 12 Nov 2019 21:58:32 +0000
treeherdermozilla-inbound@9e3f44e87a1a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1552176
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1552176 - Unit test for TRRMode for individual channels r=dragana Differential Revision: https://phabricator.services.mozilla.com/D48362
netwerk/test/unit/test_trr.js
--- a/netwerk/test/unit/test_trr.js
+++ b/netwerk/test/unit/test_trr.js
@@ -1,11 +1,13 @@
 "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
 );
 const { MockRegistrar } = ChromeUtils.import(
   "resource://testing-common/MockRegistrar.jsm"
 );
 const mainThread = Services.tm.currentThread;
 
@@ -94,16 +96,25 @@ registerCleanupFunction(() => {
   Services.prefs.clearUserPref("captivedetect.canonicalURL");
 
   Services.prefs.clearUserPref("network.http.spdy.enabled");
   Services.prefs.clearUserPref("network.http.spdy.enabled.http2");
   Services.prefs.clearUserPref("network.dns.localDomains");
   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;
     this.expectedAnswer = expectedAnswer;
     this.expectedSuccess = expectedSuccess;
     this.promise = new Promise(resolve => {
       this.resolve = resolve;
     });
@@ -139,36 +150,148 @@ class DNSListener {
     throw Cr.NS_ERROR_NO_INTERFACE;
   }
 
   // Implement then so we can await this as a promise.
   then() {
     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);
   Services.prefs.setIntPref("network.trr.mode", 2); // TRR-first
   Services.prefs.setCharPref(
     "network.trr.uri",
     `https://foo.example.com:${h2Port}/doh?responseIP=2.2.2.2`
@@ -1013,19 +1136,19 @@ add_task(async function test_connection_
   Assert.ok(
     !Components.isSuccessCode(inStatus),
     `${inStatus} should be an error code`
   );
   await new DNSListener("bar2.example.com", "3.3.3.3");
 });
 
 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", "");
   Services.prefs.setCharPref("network.trr.builtin-excluded-domains", "");
   Services.prefs.setCharPref(
     "network.trr.uri",
     `https://localhost:${h2Port}/doh?responseIP=3.3.3.3`
   );
@@ -1035,21 +1158,18 @@ add_task(async function test_connection_
   await new DNSListener("bar.example.com", "3.3.3.3");
 
   // makes the TRR connection shut down.
   let [, , inStatus] = await new DNSListener("closeme.com", undefined, false);
   Assert.ok(
     !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() {
   // 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
   dns.clearCache(true);
   Services.prefs.setIntPref("network.trr.mode", 2); // TRR-first