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 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",
25 add_setup(async
function setup() {
28 Services
.prefs
.setBoolPref(
29 "dom.security.https_first_for_custom_ports",
33 h2Port
= Services
.env
.get("MOZHTTP2_PORT");
34 Assert
.notEqual(h2Port
, null);
35 Assert
.notEqual(h2Port
, "");
37 Services
.prefs
.setCharPref(
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",
49 Services
.prefs
.setBoolPref(
50 "network.dns.use_https_rr_for_speculative_connection",
54 registerCleanupFunction(async () => {
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"
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
);
77 function makeChan(url
) {
78 let chan
= NetUtil
.newChannel({
80 loadUsingSystemPrincipal
: true,
81 contentPolicyType
: Ci
.nsIContentPolicy
.TYPE_DOCUMENT
,
82 }).QueryInterface(Ci
.nsIHttpChannel
);
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(
94 resolve([req
, buffer
]);
96 certOverrideService
.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
101 let topic
= "http-on-modify-request";
102 Services
.obs
.addObserver(observer
, topic
);
104 chan
.asyncOpen(new ChannelListener(finish
, null, flags
));
108 class EventSinkListener
{
110 if (iid
.equals(Ci
.nsIChannelEventSink
)) {
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();
166 httpserv
.registerPathHandler("/", function handler(metadata
, response
) {
167 response
.setHeader("Content-Length", `${content.length}`);
168 response
.bodyOutputStream
.write(content
, content
.length
);
171 httpserv
.identity
.setPrimary(
173 "foo.notexisted.com",
174 httpserv
.identity
.primaryPort
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();
192 httpserv
.registerPathHandler("/", function handler(metadata
, response
) {
193 response
.setHeader("Content-Length", `${content.length}`);
194 response
.bodyOutputStream
.write(content
, content
.length
);
197 httpserv
.identity
.setPrimary(
199 "foo.notexisted.com",
200 httpserv
.identity
.primaryPort
204 `http://foo.notexisted.com:${httpserv.identity.primaryPort}/`
207 let topic
= "http-on-modify-request";
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
);
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();
232 httpserv
.registerPathHandler("/", function handler(metadata
, response
) {
233 response
.setHeader("Content-Length", `${content.length}`);
234 response
.bodyOutputStream
.write(content
, content
.length
);
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";
252 let port
= httpserv
.identity
.primaryPort
;
253 httpserv
.registerPathHandler(
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
);
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
= {
276 this.obs
= Services
.obs
;
277 this.obs
.addObserver(this, "dns-resolution-request");
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");
300 httpserv
.identity
.setPrimary(
303 httpserv
.identity
.primaryPort
307 `http://foo.blocked.com:${httpserv.identity.primaryPort}/`
310 let topic
= "http-on-modify-request";
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
),
347 Ci
.nsIProtocolProxyService
.RESOLVE_ALWAYS_TUNNEL
,
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
,
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
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");