281 lines
8.1 KiB
JavaScript
281 lines
8.1 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/
|
|
*/
|
|
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
|
|
|
// This tests submitting a ping using the stand-alone pingsender program.
|
|
|
|
"use strict";
|
|
|
|
const { TelemetrySend } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/TelemetrySend.sys.mjs"
|
|
);
|
|
const { TelemetryStorage } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/TelemetryStorage.sys.mjs"
|
|
);
|
|
const { TelemetryUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/TelemetryUtils.sys.mjs"
|
|
);
|
|
const { setTimeout } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Timer.sys.mjs"
|
|
);
|
|
|
|
function generateTestPingData() {
|
|
return {
|
|
type: "test-pingsender-type",
|
|
id: TelemetryUtils.generateUUID(),
|
|
creationDate: new Date().toISOString(),
|
|
version: 4,
|
|
payload: {
|
|
dummy: "stuff",
|
|
},
|
|
};
|
|
}
|
|
|
|
function testSendingPings(pingPaths) {
|
|
const url = "http://localhost:" + PingServer.port + "/submit/telemetry/";
|
|
const pings = pingPaths.map(path => {
|
|
return {
|
|
url,
|
|
path,
|
|
};
|
|
});
|
|
TelemetrySend.testRunPingSender(pings, (_, topic, __) => {
|
|
switch (topic) {
|
|
case "process-finished": // finished indicates an exit code of 0
|
|
Assert.ok(true, "Pingsender should be able to post to localhost");
|
|
break;
|
|
case "process-failed": // failed indicates an exit code != 0
|
|
Assert.ok(false, "Pingsender should be able to post to localhost");
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Wait for a ping file to be deleted from the pending pings directory.
|
|
*/
|
|
function waitForPingDeletion(pingId) {
|
|
const path = PathUtils.join(TelemetryStorage.pingDirectoryPath, pingId);
|
|
|
|
let checkFn = (resolve, reject) =>
|
|
setTimeout(() => {
|
|
IOUtils.exists(path).then(exists => {
|
|
if (!exists) {
|
|
Assert.ok(true, `${pingId} was deleted`);
|
|
resolve();
|
|
} else {
|
|
checkFn(resolve, reject);
|
|
}
|
|
}, reject);
|
|
}, 250);
|
|
|
|
return new Promise((resolve, reject) => checkFn(resolve, reject));
|
|
}
|
|
|
|
add_task(async function setup() {
|
|
// Init the profile.
|
|
do_get_profile(true);
|
|
|
|
Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, true);
|
|
|
|
// Start the ping server and let Telemetry know about it.
|
|
PingServer.start();
|
|
});
|
|
|
|
async function test_pingSender(version = "1.0") {
|
|
// Generate a new ping and save it among the pending pings.
|
|
const data = generateTestPingData();
|
|
await TelemetryStorage.savePing(data, true);
|
|
|
|
// Get the local path of the saved ping.
|
|
const pingPath = PathUtils.join(TelemetryStorage.pingDirectoryPath, data.id);
|
|
|
|
// Spawn an HTTP server that returns an error. We will be running the
|
|
// PingSender twice, trying to send the ping to this server. After the
|
|
// second time, we will resolve |deferred404Hit|.
|
|
let failingServer = new HttpServer();
|
|
let deferred404Hit = Promise.withResolvers();
|
|
let hitCount = 0;
|
|
failingServer.registerPathHandler("/lookup_fail", (metadata, response) => {
|
|
response.setStatusLine("1.1", 404, "Not Found");
|
|
hitCount++;
|
|
|
|
if (hitCount >= 2) {
|
|
// Resolve the promise on the next tick.
|
|
Services.tm.dispatchToMainThread(() => deferred404Hit.resolve());
|
|
}
|
|
});
|
|
failingServer.start(-1);
|
|
|
|
// Try to send the ping twice using the pingsender (we expect 404 both times).
|
|
const errorUrl =
|
|
"http://localhost:" + failingServer.identity.primaryPort + "/lookup_fail";
|
|
TelemetrySend.testRunPingSender([{ url: errorUrl, path: pingPath }]);
|
|
TelemetrySend.testRunPingSender([{ url: errorUrl, path: pingPath }]);
|
|
|
|
// Wait until we hit the 404 server twice. After that, make sure that the ping
|
|
// still exists locally.
|
|
await deferred404Hit.promise;
|
|
Assert.ok(
|
|
await IOUtils.exists(pingPath),
|
|
"The pending ping must not be deleted if we fail to send using the PingSender"
|
|
);
|
|
|
|
// Try to send it using the pingsender.
|
|
testSendingPings([pingPath]);
|
|
|
|
let req = await PingServer.promiseNextRequest();
|
|
let ping = decodeRequestPayload(req);
|
|
|
|
Assert.equal(
|
|
req.getHeader("User-Agent"),
|
|
`pingsender/${version}`,
|
|
"Should have received the correct user agent string."
|
|
);
|
|
Assert.equal(
|
|
req.getHeader("X-PingSender-Version"),
|
|
version,
|
|
"Should have received the correct PingSender version string."
|
|
);
|
|
Assert.equal(
|
|
req.getHeader("Content-Encoding"),
|
|
"gzip",
|
|
"Should have a gzip encoded ping."
|
|
);
|
|
Assert.ok(req.getHeader("Date"), "Should have received a Date header.");
|
|
Assert.equal(ping.id, data.id, "Should have received the correct ping id.");
|
|
Assert.equal(
|
|
ping.type,
|
|
data.type,
|
|
"Should have received the correct ping type."
|
|
);
|
|
Assert.deepEqual(
|
|
ping.payload,
|
|
data.payload,
|
|
"Should have received the correct payload."
|
|
);
|
|
|
|
// Check that the PingSender removed the pending ping.
|
|
await waitForPingDeletion(data.id);
|
|
|
|
// Shut down the failing server.
|
|
await new Promise(r => failingServer.stop(r));
|
|
}
|
|
|
|
add_task(async function test_pingsender1() {
|
|
let orig = Services.prefs.getBoolPref(
|
|
"toolkit.telemetry.shutdownPingSender.backgroundtask.enabled",
|
|
false
|
|
);
|
|
try {
|
|
Services.prefs.setBoolPref(
|
|
"toolkit.telemetry.shutdownPingSender.backgroundtask.enabled",
|
|
false
|
|
);
|
|
await test_pingSender("1.0");
|
|
} finally {
|
|
Services.prefs.setBoolPref(
|
|
"toolkit.telemetry.shutdownPingSender.backgroundtask.enabled",
|
|
orig
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_pingsender2() {
|
|
let orig = Services.prefs.getBoolPref(
|
|
"toolkit.telemetry.shutdownPingSender.backgroundtask.enabled",
|
|
false
|
|
);
|
|
try {
|
|
Services.prefs.setBoolPref(
|
|
"toolkit.telemetry.shutdownPingSender.backgroundtask.enabled",
|
|
true
|
|
);
|
|
await test_pingSender("2.0");
|
|
} finally {
|
|
Services.prefs.setBoolPref(
|
|
"toolkit.telemetry.shutdownPingSender.backgroundtask.enabled",
|
|
orig
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_bannedDomains() {
|
|
// Generate a new ping and save it among the pending pings.
|
|
const data = generateTestPingData();
|
|
await TelemetryStorage.savePing(data, true);
|
|
|
|
// Get the local path of the saved ping.
|
|
const pingPath = PathUtils.join(TelemetryStorage.pingDirectoryPath, data.id);
|
|
|
|
// Confirm we can't send a ping to another destination url
|
|
let bannedUris = [
|
|
"https://example.com",
|
|
"http://localhost.com",
|
|
"http://localHOST.com",
|
|
"http://localhost@example.com",
|
|
"http://localhost:bob@example.com",
|
|
"http://localhost:localhost@localhost.example.com",
|
|
];
|
|
for (let url of bannedUris) {
|
|
let result = await new Promise(resolve =>
|
|
TelemetrySend.testRunPingSender(
|
|
[{ url, path: pingPath }],
|
|
(_, topic, __) => {
|
|
switch (topic) {
|
|
case "process-finished": // finished indicates an exit code of 0
|
|
case "process-failed": // failed indicates an exit code != 0
|
|
resolve(topic);
|
|
}
|
|
}
|
|
)
|
|
);
|
|
Assert.equal(
|
|
result,
|
|
"process-failed",
|
|
`Pingsender should not be able to post to ${url}`
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_pingSender_multiple_pings() {
|
|
// Generate two new pings and save them among the pending pings.
|
|
const data = [generateTestPingData(), generateTestPingData()];
|
|
|
|
for (const d of data) {
|
|
await TelemetryStorage.savePing(d, true);
|
|
}
|
|
|
|
// Get the local path of the saved pings.
|
|
const pingPaths = data.map(d =>
|
|
PathUtils.join(TelemetryStorage.pingDirectoryPath, d.id)
|
|
);
|
|
|
|
// Try to send them using the pingsender.
|
|
testSendingPings(pingPaths);
|
|
|
|
// Check the pings. We don't have an ordering guarantee, so we move the
|
|
// elements to a new array when we find them.
|
|
let data2 = [];
|
|
while (data.length) {
|
|
let req = await PingServer.promiseNextRequest();
|
|
let ping = decodeRequestPayload(req);
|
|
let idx = data.findIndex(d => d.id == ping.id);
|
|
Assert.ok(
|
|
idx >= 0,
|
|
`Should have received the correct ping id: ${data[idx].id}`
|
|
);
|
|
data2.push(data[idx]);
|
|
data.splice(idx, 1);
|
|
}
|
|
|
|
// Check that the PingSender removed the pending pings.
|
|
for (const d of data2) {
|
|
await waitForPingDeletion(d.id);
|
|
}
|
|
});
|
|
|
|
add_task(async function cleanup() {
|
|
await PingServer.stop();
|
|
});
|