test_http3_proxy_ipv6_fallback.js (5771B)
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 { Http3ProxyFilter, NodeHTTP2Server, NodeHTTP2ProxyServer } = 12 ChromeUtils.importESModule("resource://testing-common/NodeServer.sys.mjs"); 13 14 let pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(); 15 let trrServer; 16 let proxyFilter; 17 let proxyPort; 18 let proxyHost; 19 let serverPort; 20 21 add_setup(async function setup() { 22 trr_test_setup(); 23 24 if (mozinfo.socketprocess_networking) { 25 Cc["@mozilla.org/network/protocol;1?name=http"].getService( 26 Ci.nsIHttpProtocolHandler 27 ); 28 Services.dns; // Needed to trigger socket process. 29 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 30 await new Promise(resolve => setTimeout(resolve, 1000)); 31 } 32 33 Services.prefs.setBoolPref("network.http.http3.enable", true); 34 Services.prefs.setBoolPref( 35 "network.http.http3.block_loopback_ipv6_addr", 36 true 37 ); 38 Services.prefs.setBoolPref( 39 "network.http.http3.retry_different_ip_family", 40 false 41 ); 42 Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); 43 Services.prefs.setBoolPref("network.dns.get-ttl", false); 44 45 let proxy = new NodeHTTP2ProxyServer(); 46 await proxy.startWithoutProxyFilter(); 47 proxyPort = proxy.port(); 48 proxyHost = "alt2.example.com"; 49 50 trrServer = new TRRServer(); 51 await trrServer.start(); 52 53 Services.prefs.setIntPref("network.trr.mode", 3); 54 Services.prefs.setCharPref( 55 "network.trr.uri", 56 `https://foo.example.com:${trrServer.port()}/dns-query` 57 ); 58 59 await trrServer.registerDoHAnswers(proxyHost, "AAAA", { 60 answers: [ 61 { 62 name: proxyHost, 63 ttl: 55, 64 type: "AAAA", 65 flush: false, 66 data: "::1", 67 }, 68 ], 69 }); 70 await trrServer.registerDoHAnswers(proxyHost, "A", { 71 answers: [ 72 { 73 name: proxyHost, 74 ttl: 55, 75 type: "A", 76 flush: false, 77 data: "127.0.0.1", 78 }, 79 ], 80 }); 81 82 await new TRRDNSListener(proxyHost, "::1"); 83 84 let server = new NodeHTTP2Server(); 85 await server.start(); 86 serverPort = server.port(); 87 info(`server port:${server.port()}`); 88 89 // Register multiple endpoints 90 await server.registerPathHandler("/concurrent1", (req, resp) => { 91 resp.writeHead(200); 92 resp.end("response1"); 93 }); 94 await server.registerPathHandler("/concurrent2", (req, resp) => { 95 resp.writeHead(200); 96 resp.end("response2"); 97 }); 98 await server.registerPathHandler("/concurrent3", (req, resp) => { 99 resp.writeHead(200); 100 resp.end("response3"); 101 }); 102 103 proxyFilter = new Http3ProxyFilter( 104 proxyHost, 105 proxyPort, 106 0, 107 "/.well-known/masque/udp/{target_host}/{target_port}/", 108 "" 109 ); 110 111 registerCleanupFunction(async () => { 112 trr_clear_prefs(); 113 Services.prefs.clearUserPref( 114 "network.http.http3.retry_different_ip_family" 115 ); 116 Services.prefs.clearUserPref("network.http.speculative-parallel-limit"); 117 Services.prefs.clearUserPref("network.http.http3.block_loopback_ipv6_addr"); 118 Services.prefs.clearUserPref("network.dns.get-ttl"); 119 Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); 120 if (trrServer) { 121 await trrServer.stop(); 122 } 123 await proxy.stop(); 124 await server.stop(); 125 }); 126 }); 127 128 function makeChan(url) { 129 let chan = NetUtil.newChannel({ 130 uri: url, 131 loadUsingSystemPrincipal: true, 132 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, 133 }).QueryInterface(Ci.nsIHttpChannel); 134 chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI; 135 return chan; 136 } 137 138 function channelOpenPromise(chan, flags) { 139 // eslint-disable-next-line no-async-promise-executor 140 return new Promise(async resolve => { 141 function finish(req, buffer) { 142 resolve([req, buffer]); 143 } 144 chan.asyncOpen(new ChannelListener(finish, null, flags)); 145 }); 146 } 147 148 // Test if we fallback to HTTP/2 proxy when IPv6 is blocked. 149 add_task(async function test_fallback_with_speculative_connection() { 150 Services.prefs.setIntPref("network.http.speculative-parallel-limit", 6); 151 pps.registerFilter(proxyFilter, 10); 152 153 const promises = []; 154 for (let i = 1; i <= 3; i++) { 155 let chan = makeChan( 156 `https://alt1.example.com:${serverPort}/concurrent${i}` 157 ); 158 promises.push(channelOpenPromise(chan, CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL)); 159 } 160 161 const results = await Promise.all(promises); 162 163 // Verify all requests succeeded with correct responses 164 for (let i = 0; i < 3; i++) { 165 const [req, buf] = results[i]; 166 Assert.equal(req.status, Cr.NS_OK); 167 Assert.equal(buf, `response${i + 1}`); 168 } 169 170 pps.unregisterFilter(proxyFilter); 171 }); 172 173 add_task(async function test_fallback_without_speculative_connection() { 174 Services.prefs.setIntPref("network.http.speculative-parallel-limit", 0); 175 176 Services.obs.notifyObservers(null, "net:cancel-all-connections"); 177 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 178 await new Promise(resolve => setTimeout(resolve, 1000)); 179 180 pps.registerFilter(proxyFilter, 10); 181 182 const promises = []; 183 for (let i = 1; i <= 3; i++) { 184 let chan = makeChan( 185 `https://alt1.example.com:${serverPort}/concurrent${i}` 186 ); 187 promises.push(channelOpenPromise(chan, CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL)); 188 } 189 190 const results = await Promise.all(promises); 191 192 // Verify all requests succeeded with correct responses 193 for (let i = 0; i < 3; i++) { 194 const [req, buf] = results[i]; 195 Assert.equal(req.status, Cr.NS_OK); 196 Assert.equal(buf, `response${i + 1}`); 197 } 198 199 pps.unregisterFilter(proxyFilter); 200 });