3 const { HttpServer } = ChromeUtils.importESModule(
4 "resource://testing-common/httpd.sys.mjs"
9 // https://foo.example.com:(h3Port)
10 // https://bar.example.com:(h3Port) <- invalid for bar, but ok for foo
11 var h1Foo; // server http://foo.example.com:(h1Foo.identity.primaryPort)
12 var h1Bar; // server http://bar.example.com:(h1bar.identity.primaryPort)
14 var otherServer; // server socket listening for other connection.
16 var h3FooRoute; // foo.example.com:H3PORT
17 var h3BarRoute; // bar.example.com:H3PORT
18 var h3Route; // :H3PORT
19 var httpFooOrigin; // http://foo.exmaple.com:PORT/
20 var httpsFooOrigin; // https://foo.exmaple.com:PORT/
21 var httpBarOrigin; // http://bar.example.com:PORT/
22 var httpsBarOrigin; // https://bar.example.com:PORT/
25 h3Port = Services.env.get("MOZHTTP3_PORT");
26 Assert.notEqual(h3Port, null);
27 Assert.notEqual(h3Port, "");
29 // Set to allow the cert presented by our H3 server
32 Services.prefs.setBoolPref("network.http.http3.enable", true);
33 Services.prefs.setBoolPref("network.http.altsvc.enabled", true);
34 Services.prefs.setBoolPref("network.http.altsvc.oe", true);
35 Services.prefs.setCharPref(
36 "network.dns.localDomains",
37 "foo.example.com, bar.example.com"
40 // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
41 // so add that cert to the trust list as a signing cert. The same cert is used
42 // for both h3FooRoute and h3BarRoute though it is only valid for
43 // the foo.example.com domain name.
44 let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
47 addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
49 h1Foo = new HttpServer();
50 h1Foo.registerPathHandler("/altsvc-test", h1Server);
51 h1Foo.registerPathHandler("/.well-known/http-opportunistic", h1ServerWK);
53 h1Foo.identity.setPrimary(
56 h1Foo.identity.primaryPort
59 h1Bar = new HttpServer();
60 h1Bar.registerPathHandler("/altsvc-test", h1Server);
62 h1Bar.identity.setPrimary(
65 h1Bar.identity.primaryPort
68 h3FooRoute = "foo.example.com:" + h3Port;
69 h3BarRoute = "bar.example.com:" + h3Port;
70 h3Route = ":" + h3Port;
72 httpFooOrigin = "http://foo.example.com:" + h1Foo.identity.primaryPort + "/";
73 httpsFooOrigin = "https://" + h3FooRoute + "/";
74 httpBarOrigin = "http://bar.example.com:" + h1Bar.identity.primaryPort + "/";
75 httpsBarOrigin = "https://" + h3BarRoute + "/";
94 function h1Server(metadata, response) {
95 response.setStatusLine(metadata.httpVersion, 200, "OK");
96 response.setHeader("Content-Type", "text/plain", false);
97 response.setHeader("Connection", "close", false);
98 response.setHeader("Cache-Control", "no-cache", false);
99 response.setHeader("Access-Control-Allow-Origin", "*", false);
100 response.setHeader("Access-Control-Allow-Method", "GET", false);
101 response.setHeader("Access-Control-Allow-Headers", "x-altsvc", false);
104 var hval = "h3-29=" + metadata.getHeader("x-altsvc");
105 response.setHeader("Alt-Svc", hval, false);
108 var body = "Q: What did 0 say to 8? A: Nice Belt!\n";
109 response.bodyOutputStream.write(body, body.length);
112 function h1ServerWK(metadata, response) {
113 response.setStatusLine(metadata.httpVersion, 200, "OK");
114 response.setHeader("Content-Type", "application/json", false);
115 response.setHeader("Connection", "close", false);
116 response.setHeader("Cache-Control", "no-cache", false);
117 response.setHeader("Access-Control-Allow-Origin", "*", false);
118 response.setHeader("Access-Control-Allow-Method", "GET", false);
119 response.setHeader("Access-Control-Allow-Headers", "x-altsvc", false);
121 var body = '["http://foo.example.com:' + h1Foo.identity.primaryPort + '"]';
122 response.bodyOutputStream.write(body, body.length);
125 function resetPrefs() {
126 Services.prefs.clearUserPref("network.http.http3.enable");
127 Services.prefs.clearUserPref("network.dns.localDomains");
128 Services.prefs.clearUserPref("network.http.altsvc.enabled");
129 Services.prefs.clearUserPref("network.http.altsvc.oe");
130 Services.prefs.clearUserPref("network.dns.localDomains");
131 Services.prefs.clearUserPref("network.security.ports.banned");
134 function makeChan(origin) {
135 return NetUtil.newChannel({
136 uri: origin + "altsvc-test",
137 loadUsingSystemPrincipal: true,
138 }).QueryInterface(Ci.nsIHttpChannel);
143 var loadWithoutClearingMappings = false;
144 var disallowH3 = false;
145 var disallowH2 = false;
146 var testKeepAliveNotSet = false;
148 var expectPass = true;
150 var originAttributes = {};
152 var Listener = function () {};
153 Listener.prototype = {
154 onStartRequest: function testOnStartRequest(request) {
155 Assert.ok(request instanceof Ci.nsIHttpChannel);
158 if (!Components.isSuccessCode(request.status)) {
160 "Channel should have a success code! (" + request.status + ")"
163 Assert.equal(request.responseStatus, 200);
165 Assert.equal(Components.isSuccessCode(request.status), false);
169 onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
170 read_stream(stream, cnt);
173 onStopRequest: function testOnStopRequest(request, status) {
176 routed = request.getRequestHeader("Alt-Used");
178 dump("routed is " + routed + "\n");
179 Assert.equal(Components.isSuccessCode(status), expectPass);
182 Assert.equal(routed, "");
184 loadWithoutClearingMappings = true;
185 do_timeout(waitFor, doTest);
188 } else if (xaltsvc == "NA") {
189 Assert.equal(routed, "");
191 } else if (routed == xaltsvc) {
192 Assert.equal(routed, xaltsvc); // always true, but a useful log
195 dump("poll later for alt svc mapping\n");
197 loadWithoutClearingMappings = true;
198 do_timeout(500, doTest);
205 function testsDone() {
211 h1Foo.stop(do_test_finished);
213 h1Bar.stop(do_test_finished);
217 dump("execute doTest " + origin + "\n");
218 var chan = makeChan(origin);
219 var listener = new Listener();
220 if (xaltsvc != "NA") {
221 chan.setRequestHeader("x-altsvc", xaltsvc, false);
223 if (testKeepAliveNotSet) {
224 chan.setRequestHeader("Connection", "close", false);
225 testKeepAliveNotSet = false;
227 if (loadWithoutClearingMappings) {
228 chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
231 Ci.nsIRequest.LOAD_FRESH_CONNECTION |
232 Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
235 let internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
236 internalChannel.allowHttp3 = false;
240 let internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
241 internalChannel.allowSpdy = false;
244 loadWithoutClearingMappings = false;
245 chan.loadInfo.originAttributes = originAttributes;
246 chan.asyncOpen(listener);
249 // xaltsvc is overloaded to do two things..
250 // 1] it is sent in the x-altsvc request header, and the response uses the value in the Alt-Svc response header
251 // 2] the test polls until necko sets Alt-Used to that value (i.e. it uses that route)
253 // When xaltsvc is set to h3Route (i.e. :port with the implied hostname) it doesn't match the alt-used,
254 // which is always explicit, so it needs to be changed after the channel is created but before the
255 // listener is invoked
257 // http://foo served from h3-29=:port
260 origin = httpFooOrigin;
265 xaltsvc = h3FooRoute;
268 // http://foo served from h3-29=foo:port
271 origin = httpFooOrigin;
272 xaltsvc = h3FooRoute;
278 // http://foo served from h3-29=bar:port
279 // requires cert for foo
282 origin = httpFooOrigin;
283 xaltsvc = h3BarRoute;
289 // https://bar should fail because host bar has cert for foo
292 origin = httpsBarOrigin;
300 // http://bar via h3 on bar
301 // should not use TLS/h3 because h3BarRoute is not auth'd for bar
302 // however the test ought to PASS (i.e. get a 200) because fallback
303 // to plaintext happens.. thus the timeout
306 origin = httpBarOrigin;
307 xaltsvc = h3BarRoute;
315 // http://bar served from h3-29=:port, which is like the bar route in 8
318 origin = httpBarOrigin;
325 xaltsvc = h3BarRoute;
328 // check again https://bar should fail because host bar has cert for foo
331 origin = httpsBarOrigin;
339 // http://bar served from h3-29=foo, should fail because host foo only has
340 // cert for foo. Fail in this case means alt-svc is not used, but content
344 origin = httpBarOrigin;
345 xaltsvc = h3FooRoute;
354 // Insert a cache of http://foo served from h3-29=:port with origin attributes.
357 origin = httpFooOrigin;
361 firstPartyDomain: "a.com",
366 xaltsvc = h3FooRoute;
369 // Make sure we get a cache miss with a different userContextId.
370 function doTest10() {
371 dump("doTest10()\n");
372 origin = httpFooOrigin;
376 firstPartyDomain: "a.com",
378 loadWithoutClearingMappings = true;
384 // Make sure we get a cache miss with a different firstPartyDomain.
385 function doTest11() {
386 dump("doTest11()\n");
387 origin = httpFooOrigin;
391 firstPartyDomain: "b.com",
393 loadWithoutClearingMappings = true;
399 // Make sure we get a cache hit with the same origin attributes.
400 function doTest12() {
401 dump("doTest12()\n");
402 origin = httpFooOrigin;
406 firstPartyDomain: "a.com",
408 loadWithoutClearingMappings = true;
412 // This ensures a cache hit.
413 xaltsvc = h3FooRoute;
416 // Make sure we do not use H3 if it is disabled on a channel.
417 function doTest13() {
418 dump("doTest13()\n");
419 origin = httpFooOrigin;
424 firstPartyDomain: "a.com",
426 loadWithoutClearingMappings = true;
432 // Make sure we use H3 if only Http2 is disabled on a channel.
433 function doTest14() {
434 dump("doTest14()\n");
435 origin = httpFooOrigin;
440 firstPartyDomain: "a.com",
442 loadWithoutClearingMappings = true;
446 // This should ensures a cache hit.
447 xaltsvc = h3FooRoute;
450 // Make sure we do not use H3 if NS_HTTP_ALLOW_KEEPALIVE is not set.
451 function doTest15() {
452 dump("doTest15()\n");
453 origin = httpFooOrigin;
455 testKeepAliveNotSet = true;
458 firstPartyDomain: "a.com",
460 loadWithoutClearingMappings = true;
466 // Check we don't connect to blocked ports
467 function doTest16() {
468 dump("doTest16()\n");
469 origin = httpFooOrigin;
470 nextTest = testsDone;
471 otherServer = Cc["@mozilla.org/network/server-socket;1"].createInstance(
474 otherServer.init(-1, true, -1);
475 xaltsvc = "localhost:" + otherServer.port;
476 Services.prefs.setCharPref(
477 "network.security.ports.banned",
478 "" + otherServer.port
480 dump("Blocked port: " + otherServer.port);
482 otherServer.asyncListen({
484 Assert.ok(false, "Got connection to socket when we didn't expect it!");
487 // We get closed when the entire file is done, which guarantees we get the socket accept
488 // if we do connect to the alt-svc header