browser_essential_domain_fallbacks.js (5587B)
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 "use strict"; 6 7 ChromeUtils.defineESModuleGetters(this, { 8 RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", 9 EssentialDomainsRemoteSettings: 10 "resource://gre/modules/EssentialDomainsRemoteSettings.sys.mjs", 11 ESSENTIAL_DOMAINS_REMOTE_BUCKET: 12 "resource://gre/modules/EssentialDomainsRemoteSettings.sys.mjs", 13 }); 14 15 const lazy = {}; 16 ChromeUtils.defineESModuleGetters(lazy, { 17 AddonSettings: "resource://gre/modules/addons/AddonSettings.sys.mjs", 18 CertUtils: "resource://gre/modules/CertUtils.sys.mjs", 19 ServiceRequest: "resource://gre/modules/ServiceRequest.sys.mjs", 20 }); 21 22 const { sinon } = ChromeUtils.importESModule( 23 "resource://testing-common/Sinon.sys.mjs" 24 ); 25 26 const override = Cc["@mozilla.org/network/native-dns-override;1"].getService( 27 Ci.nsINativeDNSResolverOverride 28 ); 29 30 const EXPECTED_RESPONSE = "<html><body>\n</body></html>\n"; 31 32 add_setup(async function () { 33 await SpecialPowers.pushPrefEnv({ 34 set: [ 35 ["network.essential_domains_fallback", true], 36 ["network.proxy.no_proxies_on", "aus5.mozilla.org"], 37 ], 38 }); 39 40 // Pretend this domain blocked at the DNS layer. 41 override.addIPOverride("aus5.mozilla.org", "N/A"); 42 registerCleanupFunction(async () => { 43 override.clearOverrides(); 44 }); 45 46 let ncs = Cc[ 47 "@mozilla.org/network/network-connectivity-service;1" 48 ].getService(Ci.nsINetworkConnectivityService); 49 ncs.IPv4 = Ci.nsINetworkConnectivityService.OK; 50 51 const settings = await RemoteSettings(ESSENTIAL_DOMAINS_REMOTE_BUCKET); 52 let stub = sinon.stub(settings, "get").returns(newData); 53 registerCleanupFunction(async function () { 54 stub.restore(); 55 }); 56 57 await RemoteSettings(ESSENTIAL_DOMAINS_REMOTE_BUCKET).emit("sync", {}); 58 }); 59 60 let newData = [ 61 { 62 id: "111", 63 from: "aus5.mozilla.org", 64 to: "test1.example.com", 65 }, 66 ]; 67 68 function read_stream(stream, count) { 69 /* assume stream has non-ASCII data */ 70 var wrapper = Cc["@mozilla.org/binaryinputstream;1"].createInstance( 71 Ci.nsIBinaryInputStream 72 ); 73 wrapper.setInputStream(stream); 74 /* JS methods can be called with a maximum of 65535 arguments, and input 75 streams don't have to return all the data they make .available() when 76 asked to .read() that number of bytes. */ 77 var data = []; 78 while (count > 0) { 79 var bytes = wrapper.readByteArray(Math.min(65535, count)); 80 data.push(String.fromCharCode.apply(null, bytes)); 81 count -= bytes.length; 82 if (!bytes.length) { 83 throw new Error("Nothing read from input stream!"); 84 } 85 } 86 return data.join(""); 87 } 88 89 class SimpleChannelListener { 90 constructor(callback) { 91 this._onStopCallback = callback; 92 this._buffer = ""; 93 } 94 get QueryInterface() { 95 return ChromeUtils.generateQI(["nsIStreamListener", "nsIRequestObserver"]); 96 } 97 98 onStartRequest() {} 99 100 onDataAvailable(request, stream, offset, count) { 101 this._buffer = this._buffer.concat(read_stream(stream, count)); 102 } 103 104 onStopRequest(request) { 105 if (this._onStopCallback) { 106 this._onStopCallback(request, this._buffer); 107 } 108 } 109 } 110 111 function openChannelPromise(url, options = { loadUsingSystemPrincipal: true }) { 112 let uri = Services.io.newURI(url); 113 options.uri = uri; 114 let chan = NetUtil.newChannel(options); 115 return new Promise(resolve => { 116 chan.asyncOpen( 117 new SimpleChannelListener((req, buf) => resolve({ req, buf })) 118 ); 119 }); 120 } 121 122 add_task(async function test_channel_fallback_on_dns() { 123 // The host should be replaced with test1.example.com 124 // if the original channel fails and it's in the remote settings payload. 125 let { req, buf } = await openChannelPromise( 126 "https://aus5.mozilla.org/browser/netwerk/cookie/test/browser/file_empty.html" 127 ); 128 Assert.equal(buf, EXPECTED_RESPONSE); 129 Assert.equal(req.URI.host, "test1.example.com"); 130 }); 131 132 add_task(async function test_service_request_fallback() { 133 let request = await new Promise((resolve, reject) => { 134 let request = new lazy.ServiceRequest({ mozAnon: true }); 135 request.open( 136 "GET", 137 `https://aus5.mozilla.org/browser/netwerk/cookie/test/browser/file_empty.html`, 138 true 139 ); 140 request.channel.notificationCallbacks = new lazy.CertUtils.BadCertHandler( 141 !lazy.AddonSettings.UPDATE_REQUIREBUILTINCERTS 142 ); 143 request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; 144 // Prevent the request from writing to cache. 145 request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; 146 request.overrideMimeType("text/plain"); 147 request.timeout = 5000; 148 request.addEventListener("load", () => resolve(request)); 149 request.addEventListener("error", reject); 150 request.addEventListener("timeout", reject); 151 request.send(null); 152 }); 153 Assert.equal(request.responseText, "<html><body>\n</body></html>\n"); 154 Assert.equal( 155 request.responseURL, 156 "https://test1.example.com/browser/netwerk/cookie/test/browser/file_empty.html" 157 ); 158 }); 159 160 add_task(async function test_fetch_request_fallback() { 161 let response = await fetch( 162 "https://aus5.mozilla.org/browser/netwerk/cookie/test/browser/file_empty.html", 163 { 164 method: "GET", 165 cache: "no-store", // prevent caching, similar to LOAD_BYPASS_CACHE 166 } 167 ); 168 169 let text = await response.text(); 170 171 Assert.equal(text, "<html><body>\n</body></html>\n"); 172 Assert.equal( 173 response.url, 174 "https://test1.example.com/browser/netwerk/cookie/test/browser/file_empty.html" 175 ); 176 });