test_http3_network_change.js (5189B)
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 const mockNetwork = Cc[ 11 "@mozilla.org/network/mock-network-controller;1" 12 ].getService(Ci.nsIMockNetworkLayerController); 13 14 let h3Port; 15 16 const certOverrideService = Cc[ 17 "@mozilla.org/security/certoverride;1" 18 ].getService(Ci.nsICertOverrideService); 19 20 function setup() { 21 h3Port = Services.env.get("MOZHTTP3_PORT"); 22 Assert.notEqual(h3Port, null); 23 Assert.notEqual(h3Port, ""); 24 Services.prefs.setBoolPref("network.socket.attach_mock_network_layer", true); 25 } 26 27 setup(); 28 registerCleanupFunction(async () => { 29 Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr"); 30 Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc"); 31 Services.prefs.clearUserPref("network.dns.echconfig.enabled"); 32 Services.prefs.clearUserPref( 33 "network.dns.echconfig.fallback_to_origin_when_all_failed" 34 ); 35 Services.prefs.clearUserPref("network.dns.httpssvc.reset_exclustion_list"); 36 Services.prefs.clearUserPref("network.http.http3.enable"); 37 Services.prefs.clearUserPref( 38 "network.dns.httpssvc.http3_fast_fallback_timeout" 39 ); 40 Services.prefs.clearUserPref( 41 "network.http.http3.alt-svc-mapping-for-testing" 42 ); 43 Services.prefs.clearUserPref("network.dns.localDomains"); 44 Services.prefs.clearUserPref("network.http.http3.use_nspr_for_io"); 45 Services.prefs.clearUserPref("network.dns.preferIPv6"); 46 Services.prefs.clearUserPref( 47 "network.http.move_to_pending_list_after_network_change" 48 ); 49 }); 50 51 function makeChan(url) { 52 let chan = NetUtil.newChannel({ 53 uri: url, 54 loadUsingSystemPrincipal: true, 55 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, 56 }).QueryInterface(Ci.nsIHttpChannel); 57 chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI; 58 return chan; 59 } 60 61 function channelOpenPromise(chan, flags) { 62 return new Promise(resolve => { 63 function finish(req, buffer) { 64 resolve([req, buffer]); 65 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 66 false 67 ); 68 } 69 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 70 true 71 ); 72 chan.asyncOpen(new ChannelListener(finish, null, flags)); 73 }); 74 } 75 76 // Test the failure of an HTTP/3 connection when a network change occurs, 77 // specifically ensuring that blocking UDP I/O leads to the expected 78 // connection failure. 79 add_task(async function test_h3_with_network_change_fail() { 80 Services.prefs.setBoolPref("network.http.http3.use_nspr_for_io", true); 81 Services.prefs.setBoolPref( 82 "network.http.move_to_pending_list_after_network_change", 83 false 84 ); 85 await http3_setup_tests("h3"); 86 87 let chan = makeChan(`https://foo.example.com:${h3Port}`); 88 let [req] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL); 89 req.QueryInterface(Ci.nsIHttpChannel); 90 Assert.equal(req.protocolVersion, "h3"); 91 92 // Blocking the UDP I/O to simulate a network change. 93 let addr = mockNetwork.createScriptableNetAddr("127.0.0.1", h3Port); 94 mockNetwork.blockUDPAddrIO(addr); 95 registerCleanupFunction(async () => { 96 mockNetwork.clearBlockedUDPAddr(); 97 }); 98 99 Services.obs.notifyObservers(null, "network:link-status-changed", "changed"); 100 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 101 await new Promise(resolve => setTimeout(resolve, 1000)); 102 103 // This request will fail because the HTTP/3 connection is reused. 104 chan = makeChan(`https://foo.example.com:${h3Port}`); 105 [req] = await channelOpenPromise(chan, CL_EXPECT_FAILURE); 106 }); 107 108 add_task(async function test_h3_with_network_change_success() { 109 Services.prefs.setBoolPref("network.http.http3.use_nspr_for_io", true); 110 Services.prefs.setBoolPref( 111 "network.http.move_to_pending_list_after_network_change", 112 true 113 ); 114 115 h3Port = await create_h3_server(); 116 Services.prefs.setCharPref( 117 "network.http.http3.alt-svc-mapping-for-testing", 118 `foo.example.com;h3=:${h3Port}` 119 ); 120 121 let chan = makeChan(`https://foo.example.com:${h3Port}`); 122 let [req] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL); 123 req.QueryInterface(Ci.nsIHttpChannel); 124 Assert.equal(req.protocolVersion, "h3"); 125 126 let addr = mockNetwork.createScriptableNetAddr("127.0.0.1", h3Port); 127 mockNetwork.blockUDPAddrIO(addr); 128 registerCleanupFunction(async () => { 129 mockNetwork.clearBlockedUDPAddr(); 130 }); 131 132 Services.obs.notifyObservers(null, "network:link-status-changed", "changed"); 133 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 134 await new Promise(resolve => setTimeout(resolve, 1000)); 135 136 // Make sure the host name can be resolved to an IPv6 address, since 137 // 127.0.0.1 is blocked. 138 Services.prefs.setBoolPref("network.dns.preferIPv6", true); 139 Services.prefs.setBoolPref("network.dns.disableIPv6", false); 140 Services.dns.clearCache(true); 141 142 chan = makeChan(`https://foo.example.com:${h3Port}`); 143 [req] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL); 144 Assert.equal(req.protocolVersion, "h3"); 145 });