220 lines
5.7 KiB
JavaScript
220 lines
5.7 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
var { setTimeout } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Timer.sys.mjs"
|
|
);
|
|
|
|
let trrServer;
|
|
let h3Port;
|
|
let host;
|
|
|
|
add_setup(async function setup() {
|
|
trr_test_setup();
|
|
|
|
h3Port = Services.env.get("MOZHTTP3_PORT_ECH");
|
|
Assert.notEqual(h3Port, null);
|
|
Assert.notEqual(h3Port, "");
|
|
|
|
host = `https://alt1.example.com:${h3Port}/`;
|
|
|
|
Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST);
|
|
});
|
|
|
|
registerCleanupFunction(async () => {
|
|
trr_clear_prefs();
|
|
if (trrServer) {
|
|
await trrServer.stop();
|
|
}
|
|
});
|
|
|
|
function makeChan(url) {
|
|
let chan = NetUtil.newChannel({
|
|
uri: url,
|
|
loadUsingSystemPrincipal: true,
|
|
contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
|
|
}).QueryInterface(Ci.nsIHttpChannel);
|
|
return chan;
|
|
}
|
|
|
|
function channelOpenPromise(chan, flags) {
|
|
return new Promise(resolve => {
|
|
function finish(req, buffer) {
|
|
resolve([req, buffer]);
|
|
}
|
|
chan.asyncOpen(new ChannelListener(finish, null, flags));
|
|
});
|
|
}
|
|
|
|
function ActivityObserver() {
|
|
this.activites = [];
|
|
}
|
|
|
|
ActivityObserver.prototype = {
|
|
activites: [],
|
|
observeActivity(
|
|
aHttpChannel,
|
|
aActivityType,
|
|
aActivitySubtype,
|
|
aTimestamp,
|
|
aExtraSizeData,
|
|
aExtraStringData
|
|
) {
|
|
try {
|
|
aHttpChannel.QueryInterface(Ci.nsINullChannel);
|
|
aHttpChannel.QueryInterface(Ci.nsIChannel);
|
|
if (aHttpChannel.URI.spec === host) {
|
|
dump(
|
|
"*** HTTP Activity 0x" +
|
|
aActivityType.toString(16) +
|
|
" 0x" +
|
|
aActivitySubtype.toString(16) +
|
|
" " +
|
|
aExtraStringData +
|
|
"\n"
|
|
);
|
|
this.activites.push({ host, subType: aActivitySubtype });
|
|
}
|
|
} catch (e) {}
|
|
},
|
|
};
|
|
|
|
function checkHttpActivities(activites, expected) {
|
|
let foundTransClosed = false;
|
|
for (let activity of activites) {
|
|
switch (activity.subType) {
|
|
case Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE:
|
|
foundTransClosed = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert.equal(
|
|
foundTransClosed,
|
|
expected,
|
|
"The activity of speculative transaction should match"
|
|
);
|
|
}
|
|
// Test steps:
|
|
// 1. Create a TRR server that serves the HTTPS record for the H3 server.
|
|
// 2. Create a channel and connect to the H3 server.
|
|
// 3. Use nsIHttpActivityObserver to observe if we receive the HTTP activity
|
|
// that is sent from the speculative transaction.
|
|
async function doTestAltSVCWithHTTPSRR(foundActivity) {
|
|
trrServer = new TRRServer();
|
|
await trrServer.start();
|
|
|
|
let observerService = Cc[
|
|
"@mozilla.org/network/http-activity-distributor;1"
|
|
].getService(Ci.nsIHttpActivityDistributor);
|
|
|
|
Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY);
|
|
Services.prefs.setCharPref(
|
|
"network.trr.uri",
|
|
`https://foo.example.com:${trrServer.port()}/dns-query`
|
|
);
|
|
|
|
let portPrefixedName = `_${h3Port}._https.alt1.example.com`;
|
|
let vals = [
|
|
{ key: "alpn", value: "h3" },
|
|
{ key: "port", value: h3Port },
|
|
];
|
|
|
|
await trrServer.registerDoHAnswers(portPrefixedName, "HTTPS", {
|
|
answers: [
|
|
{
|
|
name: portPrefixedName,
|
|
ttl: 55,
|
|
type: "HTTPS",
|
|
flush: false,
|
|
data: {
|
|
priority: 1,
|
|
name: ".",
|
|
values: vals,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
await trrServer.registerDoHAnswers("alt1.example.com", "A", {
|
|
answers: [
|
|
{
|
|
name: "alt1.example.com",
|
|
ttl: 55,
|
|
type: "A",
|
|
flush: false,
|
|
data: "127.0.0.1",
|
|
},
|
|
],
|
|
});
|
|
|
|
let chan = makeChan(`${host}alt_svc_header`);
|
|
let h3AltSvc = ":" + h3Port;
|
|
chan.setRequestHeader("x-altsvc", h3AltSvc, false);
|
|
let observer = new ActivityObserver();
|
|
|
|
let responseObserver = {
|
|
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
|
|
observe(aSubject, aTopic) {
|
|
let channel = aSubject.QueryInterface(Ci.nsIChannel);
|
|
if (
|
|
aTopic == "http-on-examine-response" &&
|
|
channel.URI.spec === `${host}alt_svc_header`
|
|
) {
|
|
Services.obs.removeObserver(
|
|
responseObserver,
|
|
"http-on-examine-response"
|
|
);
|
|
|
|
observerService.addObserver(observer);
|
|
channel.suspend();
|
|
// We need to close all connections here, otherwise we are not allowed
|
|
// to create a specul connection to validate the Alt-svc header.
|
|
Services.obs.notifyObservers(null, "net:cancel-all-connections");
|
|
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
|
setTimeout(function () {
|
|
channel.resume();
|
|
}, 3000);
|
|
}
|
|
},
|
|
};
|
|
Services.obs.addObserver(responseObserver, "http-on-examine-response");
|
|
|
|
await channelOpenPromise(chan);
|
|
|
|
// Some dekay here to collect HTTP activites.
|
|
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
|
|
checkHttpActivities(observer.activites, foundActivity);
|
|
|
|
await trrServer.stop();
|
|
observerService.removeObserver(observer);
|
|
}
|
|
|
|
add_task(async function testAltSVCWithHTTPSRR() {
|
|
Services.prefs.setBoolPref(
|
|
"network.http.skip_alt_svc_validation_on_https_rr",
|
|
false
|
|
);
|
|
|
|
await doTestAltSVCWithHTTPSRR(true);
|
|
|
|
// Clear the alt-svc mapping.
|
|
Services.obs.notifyObservers(null, "last-pb-context-exited");
|
|
Services.obs.notifyObservers(null, "net:cancel-all-connections");
|
|
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
|
|
Services.prefs.setBoolPref(
|
|
"network.http.skip_alt_svc_validation_on_https_rr",
|
|
true
|
|
);
|
|
|
|
await doTestAltSVCWithHTTPSRR(false);
|
|
});
|