Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

"use strict";
const { NodeHTTPSServer } = ChromeUtils.importESModule(
);
let httpServer;
let dictUrl;
function makeChan(url) {
let chan = NetUtil.newChannel({
uri: url,
loadUsingSystemPrincipal: true,
contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
}).QueryInterface(Ci.nsIHttpChannel);
return chan;
}
function channelOpenPromise(chan) {
return new Promise(resolve => {
chan.asyncOpen(
new ChannelListener(
(req, buffer) => resolve([req, buffer]),
null,
CL_ALLOW_UNKNOWN_CL
)
);
});
}
function channelOpenPromiseExpectError(chan) {
return new Promise(resolve => {
chan.asyncOpen(
new SimpleChannelListener(
(req, buffer) => resolve([req, buffer]),
null,
CL_ALLOW_UNKNOWN_CL | CL_EXPECT_FAILURE
)
);
});
}
add_setup(async function setup() {
do_get_profile();
Services.fog.initializeFOG();
Services.prefs.setBoolPref("network.http.dictionaries.enable", true);
Services.prefs.setBoolPref("network.http.encoding.zstd", true);
httpServer = new NodeHTTPSServer();
await httpServer.start();
registerCleanupFunction(async () => {
await httpServer.stop();
});
dictUrl = `https://localhost:${httpServer.port()}/dict`;
await httpServer.registerPathHandler("/dict", function (req, res) {
res.writeHead(200, {
"Content-Type": "application/octet-stream",
"Use-As-Dictionary": `match="/content*", id="test-dict", type=raw`,
"Cache-Control": "max-age=3600",
});
res.end("DICTIONARY_DATA_FOR_TESTING_PURPOSES", "binary");
});
let chan = makeChan(dictUrl);
await channelOpenPromise(chan);
});
async function registerBadDCBEndpoint(httpServer, path, badPayload) {
let escapedPayload = badPayload
.replace(/\\/g, "\\\\")
.replace(/'/g, "\\'")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r");
let func = `
let badPayload = Buffer.from('${escapedPayload}', 'binary');
if (request.headers && request.headers['available-dictionary']) {
response.writeHead(200, {
"Content-Encoding": "dcb",
"Content-Type": "text/plain",
});
response.end(badPayload);
} else {
response.writeHead(200, { "Content-Type": "text/plain" });
response.end("UNCOMPRESSED_CONTENT");
}
`;
return httpServer.registerPathHandler(
path,
new Function("request", "response", func)
);
}
async function registerBadDCZEndpoint(httpServer, path, badPayload) {
let escapedPayload = badPayload
.replace(/\\/g, "\\\\")
.replace(/'/g, "\\'")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r");
let func = `
let badPayload = Buffer.from('${escapedPayload}', 'binary');
if (request.headers && request.headers['available-dictionary']) {
response.writeHead(200, {
"Content-Encoding": "dcz",
"Content-Type": "text/plain",
});
response.end(badPayload);
} else {
response.writeHead(200, { "Content-Type": "text/plain" });
response.end("UNCOMPRESSED_CONTENT");
}
`;
return httpServer.registerPathHandler(
path,
new Function("request", "response", func)
);
}
add_task(async function test_dcb_bad_magic_header_telemetry() {
Services.fog.testResetFOG();
let eventRecorded = false;
GleanPings.contentDecodingError.testBeforeNextSubmit(() => {
eventRecorded = true;
let events = Glean.network.contentDecodingErrorReport.testGetValue();
Assert.ok(events, "Event should be recorded");
Assert.equal(events.length, 1, "Should have one event");
Assert.equal(events[0].extra.error_type, "dcb", "Error type should be dcb");
Assert.ok(events[0].extra.top_level_site, "Top level site should be set");
});
let badMagicPayload =
"\xDE\xAD\xBE\xEF12345678901234567890123456789012GARBAGE_DATA_HERE";
await registerBadDCBEndpoint(
httpServer,
"/content-bad-magic",
badMagicPayload
);
let chan = makeChan(
`https://localhost:${httpServer.port()}/content-bad-magic`
);
await channelOpenPromiseExpectError(chan);
Services.obs.notifyObservers(null, "idle-daily");
Assert.ok(
eventRecorded,
"Event should have been recorded and ping submitted"
);
});
add_task(async function test_dcb_brotli_decode_error_telemetry() {
Services.fog.testResetFOG();
let eventRecorded = false;
GleanPings.contentDecodingError.testBeforeNextSubmit(() => {
eventRecorded = true;
let events = Glean.network.contentDecodingErrorReport.testGetValue();
Assert.ok(events, "Event should be recorded");
Assert.equal(events.length, 1, "Should have one event");
Assert.equal(events[0].extra.error_type, "dcb", "Error type should be dcb");
});
let badBrotliPayload =
"\xff\x44\x43\x4212345678901234567890123456789012NOT_VALID_BROTLI_DATA_XXXXX";
await registerBadDCBEndpoint(
httpServer,
"/content-bad-brotli",
badBrotliPayload
);
let chan = makeChan(
`https://localhost:${httpServer.port()}/content-bad-brotli`
);
await channelOpenPromiseExpectError(chan);
Services.obs.notifyObservers(null, "idle-daily");
Assert.ok(
eventRecorded,
"Event should have been recorded and ping submitted"
);
});
add_task(async function test_dcz_decode_error_telemetry() {
Services.fog.testResetFOG();
let eventRecorded = false;
GleanPings.contentDecodingError.testBeforeNextSubmit(() => {
eventRecorded = true;
let events = Glean.network.contentDecodingErrorReport.testGetValue();
Assert.ok(events, "Event should be recorded");
Assert.equal(events.length, 1, "Should have one event");
Assert.equal(events[0].extra.error_type, "dcz", "Error type should be dcz");
});
let badZstdPayload = "NOT_VALID_ZSTD_COMPRESSED_DATA_XXXXXXXXXXXXXXXXXXXXX";
await registerBadDCZEndpoint(httpServer, "/content-bad-zstd", badZstdPayload);
let chan = makeChan(
`https://localhost:${httpServer.port()}/content-bad-zstd`
);
await channelOpenPromiseExpectError(chan);
Services.obs.notifyObservers(null, "idle-daily");
Assert.ok(
eventRecorded,
"Event should have been recorded and ping submitted"
);
});
add_task(async function test_dict_hash_mismatch_telemetry() {
Services.fog.testResetFOG();
let cacheTesting = Services.cache2.QueryInterface(Ci.nsICacheTesting);
cacheTesting.corruptDictionaryHash(dictUrl);
Services.obs.notifyObservers(null, "clear-dictionary-data", dictUrl);
await httpServer.registerPathHandler("/content-plain", function (req, res) {
res.writeHead(200, {
"Content-Type": "text/plain",
"Cache-Control": "no-cache",
});
res.end("PLAIN_CONTENT_RESPONSE");
});
let chan = makeChan(`https://localhost:${httpServer.port()}/content-plain`);
await channelOpenPromise(chan);
let events = Glean.network.contentDecodingErrorReport.testGetValue();
Assert.ok(events, "Event should be recorded before idle-daily");
Assert.equal(events.length, 1, "Should have one event");
Assert.equal(
events[0].extra.error_type,
"dict_hash_mismatch",
"Error type should be dict_hash_mismatch"
);
Assert.ok(events[0].extra.top_level_site, "Top level site should be set");
});