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/. */
7 var { setTimeout
} = ChromeUtils
.importESModule(
8 "resource://gre/modules/Timer.sys.mjs"
15 const certOverrideService
= Cc
[
16 "@mozilla.org/security/certoverride;1"
17 ].getService(Ci
.nsICertOverrideService
);
19 add_setup(async
function setup() {
20 h2Port
= Services
.env
.get("MOZHTTP2_PORT");
21 Assert
.notEqual(h2Port
, null);
22 Assert
.notEqual(h2Port
, "");
24 h3Port
= Services
.env
.get("MOZHTTP3_PORT");
25 Assert
.notEqual(h3Port
, null);
26 Assert
.notEqual(h3Port
, "");
30 if (mozinfo
.socketprocess_networking
) {
31 Cc
["@mozilla.org/network/protocol;1?name=http"].getService(
32 Ci
.nsIHttpProtocolHandler
34 Services
.dns
; // Needed to trigger socket process.
35 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
36 await
new Promise(resolve
=> setTimeout(resolve
, 1000));
39 Services
.prefs
.setIntPref("network.trr.mode", 2); // TRR first
40 Services
.prefs
.setBoolPref("network.http.http3.enable", true);
41 Services
.prefs
.setIntPref("network.http.speculative-parallel-limit", 6);
42 Services
.prefs
.setBoolPref(
43 "network.http.http3.block_loopback_ipv6_addr",
46 Services
.prefs
.setBoolPref(
47 "network.http.http3.retry_different_ip_family",
50 Services
.prefs
.setBoolPref("network.dns.get-ttl", false);
52 certOverrideService
.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
56 registerCleanupFunction(async () => {
57 certOverrideService
.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
61 Services
.prefs
.clearUserPref(
62 "network.http.http3.retry_different_ip_family"
64 Services
.prefs
.clearUserPref("network.http.speculative-parallel-limit");
65 Services
.prefs
.clearUserPref("network.http.http3.block_loopback_ipv6_addr");
66 Services
.prefs
.clearUserPref("network.dns.get-ttl");
68 await trrServer
.stop();
73 function makeChan(url
) {
74 let chan
= NetUtil
.newChannel({
76 loadUsingSystemPrincipal
: true,
77 contentPolicyType
: Ci
.nsIContentPolicy
.TYPE_DOCUMENT
,
78 }).QueryInterface(Ci
.nsIHttpChannel
);
79 chan
.loadFlags
= Ci
.nsIChannel
.LOAD_INITIAL_DOCUMENT_URI
;
83 function channelOpenPromise(chan
, flags
) {
84 // eslint-disable-next-line no-async-promise-executor
85 return new Promise(async resolve
=> {
86 function finish(req
, buffer
) {
87 resolve([req
, buffer
]);
89 chan
.asyncOpen(new ChannelListener(finish
, null, flags
));
93 async
function registerDoHAnswers(host
, ipv4Answers
, ipv6Answers
, httpsRecord
) {
94 trrServer
= new TRRServer();
95 await trrServer
.start();
97 Services
.prefs
.setIntPref("network.trr.mode", 3);
98 Services
.prefs
.setCharPref(
100 `https://foo.example.com:${trrServer.port()}/dns-query`
103 await trrServer
.registerDoHAnswers(host
, "HTTPS", {
104 answers
: httpsRecord
,
107 await trrServer
.registerDoHAnswers(host
, "AAAA", {
108 answers
: ipv6Answers
,
111 await trrServer
.registerDoHAnswers(host
, "A", {
112 answers
: ipv4Answers
,
115 Services
.dns
.clearCache(true);
118 // Test if we retry IPv4 address for Http/3 properly.
119 add_task(async
function test_retry_with_ipv4() {
120 let host
= "test.http3_retry.com";
130 // The UDP socket will return connection refused error because we set
131 // "network.http.http3.block_loopback_ipv6_addr" to true.
151 { key
: "alpn", value
: "h3" },
152 { key
: "port", value
: h3Port
},
158 await
registerDoHAnswers(host
, ipv4answers
, ipv6answers
, httpsRecord
);
160 let chan
= makeChan(`https://${host}`);
161 let [req
] = await
channelOpenPromise(chan
);
162 Assert
.equal(req
.protocolVersion
, "h3");
164 await trrServer
.stop();
167 add_task(async
function test_retry_with_ipv4_disabled() {
168 let host
= "test.http3_retry_ipv4_blocked.com";
178 // The UDP socket will return connection refused error because we set
179 // "network.http.http3.block_loopback_ipv6_addr" to true.
199 { key
: "alpn", value
: "h3" },
200 { key
: "port", value
: h3Port
},
206 await
registerDoHAnswers(host
, ipv4answers
, ipv6answers
, httpsRecord
);
208 let chan
= makeChan(`https://${host}`);
209 chan
.QueryInterface(Ci
.nsIHttpChannelInternal
);
210 chan
.setIPv4Disabled();
212 await
channelOpenPromise(chan
, CL_EXPECT_FAILURE
);
213 await trrServer
.stop();
216 // See bug 1837252. There is no way to observe the outcome of this test, because
217 // the crash in bug 1837252 is only triggered by speculative connection.
218 // The outcome of this test is no crash.
219 add_task(async
function test_retry_with_ipv4_failed() {
220 let host
= "test.http3_retry_failed.com";
221 // Return a wrong answer intentionally.
231 // The UDP socket will return connection refused error because we set
232 // "network.http.http3.block_loopback_ipv6_addr" to true.
252 { key
: "alpn", value
: "h3" },
253 { key
: "port", value
: h3Port
},
259 await
registerDoHAnswers(host
, ipv4answers
, ipv6answers
, httpsRecord
);
261 // This speculative connection is used to trigger the mechanism to retry
262 // Http/3 connection with a IPv4 address.
263 // We want to make the connection entry's IP preference known,
264 // so DnsAndConnectSocket::mRetryWithDifferentIPFamily will be set to true
265 // before the second speculative connection.
266 let uri
= Services
.io
.newURI(`https://test.http3_retry_failed.com`);
267 Services
.io
.speculativeConnect(
269 Services
.scriptSecurityManager
.getSystemPrincipal(),
274 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
275 await
new Promise(resolve
=> setTimeout(resolve
, 3000));
277 // When this speculative connection is created, the connection entry is
278 // already set to prefer IPv4. Since we provided an invalid A response,
279 // DnsAndConnectSocket::OnLookupComplete is called with an error.
280 // Since DnsAndConnectSocket::mRetryWithDifferentIPFamily is true, we do
281 // retry DNS lookup. During retry, we should not create UDP connection.
282 Services
.io
.speculativeConnect(
284 Services
.scriptSecurityManager
.getSystemPrincipal(),
289 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
290 await
new Promise(resolve
=> setTimeout(resolve
, 3000));
291 await trrServer
.stop();
294 add_task(async
function test_retry_with_0rtt() {
295 let host
= "test.http3_retry_0rtt.com";
305 // The UDP socket will return connection refused error because we set
306 // "network.http.http3.block_loopback_ipv6_addr" to true.
326 { key
: "alpn", value
: "h3" },
327 { key
: "port", value
: h3Port
},
333 await
registerDoHAnswers(host
, ipv4answers
, ipv6answers
, httpsRecord
);
335 let chan
= makeChan(`https://${host}`);
336 chan
.QueryInterface(Ci
.nsIHttpChannelInternal
);
337 chan
.setIPv6Disabled();
339 let [req
] = await
channelOpenPromise(chan
);
340 Assert
.equal(req
.protocolVersion
, "h3");
342 // Make sure the h3 connection created by the previous test is cleared.
343 Services
.obs
.notifyObservers(null, "net:cancel-all-connections");
344 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
345 await
new Promise(resolve
=> setTimeout(resolve
, 1000));
347 chan
= makeChan(`https://${host}`);
348 chan
.QueryInterface(Ci
.nsIHttpChannelInternal
);
350 [req
] = await
channelOpenPromise(chan
);
351 Assert
.equal(req
.protocolVersion
, "h3");
353 await trrServer
.stop();