3 const { HttpServer } = ChromeUtils.importESModule(
4 "resource://testing-common/httpd.sys.mjs"
13 // https://foo.example.com:(h2Port)
14 // https://bar.example.com:(h2Port) <- invalid for bar, but ok for foo
15 var h1Foo; // server http://foo.example.com:(h1Foo.identity.primaryPort)
16 var h1Bar; // server http://bar.example.com:(h1bar.identity.primaryPort)
18 var otherServer; // server socket listening for other connection.
20 var h2FooRoute; // foo.example.com:H2PORT
21 var h2BarRoute; // bar.example.com:H2PORT
22 var h2Route; // :H2PORT
23 var httpFooOrigin; // http://foo.exmaple.com:PORT/
24 var httpsFooOrigin; // https://foo.exmaple.com:PORT/
25 var httpBarOrigin; // http://bar.example.com:PORT/
26 var httpsBarOrigin; // https://bar.example.com:PORT/
29 h2Port = Services.env.get("MOZHTTP2_PORT");
30 Assert.notEqual(h2Port, null);
31 Assert.notEqual(h2Port, "");
33 // Set to allow the cert presented by our H2 server
35 prefs = Services.prefs;
37 http2pref = prefs.getBoolPref("network.http.http2.enabled");
38 altsvcpref1 = prefs.getBoolPref("network.http.altsvc.enabled");
39 altsvcpref2 = prefs.getBoolPref("network.http.altsvc.oe", true);
41 prefs.setBoolPref("network.http.http2.enabled", true);
42 prefs.setBoolPref("network.http.altsvc.enabled", true);
43 prefs.setBoolPref("network.http.altsvc.oe", true);
45 "network.dns.localDomains",
46 "foo.example.com, bar.example.com"
49 // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
50 // so add that cert to the trust list as a signing cert. The same cert is used
51 // for both h2FooRoute and h2BarRoute though it is only valid for
52 // the foo.example.com domain name.
53 let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
56 addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
58 h1Foo = new HttpServer();
59 h1Foo.registerPathHandler("/altsvc-test", h1Server);
60 h1Foo.registerPathHandler("/.well-known/http-opportunistic", h1ServerWK);
62 h1Foo.identity.setPrimary(
65 h1Foo.identity.primaryPort
68 h1Bar = new HttpServer();
69 h1Bar.registerPathHandler("/altsvc-test", h1Server);
71 h1Bar.identity.setPrimary(
74 h1Bar.identity.primaryPort
77 h2FooRoute = "foo.example.com:" + h2Port;
78 h2BarRoute = "bar.example.com:" + h2Port;
79 h2Route = ":" + h2Port;
81 httpFooOrigin = "http://foo.example.com:" + h1Foo.identity.primaryPort + "/";
82 httpsFooOrigin = "https://" + h2FooRoute + "/";
83 httpBarOrigin = "http://bar.example.com:" + h1Bar.identity.primaryPort + "/";
84 httpsBarOrigin = "https://" + h2BarRoute + "/";
103 function h1Server(metadata, response) {
104 response.setStatusLine(metadata.httpVersion, 200, "OK");
105 response.setHeader("Content-Type", "text/plain", false);
106 response.setHeader("Connection", "close", false);
107 response.setHeader("Cache-Control", "no-cache", false);
108 response.setHeader("Access-Control-Allow-Origin", "*", false);
109 response.setHeader("Access-Control-Allow-Method", "GET", false);
110 response.setHeader("Access-Control-Allow-Headers", "x-altsvc", false);
113 var hval = "h2=" + metadata.getHeader("x-altsvc");
114 response.setHeader("Alt-Svc", hval, false);
117 var body = "Q: What did 0 say to 8? A: Nice Belt!\n";
118 response.bodyOutputStream.write(body, body.length);
121 function h1ServerWK(metadata, response) {
122 response.setStatusLine(metadata.httpVersion, 200, "OK");
123 response.setHeader("Content-Type", "application/json", false);
124 response.setHeader("Connection", "close", false);
125 response.setHeader("Cache-Control", "no-cache", false);
126 response.setHeader("Access-Control-Allow-Origin", "*", false);
127 response.setHeader("Access-Control-Allow-Method", "GET", false);
128 response.setHeader("Access-Control-Allow-Headers", "x-altsvc", false);
130 var body = '["http://foo.example.com:' + h1Foo.identity.primaryPort + '"]';
131 response.bodyOutputStream.write(body, body.length);
134 function resetPrefs() {
135 prefs.setBoolPref("network.http.http2.enabled", http2pref);
136 prefs.setBoolPref("network.http.altsvc.enabled", altsvcpref1);
137 prefs.setBoolPref("network.http.altsvc.oe", altsvcpref2);
138 prefs.clearUserPref("network.dns.localDomains");
139 prefs.clearUserPref("network.security.ports.banned");
142 function makeChan(origin) {
143 return NetUtil.newChannel({
144 uri: origin + "altsvc-test",
145 loadUsingSystemPrincipal: true,
146 }).QueryInterface(Ci.nsIHttpChannel);
151 var loadWithoutClearingMappings = false;
152 var disallowH3 = false;
153 var disallowH2 = false;
155 var expectPass = true;
157 var originAttributes = {};
159 var Listener = function () {};
160 Listener.prototype = {
161 onStartRequest: function testOnStartRequest(request) {
162 Assert.ok(request instanceof Ci.nsIHttpChannel);
165 if (!Components.isSuccessCode(request.status)) {
167 "Channel should have a success code! (" + request.status + ")"
170 Assert.equal(request.responseStatus, 200);
172 Assert.equal(Components.isSuccessCode(request.status), false);
176 onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
177 read_stream(stream, cnt);
180 onStopRequest: function testOnStopRequest(request, status) {
183 routed = request.getRequestHeader("Alt-Used");
185 dump("routed is " + routed + "\n");
186 Assert.equal(Components.isSuccessCode(status), expectPass);
189 Assert.equal(routed, "");
191 loadWithoutClearingMappings = true;
192 do_timeout(waitFor, doTest);
195 } else if (xaltsvc == "NA") {
196 Assert.equal(routed, "");
198 } else if (routed == xaltsvc) {
199 Assert.equal(routed, xaltsvc); // always true, but a useful log
202 dump("poll later for alt svc mapping\n");
204 loadWithoutClearingMappings = true;
205 do_timeout(500, doTest);
212 function testsDone() {
218 h1Foo.stop(do_test_finished);
220 h1Bar.stop(do_test_finished);
224 dump("execute doTest " + origin + "\n");
225 var chan = makeChan(origin);
226 var listener = new Listener();
227 if (xaltsvc != "NA") {
228 chan.setRequestHeader("x-altsvc", xaltsvc, false);
230 if (loadWithoutClearingMappings) {
231 chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
234 Ci.nsIRequest.LOAD_FRESH_CONNECTION |
235 Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
238 let internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
239 internalChannel.allowHttp3 = false;
243 let internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
244 internalChannel.allowSpdy = false;
247 loadWithoutClearingMappings = false;
248 chan.loadInfo.originAttributes = originAttributes;
249 chan.asyncOpen(listener);
252 // xaltsvc is overloaded to do two things..
253 // 1] it is sent in the x-altsvc request header, and the response uses the value in the Alt-Svc response header
254 // 2] the test polls until necko sets Alt-Used to that value (i.e. it uses that route)
256 // When xaltsvc is set to h2Route (i.e. :port with the implied hostname) it doesn't match the alt-used,
257 // which is always explicit, so it needs to be changed after the channel is created but before the
258 // listener is invoked
260 // http://foo served from h2=:port
263 origin = httpFooOrigin;
268 xaltsvc = h2FooRoute;
271 // http://foo served from h2=foo:port
274 origin = httpFooOrigin;
275 xaltsvc = h2FooRoute;
281 // http://foo served from h2=bar:port
282 // requires cert for foo
285 origin = httpFooOrigin;
286 xaltsvc = h2BarRoute;
292 // https://bar should fail because host bar has cert for foo
295 origin = httpsBarOrigin;
303 // https://foo no alt-svc (just check cert setup)
306 origin = httpsFooOrigin;
314 // https://foo via bar (bar has cert for foo)
317 origin = httpsFooOrigin;
318 xaltsvc = h2BarRoute;
324 // check again https://bar should fail because host bar has cert for foo
327 origin = httpsBarOrigin;
335 // http://bar via h2 on bar
336 // should not use TLS/h2 because h2BarRoute is not auth'd for bar
337 // however the test ought to PASS (i.e. get a 200) because fallback
338 // to plaintext happens.. thus the timeout
341 origin = httpBarOrigin;
342 xaltsvc = h2BarRoute;
350 // http://bar served from h2=:port, which is like the bar route in 8
353 origin = httpBarOrigin;
360 xaltsvc = h2BarRoute;
363 // check again https://bar should fail because host bar has cert for foo
364 function doTest10() {
365 dump("doTest10()\n");
366 origin = httpsBarOrigin;
374 // http://bar served from h2=foo, should fail because host foo only has
375 // cert for foo. Fail in this case means alt-svc is not used, but content
377 function doTest11() {
378 dump("doTest11()\n");
379 origin = httpBarOrigin;
380 xaltsvc = h2FooRoute;
389 // Insert a cache of http://foo served from h2=:port with origin attributes.
390 function doTest12() {
391 dump("doTest12()\n");
392 origin = httpFooOrigin;
396 firstPartyDomain: "a.com",
401 xaltsvc = h2FooRoute;
404 // Make sure we get a cache miss with a different userContextId.
405 function doTest13() {
406 dump("doTest13()\n");
407 origin = httpFooOrigin;
411 firstPartyDomain: "a.com",
413 loadWithoutClearingMappings = true;
419 // Make sure we get a cache miss with a different firstPartyDomain.
420 function doTest14() {
421 dump("doTest14()\n");
422 origin = httpFooOrigin;
426 firstPartyDomain: "b.com",
428 loadWithoutClearingMappings = true;
434 // Make sure we get a cache hit with the same origin attributes.
435 function doTest15() {
436 dump("doTest15()\n");
437 origin = httpFooOrigin;
441 firstPartyDomain: "a.com",
443 loadWithoutClearingMappings = true;
447 // This ensures a cache hit.
448 xaltsvc = h2FooRoute;
451 // Make sure we do not use H2 if it is disabled on a channel.
452 function doTest16() {
453 dump("doTest16()\n");
454 origin = httpFooOrigin;
459 firstPartyDomain: "a.com",
461 loadWithoutClearingMappings = true;
467 // Make sure we use H2 if only Http3 is disabled on a channel.
468 function doTest17() {
469 dump("doTest17()\n");
470 origin = httpFooOrigin;
475 firstPartyDomain: "a.com",
477 loadWithoutClearingMappings = true;
481 // This should ensures a cache hit.
482 xaltsvc = h2FooRoute;
485 // Check we don't connect to blocked ports
486 function doTest18() {
487 dump("doTest18()\n");
488 origin = httpFooOrigin;
489 nextTest = testsDone;
490 otherServer = Cc["@mozilla.org/network/server-socket;1"].createInstance(
493 otherServer.init(-1, true, -1);
494 xaltsvc = "localhost:" + otherServer.port;
495 Services.prefs.setCharPref(
496 "network.security.ports.banned",
497 "" + otherServer.port
499 dump("Blocked port: " + otherServer.port);
501 otherServer.asyncListen({
503 Assert.ok(false, "Got connection to socket when we didn't expect it!");
506 // We get closed when the entire file is done, which guarantees we get the socket accept
507 // if we do connect to the alt-svc header
516 // Check we don't connect to blocked ports
517 function doTest19() {
518 dump("doTest19()\n");
519 origin = httpFooOrigin;
520 nextTest = testsDone;
521 otherServer = Cc["@mozilla.org/network/server-socket;1"].createInstance(
524 const BAD_PORT_U32 = 6667 + 65536;
525 otherServer.init(BAD_PORT_U32, true, -1);
526 Assert.ok(otherServer.port == 6667, "Trying to listen on port 6667");
527 xaltsvc = "localhost:" + BAD_PORT_U32;
528 dump("Blocked port: " + otherServer.port);
530 otherServer.asyncListen({
532 Assert.ok(false, "Got connection to socket when we didn't expect it!");
535 // We get closed when the entire file is done, which guarantees we get the socket accept
536 // if we do connect to the alt-svc header
544 function doTest20() {
545 dump("doTest20()\n");
546 origin = httpFooOrigin;
547 nextTest = testsDone;
548 otherServer = Cc["@mozilla.org/network/server-socket;1"].createInstance(
551 const BAD_PORT_U64 = 6666 + 429496729;
552 otherServer.init(6666, true, -1);
553 Assert.ok(otherServer.port == 6666, "Trying to listen on port 6666");
554 xaltsvc = "localhost:" + BAD_PORT_U64;
555 dump("Blocked port: " + otherServer.port);
557 otherServer.asyncListen({
559 Assert.ok(false, "Got connection to socket when we didn't expect it!");
562 // We get closed when the entire file is done, which guarantees we get the socket accept
563 // if we do connect to the alt-svc header
571 // Port 65535 should be OK
572 function doTest21() {
573 dump("doTest21()\n");
574 origin = httpFooOrigin;
575 nextTest = testsDone;
576 otherServer = Cc["@mozilla.org/network/server-socket;1"].createInstance(
579 const GOOD_PORT = 65535;
580 otherServer.init(65535, true, -1);
581 Assert.ok(otherServer.port == 65535, "Trying to listen on port 65535");
582 xaltsvc = "localhost:" + GOOD_PORT;
583 dump("Allowed port: " + otherServer.port);
585 otherServer.asyncListen({
587 Assert.ok(true, "Got connection to socket when we didn't expect it!");
590 // We get closed when the entire file is done, which guarantees we get the socket accept
591 // if we do connect to the alt-svc header