Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / test / unit / test_http3_dns_retry.js
blob768903535b28efc4e3401d67c4dd5036d1bcff1d
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/. */
5 "use strict";
7 var { setTimeout } = ChromeUtils.importESModule(
8 "resource://gre/modules/Timer.sys.mjs"
9 );
11 let h2Port;
12 let h3Port;
13 let trrServer;
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, "");
28 trr_test_setup();
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",
44 true
46 Services.prefs.setBoolPref(
47 "network.http.http3.retry_different_ip_family",
48 true
50 Services.prefs.setBoolPref("network.dns.get-ttl", false);
52 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
53 true
56 registerCleanupFunction(async () => {
57 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
58 false
60 trr_clear_prefs();
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");
67 if (trrServer) {
68 await trrServer.stop();
70 });
71 });
73 function makeChan(url) {
74 let chan = NetUtil.newChannel({
75 uri: url,
76 loadUsingSystemPrincipal: true,
77 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
78 }).QueryInterface(Ci.nsIHttpChannel);
79 chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
80 return chan;
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));
90 });
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(
99 "network.trr.uri",
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";
121 let ipv4answers = [
123 name: host,
124 ttl: 55,
125 type: "A",
126 flush: false,
127 data: "127.0.0.1",
130 // The UDP socket will return connection refused error because we set
131 // "network.http.http3.block_loopback_ipv6_addr" to true.
132 let ipv6answers = [
134 name: host,
135 ttl: 55,
136 type: "AAAA",
137 flush: false,
138 data: "::1",
141 let httpsRecord = [
143 name: host,
144 ttl: 55,
145 type: "HTTPS",
146 flush: false,
147 data: {
148 priority: 1,
149 name: host,
150 values: [
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";
169 let ipv4answers = [
171 name: host,
172 ttl: 55,
173 type: "A",
174 flush: false,
175 data: "127.0.0.1",
178 // The UDP socket will return connection refused error because we set
179 // "network.http.http3.block_loopback_ipv6_addr" to true.
180 let ipv6answers = [
182 name: host,
183 ttl: 55,
184 type: "AAAA",
185 flush: false,
186 data: "::1",
189 let httpsRecord = [
191 name: host,
192 ttl: 55,
193 type: "HTTPS",
194 flush: false,
195 data: {
196 priority: 1,
197 name: host,
198 values: [
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.
222 let ipv4answers = [
224 name: host,
225 ttl: 55,
226 type: "AAAA",
227 flush: false,
228 data: "127.0.0.1",
231 // The UDP socket will return connection refused error because we set
232 // "network.http.http3.block_loopback_ipv6_addr" to true.
233 let ipv6answers = [
235 name: host,
236 ttl: 55,
237 type: "AAAA",
238 flush: false,
239 data: "::1",
242 let httpsRecord = [
244 name: host,
245 ttl: 55,
246 type: "HTTPS",
247 flush: false,
248 data: {
249 priority: 1,
250 name: host,
251 values: [
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(
268 uri,
269 Services.scriptSecurityManager.getSystemPrincipal(),
270 null,
271 false
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(
283 uri,
284 Services.scriptSecurityManager.getSystemPrincipal(),
285 null,
286 false
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";
296 let ipv4answers = [
298 name: host,
299 ttl: 55,
300 type: "A",
301 flush: false,
302 data: "127.0.0.1",
305 // The UDP socket will return connection refused error because we set
306 // "network.http.http3.block_loopback_ipv6_addr" to true.
307 let ipv6answers = [
309 name: host,
310 ttl: 55,
311 type: "AAAA",
312 flush: false,
313 data: "::1",
316 let httpsRecord = [
318 name: host,
319 ttl: 55,
320 type: "HTTPS",
321 flush: false,
322 data: {
323 priority: 1,
324 name: host,
325 values: [
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();