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 { TestUtils } = ChromeUtils.importESModule(
16 "resource://testing-common/TestUtils.sys.mjs"
18 const certOverrideService = Cc[
19 "@mozilla.org/security/certoverride;1"
20 ].getService(Ci.nsICertOverrideService);
22 add_setup(async function setup() {
23 h2Port = Services.env.get("MOZHTTP2_PORT");
24 Assert.notEqual(h2Port, null);
25 Assert.notEqual(h2Port, "");
27 h3Port = Services.env.get("MOZHTTP3_PORT_NO_RESPONSE");
28 Assert.notEqual(h3Port, null);
29 Assert.notEqual(h3Port, "");
33 if (mozinfo.socketprocess_networking) {
34 Services.dns; // Needed to trigger socket process.
35 await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
38 Services.prefs.setIntPref("network.trr.mode", 2); // TRR first
39 Services.prefs.setBoolPref("network.http.http3.enable", true);
40 Services.prefs.setIntPref("network.http.speculative-parallel-limit", 6);
42 registerCleanupFunction(async () => {
44 Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr");
45 Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc");
46 Services.prefs.clearUserPref("network.dns.echconfig.enabled");
47 Services.prefs.clearUserPref("network.dns.http3_echconfig.enabled");
48 Services.prefs.clearUserPref(
49 "network.dns.echconfig.fallback_to_origin_when_all_failed"
51 Services.prefs.clearUserPref("network.dns.httpssvc.reset_exclustion_list");
52 Services.prefs.clearUserPref("network.http.http3.enable");
53 Services.prefs.clearUserPref(
54 "network.dns.httpssvc.http3_fast_fallback_timeout"
56 Services.prefs.clearUserPref(
57 "network.http.http3.alt-svc-mapping-for-testing"
59 Services.prefs.clearUserPref("network.http.http3.backup_timer_delay");
60 Services.prefs.clearUserPref("network.http.speculative-parallel-limit");
61 Services.prefs.clearUserPref(
62 "network.http.http3.parallel_fallback_conn_limit"
65 await trrServer.stop();
70 function makeChan(url) {
71 let chan = NetUtil.newChannel({
73 loadUsingSystemPrincipal: true,
74 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
75 }).QueryInterface(Ci.nsIHttpChannel);
76 chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
80 function channelOpenPromise(chan, flags, delay) {
81 // eslint-disable-next-line no-async-promise-executor
82 return new Promise(async resolve => {
83 function finish(req, buffer) {
84 resolve([req, buffer]);
85 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
89 let internal = chan.QueryInterface(Ci.nsIHttpChannelInternal);
90 internal.setWaitForHTTPSSVCRecord();
91 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
95 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
96 await new Promise(r => setTimeout(r, delay));
98 chan.asyncOpen(new ChannelListener(finish, null, flags));
102 let CheckOnlyHttp2Listener = function () {};
104 CheckOnlyHttp2Listener.prototype = {
105 onStartRequest: function testOnStartRequest() {},
107 onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
108 read_stream(stream, cnt);
111 onStopRequest: function testOnStopRequest(request, status) {
112 Assert.equal(status, Cr.NS_OK);
113 let httpVersion = "";
115 httpVersion = request.protocolVersion;
117 Assert.equal(httpVersion, "h2");
121 routed = request.getRequestHeader("Alt-Used");
123 dump("routed is " + routed + "\n");
124 Assert.ok(routed === "0" || routed === "NA");
129 async function fast_fallback_test() {
131 // We need to loop here because we need to wait for AltSvc storage to
133 // We also do not have a way to verify that HTTP3 has been tried, because
134 // the fallback is automatic, so try a couple of times.
136 // We need to close HTTP2 connections, otherwise our connection pooling
137 // will dispatch the request over already existing HTTP2 connection.
138 Services.obs.notifyObservers(null, "net:prune-all-connections");
139 let chan = makeChan(`https://foo.example.com:${h2Port}/`);
140 let listener = new CheckOnlyHttp2Listener();
141 await altsvcSetupPromise(chan, listener);
143 } while (result < 3);
146 // Test the case when speculative connection is enabled. In this case, when the
147 // backup connection is ready, the http transaction is still in pending
148 // queue because the h3 connection is never ready to accept transactions.
149 add_task(async function test_fast_fallback_with_speculative_connection() {
150 Services.prefs.setBoolPref("network.http.http3.enable", true);
151 Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com");
152 // Set AltSvc to point to not existing HTTP3 server on port 443
153 Services.prefs.setCharPref(
154 "network.http.http3.alt-svc-mapping-for-testing",
155 "foo.example.com;h3-29=:" + h3Port
157 Services.prefs.setBoolPref("network.dns.disableIPv6", true);
158 Services.prefs.setIntPref("network.http.speculative-parallel-limit", 6);
160 await fast_fallback_test();
163 // Test the case when speculative connection is disabled. In this case, when the
164 // back connection is ready, the http transaction is already activated,
165 // but the socket is not ready to write.
166 add_task(async function test_fast_fallback_without_speculative_connection() {
167 // Make sure the h3 connection created by the previous test is cleared.
168 Services.obs.notifyObservers(null, "net:cancel-all-connections");
169 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
170 await new Promise(resolve => setTimeout(resolve, 1000));
171 // Clear the h3 excluded list, otherwise the Alt-Svc mapping will not be used.
172 Services.obs.notifyObservers(null, "network:reset-http3-excluded-list");
173 Services.prefs.setIntPref("network.http.speculative-parallel-limit", 0);
175 await fast_fallback_test();
177 Services.prefs.clearUserPref(
178 "network.http.http3.alt-svc-mapping-for-testing"
182 // Test when echConfig is disabled and we have https rr for http3. We use a
183 // longer timeout in this test, so when fast fallback timer is triggered, the
184 // http transaction is already activated.
185 add_task(async function testFastfallback() {
186 trrServer = new TRRServer();
187 await trrServer.start();
188 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
189 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
190 Services.prefs.setBoolPref("network.dns.echconfig.enabled", false);
192 Services.prefs.setIntPref("network.trr.mode", 3);
193 Services.prefs.setCharPref(
195 `https://foo.example.com:${trrServer.port()}/dns-query`
197 Services.prefs.setBoolPref("network.http.http3.enable", true);
199 Services.prefs.setIntPref(
200 "network.dns.httpssvc.http3_fast_fallback_timeout",
204 await trrServer.registerDoHAnswers("test.fastfallback.com", "HTTPS", {
207 name: "test.fastfallback.com",
213 name: "test.fastfallback1.com",
215 { key: "alpn", value: "h3-29" },
216 { key: "port", value: h3Port },
217 { key: "echconfig", value: "456..." },
222 name: "test.fastfallback.com",
228 name: "test.fastfallback2.com",
230 { key: "alpn", value: "h2" },
231 { key: "port", value: h2Port },
232 { key: "echconfig", value: "456..." },
239 await trrServer.registerDoHAnswers("test.fastfallback1.com", "A", {
242 name: "test.fastfallback1.com",
251 await trrServer.registerDoHAnswers("test.fastfallback2.com", "A", {
254 name: "test.fastfallback2.com",
263 let chan = makeChan(`https://test.fastfallback.com/server-timing`);
264 let [req] = await channelOpenPromise(chan);
265 Assert.equal(req.protocolVersion, "h2");
266 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
267 Assert.equal(internal.remotePort, h2Port);
269 await trrServer.stop();
272 // Like the previous test, but with a shorter timeout, so when fast fallback
273 // timer is triggered, the http transaction is still in pending queue.
274 add_task(async function testFastfallback1() {
275 trrServer = new TRRServer();
276 await trrServer.start();
277 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
278 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
279 Services.prefs.setBoolPref("network.dns.echconfig.enabled", false);
281 Services.prefs.setIntPref("network.trr.mode", 3);
282 Services.prefs.setCharPref(
284 `https://foo.example.com:${trrServer.port()}/dns-query`
286 Services.prefs.setBoolPref("network.http.http3.enable", true);
288 Services.prefs.setIntPref(
289 "network.dns.httpssvc.http3_fast_fallback_timeout",
293 await trrServer.registerDoHAnswers("test.fastfallback.org", "HTTPS", {
296 name: "test.fastfallback.org",
302 name: "test.fastfallback1.org",
304 { key: "alpn", value: "h3-29" },
305 { key: "port", value: h3Port },
306 { key: "echconfig", value: "456..." },
311 name: "test.fastfallback.org",
317 name: "test.fastfallback2.org",
319 { key: "alpn", value: "h2" },
320 { key: "port", value: h2Port },
321 { key: "echconfig", value: "456..." },
328 await trrServer.registerDoHAnswers("test.fastfallback1.org", "A", {
331 name: "test.fastfallback1.org",
340 await trrServer.registerDoHAnswers("test.fastfallback2.org", "A", {
343 name: "test.fastfallback2.org",
352 let chan = makeChan(`https://test.fastfallback.org/server-timing`);
353 let [req] = await channelOpenPromise(chan);
354 Assert.equal(req.protocolVersion, "h2");
355 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
356 Assert.equal(internal.remotePort, h2Port);
358 await trrServer.stop();
361 // Test when echConfig is enabled, we can sucessfully fallback to the last
363 add_task(async function testFastfallbackWithEchConfig() {
364 trrServer = new TRRServer();
365 await trrServer.start();
366 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
367 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
368 Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
369 Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
371 Services.prefs.setIntPref("network.trr.mode", 3);
372 Services.prefs.setCharPref(
374 `https://foo.example.com:${trrServer.port()}/dns-query`
376 Services.prefs.setBoolPref("network.http.http3.enable", true);
378 Services.prefs.setIntPref(
379 "network.dns.httpssvc.http3_fast_fallback_timeout",
383 await trrServer.registerDoHAnswers("test.ech.org", "HTTPS", {
386 name: "test.ech.org",
392 name: "test.ech1.org",
394 { key: "alpn", value: "h3-29" },
395 { key: "port", value: h3Port },
396 { key: "echconfig", value: "456..." },
401 name: "test.ech.org",
407 name: "test.ech2.org",
409 { key: "alpn", value: "h2" },
410 { key: "port", value: h2Port },
411 { key: "echconfig", value: "456..." },
416 name: "test.ech.org",
422 name: "test.ech3.org",
424 { key: "alpn", value: "h2" },
425 { key: "port", value: h2Port },
426 { key: "echconfig", value: "456..." },
433 await trrServer.registerDoHAnswers("test.ech1.org", "A", {
436 name: "test.ech1.org",
445 await trrServer.registerDoHAnswers("test.ech3.org", "A", {
448 name: "test.ech3.org",
457 let chan = makeChan(`https://test.ech.org/server-timing`);
458 let [req] = await channelOpenPromise(chan);
459 Assert.equal(req.protocolVersion, "h2");
460 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
461 Assert.equal(internal.remotePort, h2Port);
463 await trrServer.stop();
466 // Test when echConfig is enabled, the connection should fail when not all
467 // records have echConfig.
468 add_task(async function testFastfallbackWithpartialEchConfig() {
469 trrServer = new TRRServer();
470 await trrServer.start();
471 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
472 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
473 Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
474 Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
476 Services.prefs.setIntPref("network.trr.mode", 3);
477 Services.prefs.setCharPref(
479 `https://foo.example.com:${trrServer.port()}/dns-query`
481 Services.prefs.setBoolPref("network.http.http3.enable", true);
483 Services.prefs.setIntPref(
484 "network.dns.httpssvc.http3_fast_fallback_timeout",
488 await trrServer.registerDoHAnswers("test.partial_ech.org", "HTTPS", {
491 name: "test.partial_ech.org",
497 name: "test.partial_ech1.org",
499 { key: "alpn", value: "h3-29" },
500 { key: "port", value: h3Port },
501 { key: "echconfig", value: "456..." },
506 name: "test.partial_ech.org",
512 name: "test.partial_ech2.org",
514 { key: "alpn", value: "h2" },
515 { key: "port", value: h2Port },
522 await trrServer.registerDoHAnswers("test.partial_ech1.org", "A", {
525 name: "test.partial_ech1.org",
534 await trrServer.registerDoHAnswers("test.partial_ech2.org", "A", {
537 name: "test.partial_ech2.org",
546 let chan = makeChan(`https://test.partial_ech.org/server-timing`);
547 await channelOpenPromise(chan, CL_EXPECT_LATE_FAILURE | CL_ALLOW_UNKNOWN_CL);
549 await trrServer.stop();
552 add_task(async function testFastfallbackWithoutEchConfig() {
553 trrServer = new TRRServer();
554 await trrServer.start();
555 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
556 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
558 Services.prefs.setIntPref("network.trr.mode", 3);
559 Services.prefs.setCharPref(
561 `https://foo.example.com:${trrServer.port()}/dns-query`
563 Services.prefs.setBoolPref("network.http.http3.enable", true);
565 Services.prefs.setIntPref(
566 "network.dns.httpssvc.http3_fast_fallback_timeout",
570 await trrServer.registerDoHAnswers("test.no_ech_h2.org", "HTTPS", {
573 name: "test.no_ech_h2.org",
579 name: "test.no_ech_h3.org",
581 { key: "alpn", value: "h3-29" },
582 { key: "port", value: h3Port },
589 await trrServer.registerDoHAnswers("test.no_ech_h3.org", "A", {
592 name: "test.no_ech_h3.org",
601 await trrServer.registerDoHAnswers("test.no_ech_h2.org", "A", {
604 name: "test.no_ech_h2.org",
613 let chan = makeChan(`https://test.no_ech_h2.org:${h2Port}/server-timing`);
614 let [req] = await channelOpenPromise(chan);
615 Assert.equal(req.protocolVersion, "h2");
616 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
617 Assert.equal(internal.remotePort, h2Port);
619 await trrServer.stop();
622 add_task(async function testH3FallbackWithMultipleTransactions() {
623 trrServer = new TRRServer();
624 await trrServer.start();
625 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
626 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
627 Services.prefs.setBoolPref("network.dns.echconfig.enabled", false);
629 Services.prefs.setIntPref("network.trr.mode", 3);
630 Services.prefs.setCharPref(
632 `https://foo.example.com:${trrServer.port()}/dns-query`
634 Services.prefs.setBoolPref("network.http.http3.enable", true);
636 // Disable fast fallback.
637 Services.prefs.setIntPref(
638 "network.http.http3.parallel_fallback_conn_limit",
641 Services.prefs.setIntPref("network.http.speculative-parallel-limit", 0);
643 await trrServer.registerDoHAnswers("test.multiple_trans.org", "HTTPS", {
646 name: "test.multiple_trans.org",
652 name: "test.multiple_trans.org",
654 { key: "alpn", value: "h3-29" },
655 { key: "port", value: h3Port },
662 await trrServer.registerDoHAnswers("test.multiple_trans.org", "A", {
665 name: "test.multiple_trans.org",
675 for (let i = 0; i < 2; ++i) {
677 `https://test.multiple_trans.org:${h2Port}/server-timing`
679 promises.push(channelOpenPromise(chan));
682 let res = await Promise.all(promises);
683 res.forEach(function (e) {
685 Assert.equal(req.protocolVersion, "h2");
686 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
687 Assert.equal(internal.remotePort, h2Port);
690 await trrServer.stop();
693 add_task(async function testTwoFastFallbackTimers() {
694 trrServer = new TRRServer();
695 await trrServer.start();
696 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
697 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
698 Services.prefs.setBoolPref("network.dns.echconfig.enabled", false);
700 Services.prefs.setIntPref("network.trr.mode", 3);
701 Services.prefs.setCharPref(
703 `https://foo.example.com:${trrServer.port()}/dns-query`
705 Services.prefs.setBoolPref("network.http.http3.enable", true);
707 Services.prefs.setIntPref("network.http.speculative-parallel-limit", 6);
708 Services.prefs.clearUserPref(
709 "network.http.http3.parallel_fallback_conn_limit"
712 Services.prefs.setCharPref(
713 "network.http.http3.alt-svc-mapping-for-testing",
714 "foo.fallback.org;h3-29=:" + h3Port
717 Services.prefs.setIntPref(
718 "network.dns.httpssvc.http3_fast_fallback_timeout",
721 Services.prefs.setIntPref("network.http.http3.backup_timer_delay", 100);
723 await trrServer.registerDoHAnswers("foo.fallback.org", "HTTPS", {
726 name: "foo.fallback.org",
732 name: "foo.fallback.org",
734 { key: "alpn", value: "h3-29" },
735 { key: "port", value: h3Port },
742 await trrServer.registerDoHAnswers("foo.fallback.org", "A", {
745 name: "foo.fallback.org",
754 // Test the case that http3 backup timer is triggered after
755 // fast fallback timer or HTTPS RR.
756 Services.prefs.setIntPref(
757 "network.dns.httpssvc.http3_fast_fallback_timeout",
760 Services.prefs.setIntPref("network.http.http3.backup_timer_delay", 100);
762 async function createChannelAndStartTest() {
763 let chan = makeChan(`https://foo.fallback.org:${h2Port}/server-timing`);
764 let [req] = await channelOpenPromise(chan);
765 Assert.equal(req.protocolVersion, "h2");
766 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
767 Assert.equal(internal.remotePort, h2Port);
770 await createChannelAndStartTest();
772 Services.obs.notifyObservers(null, "net:prune-all-connections");
773 Services.obs.notifyObservers(null, "network:reset-http3-excluded-list");
774 Services.dns.clearCache(true);
776 // Do the same test again, but with a different configuration.
777 Services.prefs.setIntPref(
778 "network.dns.httpssvc.http3_fast_fallback_timeout",
781 Services.prefs.setIntPref("network.http.http3.backup_timer_delay", 10);
783 await createChannelAndStartTest();
785 await trrServer.stop();
788 add_task(async function testH3FastFallbackWithMultipleTransactions() {
789 trrServer = new TRRServer();
790 await trrServer.start();
791 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
792 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
793 Services.prefs.setBoolPref("network.dns.echconfig.enabled", false);
795 Services.prefs.setIntPref("network.trr.mode", 3);
796 Services.prefs.setCharPref(
798 `https://foo.example.com:${trrServer.port()}/dns-query`
800 Services.prefs.setBoolPref("network.http.http3.enable", true);
802 Services.prefs.setIntPref("network.http.speculative-parallel-limit", 6);
803 Services.prefs.clearUserPref(
804 "network.http.http3.parallel_fallback_conn_limit"
807 Services.prefs.setIntPref("network.http.http3.backup_timer_delay", 500);
809 Services.prefs.setCharPref(
810 "network.http.http3.alt-svc-mapping-for-testing",
811 "test.multiple_fallback_trans.org;h3-29=:" + h3Port
814 await trrServer.registerDoHAnswers("test.multiple_fallback_trans.org", "A", {
817 name: "test.multiple_fallback_trans.org",
827 for (let i = 0; i < 3; ++i) {
829 `https://test.multiple_fallback_trans.org:${h2Port}/server-timing`
832 promises.push(channelOpenPromise(chan));
834 promises.push(channelOpenPromise(chan, null, 500));
838 let res = await Promise.all(promises);
839 res.forEach(function (e) {
841 Assert.equal(req.protocolVersion, "h2");
842 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
843 Assert.equal(internal.remotePort, h2Port);
846 await trrServer.stop();
849 add_task(async function testFastfallbackToTheSameRecord() {
850 trrServer = new TRRServer();
851 await trrServer.start();
852 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
853 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
854 Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
855 Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
857 Services.prefs.setIntPref("network.trr.mode", 3);
858 Services.prefs.setCharPref(
860 `https://foo.example.com:${trrServer.port()}/dns-query`
862 Services.prefs.setBoolPref("network.http.http3.enable", true);
864 Services.prefs.setIntPref(
865 "network.dns.httpssvc.http3_fast_fallback_timeout",
869 await trrServer.registerDoHAnswers("test.ech.org", "HTTPS", {
872 name: "test.ech.org",
878 name: "test.ech1.org",
880 { key: "alpn", value: ["h3-29", "h2"] },
881 { key: "port", value: h2Port },
882 { key: "echconfig", value: "456..." },
889 await trrServer.registerDoHAnswers("test.ech1.org", "A", {
892 name: "test.ech1.org",
901 let chan = makeChan(`https://test.ech.org/server-timing`);
902 let [req] = await channelOpenPromise(chan);
903 Assert.equal(req.protocolVersion, "h2");
904 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
905 Assert.equal(internal.remotePort, h2Port);
907 await trrServer.stop();