test_essential_domain_fallback.js (6892B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 const lazy = {}; 6 ChromeUtils.defineESModuleGetters(lazy, { 7 AddonSettings: "resource://gre/modules/addons/AddonSettings.sys.mjs", 8 CertUtils: "resource://gre/modules/CertUtils.sys.mjs", 9 ServiceRequest: "resource://gre/modules/ServiceRequest.sys.mjs", 10 }); 11 12 const { NodeHTTPSServer } = ChromeUtils.importESModule( 13 "resource://testing-common/NodeServer.sys.mjs" 14 ); 15 16 function waitForNotificationPromise(notification) { 17 return new Promise(resolve => { 18 function observer(aSubject, _aTopic, _aData) { 19 info(aSubject); 20 Services.obs.removeObserver(observer, notification); 21 resolve(aSubject); 22 } 23 Services.obs.addObserver(observer, notification); 24 }); 25 } 26 27 function openChannelPromise(url, options = { loadUsingSystemPrincipal: true }) { 28 let uri = Services.io.newURI(url); 29 options.uri = uri; 30 let chan = NetUtil.newChannel(options); 31 let flags = CL_ALLOW_UNKNOWN_CL; 32 if (options.expectFailure) { 33 flags |= CL_EXPECT_FAILURE; 34 } 35 return new Promise(resolve => { 36 chan.asyncOpen( 37 new ChannelListener((req, buf) => resolve({ req, buf }), null, flags) 38 ); 39 }); 40 } 41 42 let backupServer; 43 const override = Cc["@mozilla.org/network/native-dns-override;1"].getService( 44 Ci.nsINativeDNSResolverOverride 45 ); 46 47 add_setup(async function setup() { 48 let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( 49 Ci.nsIX509CertDB 50 ); 51 addCertFromFile(certdb, "../unit/http2-ca.pem", "CTu,u,u"); 52 Services.prefs.setBoolPref("network.essential_domains_fallback", true); 53 54 backupServer = new NodeHTTPSServer(); 55 await backupServer.start(); 56 registerCleanupFunction(async () => { 57 await backupServer.stop(); 58 }); 59 await backupServer.registerPathHandler("/stuff", (req, res) => { 60 res.end("Good stuff"); 61 }); 62 63 // Just so we can simulate mapping the default HTTPS port and domain. 64 Services.prefs.setStringPref( 65 "network.socket.forcePort", 66 `443=${backupServer.port()}` 67 ); 68 69 Services.io.addEssentialDomainMapping("aus5.mozilla.org", "foo.example.com"); 70 71 let ncs = Cc[ 72 "@mozilla.org/network/network-connectivity-service;1" 73 ].getService(Ci.nsINetworkConnectivityService); 74 ncs.IPv4 = Ci.nsINetworkConnectivityService.OK; 75 }); 76 77 add_task(async function test_fallback_on_dns_failure() { 78 Services.fog.testResetFOG(); 79 Services.obs.notifyObservers(null, "net:cancel-all-connections"); 80 Services.dns.clearCache(true); 81 override.addIPOverride("aus5.mozilla.org", "N/A"); 82 override.addIPOverride("foo.example.com", "127.0.0.1"); 83 84 let { buf } = await openChannelPromise("https://aus5.mozilla.org/stuff"); 85 equal(buf, "Good stuff"); 86 87 equal( 88 await Glean.network.systemChannelUpdateStatus.dns.testGetValue(), 89 1, 90 "Expecting one failed request due to DNS" 91 ); 92 equal( 93 await Glean.network.retriedSystemChannelUpdateStatus.ok.testGetValue(), 94 1, 95 "Expecting retried update request to succeed" 96 ); 97 }); 98 99 add_task(async function test_dns_xhr() { 100 Services.dns.clearCache(true); 101 override.clearOverrides(); 102 override.addIPOverride("aus5.mozilla.org", "N/A"); 103 override.addIPOverride("foo.example.com", "127.0.0.1"); 104 105 let text = await new Promise((resolve, reject) => { 106 let request = new XMLHttpRequest({ mozAnon: true, mozSystem: true }); 107 request.open("GET", `https://aus5.mozilla.org/stuff`, true); 108 request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; 109 // Prevent the request from writing to cache. 110 request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; 111 request.overrideMimeType("text/plain"); 112 request.timeout = 5000; 113 request.addEventListener("load", () => resolve(request.responseText)); 114 request.addEventListener("error", reject); 115 request.addEventListener("timeout", reject); 116 request.send(null); 117 }); 118 equal(text, "Good stuff"); 119 }); 120 121 add_task(async function test_dns_service_request() { 122 Services.dns.clearCache(true); 123 override.clearOverrides(); 124 override.addIPOverride("aus5.mozilla.org", "N/A"); 125 override.addIPOverride("foo.example.com", "127.0.0.1"); 126 127 let text = await new Promise((resolve, reject) => { 128 let request = new lazy.ServiceRequest({ mozAnon: true }); 129 request.open("GET", `https://aus5.mozilla.org/stuff`, true); 130 request.channel.notificationCallbacks = new lazy.CertUtils.BadCertHandler( 131 !lazy.AddonSettings.UPDATE_REQUIREBUILTINCERTS 132 ); 133 request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; 134 // Prevent the request from writing to cache. 135 request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; 136 request.overrideMimeType("text/plain"); 137 request.timeout = 5000; 138 request.addEventListener("load", () => resolve(request.responseText)); 139 request.addEventListener("error", reject); 140 request.addEventListener("timeout", reject); 141 request.send(null); 142 }); 143 equal(text, "Good stuff"); 144 }); 145 146 add_task(async function test_fallback_on_tls_failure() { 147 Services.obs.notifyObservers(null, "net:cancel-all-connections"); 148 Services.dns.clearCache(true); 149 150 override.clearOverrides(); 151 override.addIPOverride("aus5.mozilla.org", "127.0.0.1"); 152 override.addIPOverride("foo.example.com", "127.0.0.1"); 153 let { buf } = await openChannelPromise("https://aus5.mozilla.org/stuff"); 154 equal(buf, "Good stuff"); 155 }); 156 157 add_task(async function test_no_fallback_with_content_principal() { 158 Services.obs.notifyObservers(null, "net:cancel-all-connections"); 159 Services.dns.clearCache(true); 160 161 let { req } = await openChannelPromise("https://aus5.mozilla.org/stuff", { 162 loadingPrincipal: Services.scriptSecurityManager.createContentPrincipal( 163 Services.io.newURI("https://aus5.mozilla.org"), 164 {} 165 ), 166 securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT, 167 contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, 168 expectFailure: true, 169 }); 170 // "NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY, SSL_ERROR_BAD_CERT_DOMAIN)" 171 equal(req.status, 0x805a2ff4); 172 }); 173 174 add_task(async function test_fallback_on_connection_failure() { 175 Services.obs.notifyObservers(null, "net:cancel-all-connections"); 176 Services.dns.clearCache(true); 177 178 Services.prefs.setIntPref("network.http.connection-timeout", 1); 179 override.clearOverrides(); 180 override.addIPOverride("aus5.mozilla.org", "10.99.99.99"); // Local IP that doesn't exist (hopefully). 181 override.addIPOverride("foo.example.com", "127.0.0.1"); 182 183 await new Promise(resolve => do_timeout(100, resolve)); 184 let chanNotif = waitForNotificationPromise("httpchannel-fallback"); 185 let chanPromise = openChannelPromise("https://aus5.mozilla.org/stuff"); 186 let chan = await chanNotif; 187 equal(chan.status, Cr.NS_ERROR_NET_TIMEOUT); 188 let { buf } = await chanPromise; 189 equal(buf, "Good stuff"); 190 });