test_bug1948203.js (5881B)
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 let trrServer; 12 let h3Port; 13 let host; 14 15 add_setup(async function setup() { 16 trr_test_setup(); 17 18 h3Port = Services.env.get("MOZHTTP3_PORT_ECH"); 19 Assert.notEqual(h3Port, null); 20 Assert.notEqual(h3Port, ""); 21 22 host = `https://alt1.example.com:${h3Port}/`; 23 24 Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST); 25 }); 26 27 registerCleanupFunction(async () => { 28 trr_clear_prefs(); 29 if (trrServer) { 30 await trrServer.stop(); 31 } 32 }); 33 34 function makeChan(url) { 35 let chan = NetUtil.newChannel({ 36 uri: url, 37 loadUsingSystemPrincipal: true, 38 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, 39 }).QueryInterface(Ci.nsIHttpChannel); 40 return chan; 41 } 42 43 function channelOpenPromise(chan, flags) { 44 return new Promise(resolve => { 45 function finish(req, buffer) { 46 resolve([req, buffer]); 47 } 48 chan.asyncOpen(new ChannelListener(finish, null, flags)); 49 }); 50 } 51 52 function ActivityObserver() { 53 this.activites = []; 54 } 55 56 ActivityObserver.prototype = { 57 activites: [], 58 observeActivity( 59 aHttpChannel, 60 aActivityType, 61 aActivitySubtype, 62 aTimestamp, 63 aExtraSizeData, 64 aExtraStringData 65 ) { 66 try { 67 aHttpChannel.QueryInterface(Ci.nsINullChannel); 68 aHttpChannel.QueryInterface(Ci.nsIChannel); 69 if (aHttpChannel.URI.spec === host) { 70 dump( 71 "*** HTTP Activity 0x" + 72 aActivityType.toString(16) + 73 " 0x" + 74 aActivitySubtype.toString(16) + 75 " " + 76 aExtraStringData + 77 "\n" 78 ); 79 this.activites.push({ host, subType: aActivitySubtype }); 80 } 81 } catch (e) {} 82 }, 83 }; 84 85 function checkHttpActivities(activites, expected) { 86 let foundTransClosed = false; 87 for (let activity of activites) { 88 switch (activity.subType) { 89 case Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE: 90 foundTransClosed = true; 91 break; 92 default: 93 break; 94 } 95 } 96 97 Assert.equal( 98 foundTransClosed, 99 expected, 100 "The activity of speculative transaction should match" 101 ); 102 } 103 // Test steps: 104 // 1. Create a TRR server that serves the HTTPS record for the H3 server. 105 // 2. Create a channel and connect to the H3 server. 106 // 3. Use nsIHttpActivityObserver to observe if we receive the HTTP activity 107 // that is sent from the speculative transaction. 108 async function doTestAltSVCWithHTTPSRR(foundActivity) { 109 trrServer = new TRRServer(); 110 await trrServer.start(); 111 112 let observerService = Cc[ 113 "@mozilla.org/network/http-activity-distributor;1" 114 ].getService(Ci.nsIHttpActivityDistributor); 115 116 Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY); 117 Services.prefs.setCharPref( 118 "network.trr.uri", 119 `https://foo.example.com:${trrServer.port()}/dns-query` 120 ); 121 122 let portPrefixedName = `_${h3Port}._https.alt1.example.com`; 123 let vals = [ 124 { key: "alpn", value: "h3" }, 125 { key: "port", value: h3Port }, 126 ]; 127 128 await trrServer.registerDoHAnswers(portPrefixedName, "HTTPS", { 129 answers: [ 130 { 131 name: portPrefixedName, 132 ttl: 55, 133 type: "HTTPS", 134 flush: false, 135 data: { 136 priority: 1, 137 name: ".", 138 values: vals, 139 }, 140 }, 141 ], 142 }); 143 144 await trrServer.registerDoHAnswers("alt1.example.com", "A", { 145 answers: [ 146 { 147 name: "alt1.example.com", 148 ttl: 55, 149 type: "A", 150 flush: false, 151 data: "127.0.0.1", 152 }, 153 ], 154 }); 155 156 let chan = makeChan(`${host}alt_svc_header`); 157 let h3AltSvc = ":" + h3Port; 158 chan.setRequestHeader("x-altsvc", h3AltSvc, false); 159 let observer = new ActivityObserver(); 160 161 let responseObserver = { 162 QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), 163 observe(aSubject, aTopic) { 164 let channel = aSubject.QueryInterface(Ci.nsIChannel); 165 if ( 166 aTopic == "http-on-examine-response" && 167 channel.URI.spec === `${host}alt_svc_header` 168 ) { 169 Services.obs.removeObserver( 170 responseObserver, 171 "http-on-examine-response" 172 ); 173 174 observerService.addObserver(observer); 175 channel.suspend(); 176 // We need to close all connections here, otherwise we are not allowed 177 // to create a specul connection to validate the Alt-svc header. 178 Services.obs.notifyObservers(null, "net:cancel-all-connections"); 179 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 180 setTimeout(function () { 181 channel.resume(); 182 }, 3000); 183 } 184 }, 185 }; 186 Services.obs.addObserver(responseObserver, "http-on-examine-response"); 187 188 await channelOpenPromise(chan); 189 190 // Some dekay here to collect HTTP activites. 191 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 192 await new Promise(resolve => setTimeout(resolve, 3000)); 193 194 checkHttpActivities(observer.activites, foundActivity); 195 196 await trrServer.stop(); 197 observerService.removeObserver(observer); 198 } 199 200 add_task(async function testAltSVCWithHTTPSRR() { 201 Services.prefs.setBoolPref( 202 "network.http.skip_alt_svc_validation_on_https_rr", 203 false 204 ); 205 206 await doTestAltSVCWithHTTPSRR(true); 207 208 // Clear the alt-svc mapping. 209 Services.obs.notifyObservers(null, "last-pb-context-exited"); 210 Services.obs.notifyObservers(null, "net:cancel-all-connections"); 211 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 212 await new Promise(resolve => setTimeout(resolve, 3000)); 213 214 Services.prefs.setBoolPref( 215 "network.http.skip_alt_svc_validation_on_https_rr", 216 true 217 ); 218 219 await doTestAltSVCWithHTTPSRR(false); 220 });