test_cors_preflight_dns_cache.js (5835B)
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 var { setTimeout } = ChromeUtils.importESModule( 8 "resource://gre/modules/Timer.sys.mjs" 9 ); 10 11 const { NodeHTTP2Server } = ChromeUtils.importESModule( 12 "resource://testing-common/NodeServer.sys.mjs" 13 ); 14 15 /* import-globals-from head_cache.js */ 16 /* import-globals-from head_cookies.js */ 17 /* import-globals-from head_channels.js */ 18 19 function channelOpenPromise(chan, flags) { 20 return new Promise(resolve => { 21 function finish(req, buffer) { 22 resolve([req, buffer]); 23 } 24 chan.asyncOpen(new ChannelListener(finish, null, flags)); 25 }); 26 } 27 28 let trrServer; 29 let server; 30 let preflightCache; 31 32 add_setup(async function () { 33 trr_test_setup(); 34 35 trrServer = new TRRServer(); 36 await trrServer.start(); 37 38 await trrServer.registerDoHAnswers("alt1.example.com", "A", { 39 answers: [ 40 { 41 name: "alt1.example.com", 42 ttl: 55, 43 type: "A", 44 flush: false, 45 data: "127.0.0.1", 46 }, 47 ], 48 }); 49 await trrServer.registerDoHAnswers("alt2.example.com", "A", { 50 answers: [ 51 { 52 name: "alt2.example.com", 53 ttl: 55, 54 type: "A", 55 flush: false, 56 data: "127.0.0.1", 57 }, 58 ], 59 }); 60 61 Services.prefs.setIntPref("network.trr.mode", 3); 62 Services.prefs.setCharPref( 63 "network.trr.uri", 64 `https://foo.example.com:${trrServer.port()}/dns-query` 65 ); 66 67 preflightCache = Cc["@mozilla.org/network/cors-preflight-cache;1"].getService( 68 Ci.nsICORSPreflightCache 69 ); 70 71 server = new NodeHTTP2Server(); 72 await server.start(); 73 await server.registerPathHandler("/", (req, resp) => { 74 resp.setHeader("Access-Control-Allow-Methods", "PUT"); 75 resp.setHeader("Access-Control-Allow-Origin", "https://example.org"); 76 resp.writeHead(200); 77 resp.end(global.server_name); 78 }); 79 80 registerCleanupFunction(async () => { 81 trr_clear_prefs(); 82 if (trrServer) { 83 await trrServer.stop(); 84 } 85 await server.stop(); 86 }); 87 }); 88 89 function createCORSRequest(corsURI) { 90 let uri = NetUtil.newURI(corsURI); 91 let principal = Services.scriptSecurityManager.createContentPrincipal(uri, { 92 firstPartyDomain: "https://example.org", 93 }); 94 let channel = NetUtil.newChannel({ 95 uri, 96 loadingPrincipal: principal, 97 securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT, 98 contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, 99 }).QueryInterface(Ci.nsIHttpChannel); 100 channel.requestMethod = "PUT"; 101 let triggeringPrincipal = 102 Services.scriptSecurityManager.createContentPrincipal( 103 NetUtil.newURI("https://example.org/"), 104 { 105 firstPartyDomain: "https://example.org", 106 } 107 ); 108 109 channel.loadInfo.setTriggeringPrincipalForTesting(triggeringPrincipal); 110 return [channel, principal]; 111 } 112 113 async function runCORSTest({ 114 uri, 115 expectedPreflightCount, 116 beforeSecondRequest = () => {}, 117 }) { 118 let preflightRequestCount = 0; 119 120 const observer = { 121 QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), 122 observe(aSubject, aTopic) { 123 aSubject = aSubject.QueryInterface(Ci.nsIHttpChannel); 124 if (aTopic === "http-on-before-connect" && aSubject.URI.spec === uri) { 125 dump("aSubject.requestMethod:" + aSubject.requestMethod + "\n"); 126 if (aSubject.requestMethod === "OPTIONS") { 127 preflightRequestCount++; 128 } 129 } 130 }, 131 }; 132 133 Services.obs.addObserver(observer, "http-on-before-connect"); 134 135 // First request 136 let [channel, principal] = createCORSRequest(uri); 137 await channelOpenPromise(channel, CL_ALLOW_UNKNOWN_CL); 138 139 let entries = preflightCache.getEntries(principal); 140 Assert.equal(entries.length, 1); 141 Assert.equal(entries[0].URI.asciiSpec, uri); 142 143 await beforeSecondRequest(); 144 145 // Second request 146 let [secondChannel] = createCORSRequest(uri); 147 await channelOpenPromise(secondChannel, CL_ALLOW_UNKNOWN_CL); 148 149 Assert.equal(preflightRequestCount, expectedPreflightCount); 150 151 Services.obs.removeObserver(observer, "http-on-before-connect"); 152 } 153 154 add_task(async function test_cors_with_valid_dns_cache() { 155 const corsURI = `https://alt1.example.com:${server.port()}/`; 156 await runCORSTest({ 157 uri: corsURI, 158 expectedPreflightCount: 1, 159 }); 160 }); 161 162 add_task(async function test_cors_without_valid_dns_cache() { 163 const corsURI = `https://alt2.example.com:${server.port()}/`; 164 Services.dns.clearCache(true); // clear before first request 165 await runCORSTest({ 166 uri: corsURI, 167 expectedPreflightCount: 2, 168 beforeSecondRequest: async () => { 169 Services.dns.clearCache(true); 170 }, 171 }); 172 }); 173 174 add_task(async function test_cors_with_dns_cache_changed() { 175 const corsURI = `https://alt2.example.com:${server.port()}/`; 176 Services.dns.clearCache(true); // clear before first request 177 await runCORSTest({ 178 uri: corsURI, 179 expectedPreflightCount: 2, 180 beforeSecondRequest: async () => { 181 Services.dns.clearCache(true); 182 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 183 await new Promise(resolve => setTimeout(resolve, 500)); 184 185 await trrServer.registerDoHAnswers("alt2.example.com", "A", { 186 answers: [ 187 { 188 name: "alt2.example.com", 189 ttl: 55, 190 type: "A", 191 flush: false, 192 data: "127.0.0.1", 193 }, 194 { 195 name: "alt2.example.com", 196 ttl: 55, 197 type: "A", 198 flush: false, 199 data: "127.0.0.3", 200 }, 201 ], 202 }); 203 204 const oa = { 205 firstPartyDomain: "https://example.org", 206 }; 207 await new TRRDNSListener("alt2.example.com", { 208 expectedAnswer: "127.0.0.1", 209 originAttributes: oa, 210 }); 211 }, 212 }); 213 });