Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'android' OR os == 'mac' && os_version == '10.15' && processor == 'x86_64' && opt OR os == 'mac' && os_version == '11.20' && arch == 'aarch64' && opt OR asan OR os == 'win'
- Manifest: netwerk/test/unit/xpcshell.toml
/* 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
"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, "");
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",
);
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);
});