Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / test / unit / test_httpssvc_https_upgrade.js
blob5465b4383b47d85154cd9fdbabd4cacab4e771e0
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 const certOverrideService = Cc[
8 "@mozilla.org/security/certoverride;1"
9 ].getService(Ci.nsICertOverrideService);
10 const { HttpServer } = ChromeUtils.importESModule(
11 "resource://testing-common/httpd.sys.mjs"
13 const { TestUtils } = ChromeUtils.importESModule(
14 "resource://testing-common/TestUtils.sys.mjs"
17 const ReferrerInfo = Components.Constructor(
18 "@mozilla.org/referrer-info;1",
19 "nsIReferrerInfo",
20 "init"
23 let h2Port;
25 add_setup(async function setup() {
26 trr_test_setup();
28 Services.prefs.setBoolPref(
29 "dom.security.https_first_for_custom_ports",
30 false
33 h2Port = Services.env.get("MOZHTTP2_PORT");
34 Assert.notEqual(h2Port, null);
35 Assert.notEqual(h2Port, "");
37 Services.prefs.setCharPref(
38 "network.trr.uri",
39 "https://foo.example.com:" + h2Port + "/httpssvc_as_altsvc"
42 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
43 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
44 Services.prefs.setBoolPref(
45 "network.dns.https_rr.check_record_with_cname",
46 false
49 Services.prefs.setBoolPref(
50 "network.dns.use_https_rr_for_speculative_connection",
51 true
54 registerCleanupFunction(async () => {
55 trr_clear_prefs();
56 Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr");
57 Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc");
58 Services.prefs.clearUserPref(
59 "network.dns.use_https_rr_for_speculative_connection"
61 Services.prefs.clearUserPref("network.dns.notifyResolution");
62 Services.prefs.clearUserPref("network.dns.disablePrefetch");
63 Services.prefs.clearUserPref("dom.security.https_first_for_custom_ports");
64 Services.prefs.clearUserPref(
65 "network.dns.https_rr.check_record_with_cname"
67 });
69 if (mozinfo.socketprocess_networking) {
70 Services.dns; // Needed to trigger socket process.
71 await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
74 Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY);
75 });
77 function makeChan(url) {
78 let chan = NetUtil.newChannel({
79 uri: url,
80 loadUsingSystemPrincipal: true,
81 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
82 }).QueryInterface(Ci.nsIHttpChannel);
83 return chan;
86 // When observer is specified, the channel will be suspended when receiving
87 // "http-on-modify-request".
88 function channelOpenPromise(chan, flags, observer) {
89 return new Promise(resolve => {
90 function finish(req, buffer) {
91 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
92 false
94 resolve([req, buffer]);
96 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
97 true
100 if (observer) {
101 let topic = "http-on-modify-request";
102 Services.obs.addObserver(observer, topic);
104 chan.asyncOpen(new ChannelListener(finish, null, flags));
108 class EventSinkListener {
109 getInterface(iid) {
110 if (iid.equals(Ci.nsIChannelEventSink)) {
111 return this;
113 throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
115 asyncOnChannelRedirect(oldChan, newChan, flags, callback) {
116 Assert.equal(oldChan.URI.hostPort, newChan.URI.hostPort);
117 Assert.equal(oldChan.URI.scheme, "http");
118 Assert.equal(newChan.URI.scheme, "https");
119 callback.onRedirectVerifyCallback(Cr.NS_OK);
123 EventSinkListener.prototype.QueryInterface = ChromeUtils.generateQI([
124 "nsIInterfaceRequestor",
125 "nsIChannelEventSink",
128 // Test if the request is upgraded to https with a HTTPSSVC record.
129 add_task(async function testUseHTTPSSVCAsHSTS() {
130 Services.dns.clearCache(true);
131 // Do DNS resolution before creating the channel, so the HTTPSSVC record will
132 // be resolved from the cache.
133 await new TRRDNSListener("test.httpssvc.com", {
134 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
137 // Since the HTTPS RR should be served from cache, the DNS record is available
138 // before nsHttpChannel::MaybeUseHTTPSRRForUpgrade() is called.
139 let chan = makeChan(`http://test.httpssvc.com:80/server-timing`);
140 let listener = new EventSinkListener();
141 chan.notificationCallbacks = listener;
143 let [req] = await channelOpenPromise(chan);
145 req.QueryInterface(Ci.nsIHttpChannel);
146 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes");
148 chan = makeChan(`http://test.httpssvc.com:80/server-timing`);
149 listener = new EventSinkListener();
150 chan.notificationCallbacks = listener;
152 [req] = await channelOpenPromise(chan);
154 req.QueryInterface(Ci.nsIHttpChannel);
155 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes");
158 // Test the case that we got an invalid DNS response. In this case,
159 // nsHttpChannel::OnHTTPSRRAvailable is called after
160 // nsHttpChannel::MaybeUseHTTPSRRForUpgrade.
161 add_task(async function testInvalidDNSResult() {
162 Services.dns.clearCache(true);
164 let httpserv = new HttpServer();
165 let content = "ok";
166 httpserv.registerPathHandler("/", function handler(metadata, response) {
167 response.setHeader("Content-Length", `${content.length}`);
168 response.bodyOutputStream.write(content, content.length);
170 httpserv.start(-1);
171 httpserv.identity.setPrimary(
172 "http",
173 "foo.notexisted.com",
174 httpserv.identity.primaryPort
177 let chan = makeChan(
178 `http://foo.notexisted.com:${httpserv.identity.primaryPort}/`
180 let [, response] = await channelOpenPromise(chan);
181 Assert.equal(response, content);
182 await new Promise(resolve => httpserv.stop(resolve));
185 // The same test as above, but nsHttpChannel::MaybeUseHTTPSRRForUpgrade is
186 // called after nsHttpChannel::OnHTTPSRRAvailable.
187 add_task(async function testInvalidDNSResult1() {
188 Services.dns.clearCache(true);
190 let httpserv = new HttpServer();
191 let content = "ok";
192 httpserv.registerPathHandler("/", function handler(metadata, response) {
193 response.setHeader("Content-Length", `${content.length}`);
194 response.bodyOutputStream.write(content, content.length);
196 httpserv.start(-1);
197 httpserv.identity.setPrimary(
198 "http",
199 "foo.notexisted.com",
200 httpserv.identity.primaryPort
203 let chan = makeChan(
204 `http://foo.notexisted.com:${httpserv.identity.primaryPort}/`
207 let topic = "http-on-modify-request";
208 let observer = {
209 QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
210 observe(aSubject, aTopic) {
211 if (aTopic == topic) {
212 Services.obs.removeObserver(observer, topic);
213 let channel = aSubject.QueryInterface(Ci.nsIChannel);
214 channel.suspend();
216 new TRRDNSListener("foo.notexisted.com", {
217 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
218 expectedSuccess: false,
219 }).then(() => channel.resume());
224 let [, response] = await channelOpenPromise(chan, 0, observer);
225 Assert.equal(response, content);
226 await new Promise(resolve => httpserv.stop(resolve));
229 add_task(async function testLiteralIP() {
230 let httpserv = new HttpServer();
231 let content = "ok";
232 httpserv.registerPathHandler("/", function handler(metadata, response) {
233 response.setHeader("Content-Length", `${content.length}`);
234 response.bodyOutputStream.write(content, content.length);
236 httpserv.start(-1);
238 let chan = makeChan(`http://127.0.0.1:${httpserv.identity.primaryPort}/`);
239 let [, response] = await channelOpenPromise(chan);
240 Assert.equal(response, content);
241 await new Promise(resolve => httpserv.stop(resolve));
244 // Test the case that an HTTPS RR is available and the server returns a 307
245 // for redirecting back to http.
246 add_task(async function testEndlessUpgradeDowngrade() {
247 Services.dns.clearCache(true);
249 let httpserv = new HttpServer();
250 let content = "okok";
251 httpserv.start(-1);
252 let port = httpserv.identity.primaryPort;
253 httpserv.registerPathHandler(
254 `/redirect_to_http`,
255 function handler(metadata, response) {
256 response.setHeader("Content-Length", `${content.length}`);
257 response.bodyOutputStream.write(content, content.length);
260 httpserv.identity.setPrimary("http", "test.httpsrr.redirect.com", port);
262 let chan = makeChan(
263 `http://test.httpsrr.redirect.com:${port}/redirect_to_http?port=${port}`
266 let [, response] = await channelOpenPromise(chan);
267 Assert.equal(response, content);
268 await new Promise(resolve => httpserv.stop(resolve));
271 add_task(async function testHttpRequestBlocked() {
272 Services.dns.clearCache(true);
274 let dnsRequestObserver = {
275 register() {
276 this.obs = Services.obs;
277 this.obs.addObserver(this, "dns-resolution-request");
279 unregister() {
280 if (this.obs) {
281 this.obs.removeObserver(this, "dns-resolution-request");
284 observe(subject, topic) {
285 if (topic == "dns-resolution-request") {
286 Assert.ok(false, "unreachable");
291 dnsRequestObserver.register();
292 Services.prefs.setBoolPref("network.dns.notifyResolution", true);
293 Services.prefs.setBoolPref("network.dns.disablePrefetch", true);
295 let httpserv = new HttpServer();
296 httpserv.registerPathHandler("/", function handler() {
297 Assert.ok(false, "unreachable");
299 httpserv.start(-1);
300 httpserv.identity.setPrimary(
301 "http",
302 "foo.blocked.com",
303 httpserv.identity.primaryPort
306 let chan = makeChan(
307 `http://foo.blocked.com:${httpserv.identity.primaryPort}/`
310 let topic = "http-on-modify-request";
311 let observer = {
312 QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
313 observe(aSubject, aTopic) {
314 if (aTopic == topic) {
315 Services.obs.removeObserver(observer, topic);
316 let channel = aSubject.QueryInterface(Ci.nsIChannel);
317 channel.cancel(Cr.NS_BINDING_ABORTED);
322 let [request] = await channelOpenPromise(chan, CL_EXPECT_FAILURE, observer);
323 request.QueryInterface(Ci.nsIHttpChannel);
324 Assert.equal(request.status, Cr.NS_BINDING_ABORTED);
325 dnsRequestObserver.unregister();
326 await new Promise(resolve => httpserv.stop(resolve));
329 function createPrincipal(url) {
330 return Services.scriptSecurityManager.createContentPrincipal(
331 Services.io.newURI(url),
336 // Test if the Origin header stays the same after an internal HTTPS upgrade
337 // caused by HTTPS RR.
338 add_task(async function testHTTPSRRUpgradeWithOriginHeader() {
339 Services.dns.clearCache(true);
341 const url = "http://test.httpssvc.com:80/origin_header";
342 const originURL = "http://example.com";
343 let chan = Services.io
344 .newChannelFromURIWithProxyFlags(
345 Services.io.newURI(url),
346 null,
347 Ci.nsIProtocolProxyService.RESOLVE_ALWAYS_TUNNEL,
348 null,
349 createPrincipal(originURL),
350 createPrincipal(url),
351 Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
352 Ci.nsIContentPolicy.TYPE_DOCUMENT
354 .QueryInterface(Ci.nsIHttpChannel);
355 chan.referrerInfo = new ReferrerInfo(
356 Ci.nsIReferrerInfo.EMPTY,
357 true,
358 NetUtil.newURI(url)
360 chan.setRequestHeader("Origin", originURL, false);
362 let [req, buf] = await channelOpenPromise(chan);
364 req.QueryInterface(Ci.nsIHttpChannel);
365 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes");
366 Assert.equal(buf, originURL);
369 // See bug 1899841. Test the case when network.dns.use_https_rr_as_altsvc
370 // is disabled.
371 add_task(async function testPrefDisabled() {
372 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", false);
374 let chan = makeChan(`https://test.httpssvc.com:${h2Port}/server-timing`);
375 let [req] = await channelOpenPromise(chan);
377 req.QueryInterface(Ci.nsIHttpChannel);
378 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes");