154 lines
5.3 KiB
JavaScript
154 lines
5.3 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/. */
|
|
|
|
import { Log } from "resource://gre/modules/Log.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
CommonUtils: "resource://services-common/utils.sys.mjs",
|
|
ServiceRequest: "resource://gre/modules/ServiceRequest.sys.mjs",
|
|
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
|
|
});
|
|
|
|
const COVERAGE_VERSION = "2";
|
|
|
|
const COVERAGE_ENABLED_PREF = "toolkit.coverage.enabled";
|
|
const LOG_LEVEL_PREF = "toolkit.coverage.log-level";
|
|
const OPT_OUT_PREF = "toolkit.coverage.opt-out";
|
|
const ALREADY_RUN_PREF = `toolkit.coverage.already-run.v${COVERAGE_VERSION}`;
|
|
const COVERAGE_UUID_PREF = `toolkit.coverage.uuid.v${COVERAGE_VERSION}`;
|
|
const TELEMETRY_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
|
|
const REPORTING_ENDPOINT_BASE_PREF = `toolkit.coverage.endpoint.base`;
|
|
const REPORTING_ENDPOINT = "submit/coverage/coverage";
|
|
const PING_SUBMISSION_TIMEOUT = 30 * 1000; // 30 seconds
|
|
|
|
const log = Log.repository.getLogger("Telemetry::CoveragePing");
|
|
log.level = Services.prefs.getIntPref(LOG_LEVEL_PREF, Log.Level.Error);
|
|
log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
|
|
|
|
export var CoveragePing = Object.freeze({
|
|
async startup() {
|
|
if (!Services.prefs.getBoolPref(COVERAGE_ENABLED_PREF, false)) {
|
|
log.debug("coverage not enabled");
|
|
return;
|
|
}
|
|
|
|
if (Services.prefs.getBoolPref(OPT_OUT_PREF, false)) {
|
|
log.debug("user has set opt-out pref");
|
|
return;
|
|
}
|
|
|
|
if (Services.prefs.getBoolPref(ALREADY_RUN_PREF, false)) {
|
|
log.debug("already run on this profile");
|
|
return;
|
|
}
|
|
|
|
if (!Services.prefs.getCharPref(REPORTING_ENDPOINT_BASE_PREF, null)) {
|
|
log.error("no endpoint base set");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await this.reportTelemetrySetting();
|
|
} catch (e) {
|
|
log.error("unable to upload payload", e);
|
|
}
|
|
},
|
|
|
|
// NOTE - this does not use existing Telemetry code or honor Telemetry opt-out prefs,
|
|
// by design. It also sends no identifying data like the client ID. See the "coverage ping"
|
|
// documentation for details.
|
|
reportTelemetrySetting() {
|
|
const enabled = Services.prefs.getBoolPref(TELEMETRY_ENABLED_PREF, false);
|
|
|
|
const payload = {
|
|
appVersion: Services.appinfo.version,
|
|
appUpdateChannel: lazy.UpdateUtils.getUpdateChannel(false),
|
|
osName: Services.sysinfo.getProperty("name"),
|
|
osVersion: Services.sysinfo.getProperty("version"),
|
|
telemetryEnabled: enabled,
|
|
};
|
|
|
|
let cachedUuid = Services.prefs.getCharPref(COVERAGE_UUID_PREF, null);
|
|
if (!cachedUuid) {
|
|
// Totally random UUID, just for detecting duplicates.
|
|
cachedUuid = lazy.CommonUtils.generateUUID();
|
|
Services.prefs.setCharPref(COVERAGE_UUID_PREF, cachedUuid);
|
|
}
|
|
|
|
let reportingEndpointBase = Services.prefs.getCharPref(
|
|
REPORTING_ENDPOINT_BASE_PREF,
|
|
null
|
|
);
|
|
|
|
let endpoint = `${reportingEndpointBase}/${REPORTING_ENDPOINT}/${COVERAGE_VERSION}/${cachedUuid}`;
|
|
|
|
log.debug(`putting to endpoint ${endpoint} with payload:`, payload);
|
|
|
|
let deferred = Promise.withResolvers();
|
|
|
|
let request = new lazy.ServiceRequest({ mozAnon: true });
|
|
request.mozBackgroundRequest = true;
|
|
request.timeout = PING_SUBMISSION_TIMEOUT;
|
|
|
|
request.open("PUT", endpoint, true);
|
|
request.overrideMimeType("text/plain");
|
|
request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
|
|
request.setRequestHeader("Date", new Date().toUTCString());
|
|
|
|
let errorhandler = event => {
|
|
let failure = event.type;
|
|
log.error(`error making request to ${endpoint}: ${failure}`);
|
|
deferred.reject(event);
|
|
};
|
|
|
|
request.onerror = errorhandler;
|
|
request.ontimeout = errorhandler;
|
|
request.onabort = errorhandler;
|
|
|
|
request.onloadend = event => {
|
|
let status = request.status;
|
|
let statusClass = status - (status % 100);
|
|
let success = false;
|
|
|
|
if (statusClass === 200) {
|
|
// We can treat all 2XX as success.
|
|
log.info(`successfully submitted, status: ${status}`);
|
|
success = true;
|
|
} else if (statusClass === 400) {
|
|
// 4XX means that something with the request was broken.
|
|
|
|
// TODO: we should handle this better, but for now we should avoid resubmitting
|
|
// broken requests by pretending success.
|
|
success = true;
|
|
log.error(
|
|
`error submitting to ${endpoint}, status: ${status} - ping request broken?`
|
|
);
|
|
} else if (statusClass === 500) {
|
|
// 5XX means there was a server-side error and we should try again later.
|
|
log.error(
|
|
`error submitting to ${endpoint}, status: ${status} - server error, should retry later`
|
|
);
|
|
} else {
|
|
// We received an unexpected status code.
|
|
log.error(
|
|
`error submitting to ${endpoint}, status: ${status}, type: ${event.type}`
|
|
);
|
|
}
|
|
|
|
if (success) {
|
|
Services.prefs.setBoolPref(ALREADY_RUN_PREF, true);
|
|
log.debug(`result from PUT: ${request.responseText}`);
|
|
deferred.resolve();
|
|
} else {
|
|
deferred.reject(event);
|
|
}
|
|
};
|
|
|
|
request.send(JSON.stringify(payload));
|
|
|
|
return deferred.promise;
|
|
},
|
|
});
|