зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1377533 - Remove scattered references to Kinto and Sqlite in blocklist clients r=glasserc,mgoodwin
MozReview-Commit-ID: FExozSDHgNN --HG-- extra : rebase_source : 892c1c5825ac20243ce74118d4df437ec99cfe2c
This commit is contained in:
Родитель
c08d0d1a58
Коммит
b4905330e7
|
@ -49,10 +49,10 @@ const PREF_BLOCKLIST_ENFORCE_SIGNING = "services.blocklist.signing.enfor
|
|||
|
||||
const INVALID_SIGNATURE = "Invalid content/signature";
|
||||
|
||||
// FIXME: this was the default path in earlier versions of
|
||||
// This was the default path in earlier versions of
|
||||
// FirefoxAdapter, so for backwards compatibility we maintain this
|
||||
// filename, even though it isn't descriptive of who is using it.
|
||||
this.KINTO_STORAGE_PATH = "kinto.sqlite";
|
||||
const KINTO_STORAGE_PATH = "kinto.sqlite";
|
||||
|
||||
|
||||
|
||||
|
@ -114,6 +114,37 @@ class BlocklistClient {
|
|||
return `${identifier}.json`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the underlying Kinto collection, using the appropriate adapter and
|
||||
* options. This acts as a context manager where the connection is closed
|
||||
* once the specified `callback` has finished.
|
||||
*
|
||||
* @param {callback} function the async function to execute with the open SQlite connection.
|
||||
* @param {Object} options additional advanced options.
|
||||
* @param {string} options.bucket override bucket name of client (default: this.bucketName)
|
||||
* @param {string} options.collection override collection name of client (default: this.collectionName)
|
||||
* @param {string} options.path override default Sqlite path (default: kinto.sqlite)
|
||||
* @param {string} options.hooks hooks to execute on synchronization (see Kinto.js docs)
|
||||
*/
|
||||
async openCollection(callback, options = {}) {
|
||||
const { bucket = this.bucketName, path = KINTO_STORAGE_PATH } = options;
|
||||
if (!this._kinto) {
|
||||
this._kinto = new Kinto({bucket, adapter: FirefoxAdapter});
|
||||
}
|
||||
let sqliteHandle;
|
||||
try {
|
||||
sqliteHandle = await FirefoxAdapter.openConnection({path});
|
||||
const colOptions = Object.assign({adapterOptions: {sqliteHandle}}, options);
|
||||
const {collection: collectionName = this.collectionName} = options;
|
||||
const collection = this._kinto.collection(collectionName, colOptions);
|
||||
return await callback(collection);
|
||||
} finally {
|
||||
if (sqliteHandle) {
|
||||
await sqliteHandle.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the the JSON file distributed with the release for this blocklist.
|
||||
*
|
||||
|
@ -186,118 +217,106 @@ class BlocklistClient {
|
|||
const enforceCollectionSigning =
|
||||
Services.prefs.getBoolPref(PREF_BLOCKLIST_ENFORCE_SIGNING);
|
||||
|
||||
if (!this._kinto) {
|
||||
this._kinto = new Kinto({
|
||||
bucket: this.bucketName,
|
||||
adapter: FirefoxAdapter,
|
||||
});
|
||||
}
|
||||
|
||||
// if there is a signerName and collection signing is enforced, add a
|
||||
// hook for incoming changes that validates the signature
|
||||
let hooks;
|
||||
const colOptions = {};
|
||||
if (this.signerName && enforceCollectionSigning) {
|
||||
hooks = {
|
||||
colOptions.hooks = {
|
||||
"incoming-changes": [(payload, collection) => {
|
||||
return this.validateCollectionSignature(remote, payload, collection);
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
let sqliteHandle;
|
||||
let reportStatus = null;
|
||||
try {
|
||||
// Synchronize remote data into a local Sqlite DB.
|
||||
sqliteHandle = await FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
|
||||
const options = {
|
||||
hooks,
|
||||
adapterOptions: {sqliteHandle},
|
||||
};
|
||||
const collection = this._kinto.collection(this.collectionName, options);
|
||||
return await this.openCollection(async (collection) => {
|
||||
// Synchronize remote data into a local Sqlite DB.
|
||||
let collectionLastModified = await collection.db.getLastModified();
|
||||
|
||||
let collectionLastModified = await collection.db.getLastModified();
|
||||
|
||||
// If there is no data currently in the collection, attempt to import
|
||||
// initial data from the application defaults.
|
||||
// This allows to avoid synchronizing the whole collection content on
|
||||
// cold start.
|
||||
if (!collectionLastModified && loadDump) {
|
||||
try {
|
||||
const initialData = await this.loadDumpFile();
|
||||
await collection.loadDump(initialData.data);
|
||||
collectionLastModified = await collection.db.getLastModified();
|
||||
} catch (e) {
|
||||
// Report but go-on.
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// If the data is up to date, there's no need to sync. We still need
|
||||
// to record the fact that a check happened.
|
||||
if (lastModified <= collectionLastModified) {
|
||||
this.updateLastCheck(serverTime);
|
||||
reportStatus = UptakeTelemetry.STATUS.UP_TO_DATE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch changes from server.
|
||||
try {
|
||||
// Server changes have priority during synchronization.
|
||||
const strategy = Kinto.syncStrategy.SERVER_WINS;
|
||||
const {ok} = await collection.sync({remote, strategy});
|
||||
if (!ok) {
|
||||
// Some synchronization conflicts occured.
|
||||
reportStatus = UptakeTelemetry.STATUS.CONFLICT_ERROR;
|
||||
throw new Error("Sync failed");
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message == INVALID_SIGNATURE) {
|
||||
// Signature verification failed during synchronzation.
|
||||
reportStatus = UptakeTelemetry.STATUS.SIGNATURE_ERROR;
|
||||
// if sync fails with a signature error, it's likely that our
|
||||
// local data has been modified in some way.
|
||||
// We will attempt to fix this by retrieving the whole
|
||||
// remote collection.
|
||||
const payload = await fetchRemoteCollection(remote, collection);
|
||||
// If there is no data currently in the collection, attempt to import
|
||||
// initial data from the application defaults.
|
||||
// This allows to avoid synchronizing the whole collection content on
|
||||
// cold start.
|
||||
if (!collectionLastModified && loadDump) {
|
||||
try {
|
||||
await this.validateCollectionSignature(remote, payload, collection, {ignoreLocal: true});
|
||||
const initialData = await this.loadDumpFile();
|
||||
await collection.loadDump(initialData.data);
|
||||
collectionLastModified = await collection.db.getLastModified();
|
||||
} catch (e) {
|
||||
reportStatus = UptakeTelemetry.STATUS.SIGNATURE_RETRY_ERROR;
|
||||
// Report but go-on.
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// If the data is up to date, there's no need to sync. We still need
|
||||
// to record the fact that a check happened.
|
||||
if (lastModified <= collectionLastModified) {
|
||||
this.updateLastCheck(serverTime);
|
||||
reportStatus = UptakeTelemetry.STATUS.UP_TO_DATE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch changes from server.
|
||||
try {
|
||||
// Server changes have priority during synchronization.
|
||||
const strategy = Kinto.syncStrategy.SERVER_WINS;
|
||||
const {ok} = await collection.sync({remote, strategy});
|
||||
if (!ok) {
|
||||
// Some synchronization conflicts occured.
|
||||
reportStatus = UptakeTelemetry.STATUS.CONFLICT_ERROR;
|
||||
throw new Error("Sync failed");
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message == INVALID_SIGNATURE) {
|
||||
// Signature verification failed during synchronzation.
|
||||
reportStatus = UptakeTelemetry.STATUS.SIGNATURE_ERROR;
|
||||
// if sync fails with a signature error, it's likely that our
|
||||
// local data has been modified in some way.
|
||||
// We will attempt to fix this by retrieving the whole
|
||||
// remote collection.
|
||||
const payload = await fetchRemoteCollection(remote, collection);
|
||||
try {
|
||||
await this.validateCollectionSignature(remote, payload, collection, {ignoreLocal: true});
|
||||
} catch (e) {
|
||||
reportStatus = UptakeTelemetry.STATUS.SIGNATURE_RETRY_ERROR;
|
||||
throw e;
|
||||
}
|
||||
// if the signature is good (we haven't thrown), and the remote
|
||||
// last_modified is newer than the local last_modified, replace the
|
||||
// local data
|
||||
const localLastModified = await collection.db.getLastModified();
|
||||
if (payload.last_modified >= localLastModified) {
|
||||
await collection.clear();
|
||||
await collection.loadDump(payload.data);
|
||||
}
|
||||
} else {
|
||||
// The sync has thrown, it can be a network or a general error.
|
||||
if (/NetworkError/.test(e.message)) {
|
||||
reportStatus = UptakeTelemetry.STATUS.NETWORK_ERROR;
|
||||
} else if (/Backoff/.test(e.message)) {
|
||||
reportStatus = UptakeTelemetry.STATUS.BACKOFF;
|
||||
} else {
|
||||
reportStatus = UptakeTelemetry.STATUS.SYNC_ERROR;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
// if the signature is good (we haven't thrown), and the remote
|
||||
// last_modified is newer than the local last_modified, replace the
|
||||
// local data
|
||||
const localLastModified = await collection.db.getLastModified();
|
||||
if (payload.last_modified >= localLastModified) {
|
||||
await collection.clear();
|
||||
await collection.loadDump(payload.data);
|
||||
}
|
||||
} else {
|
||||
// The sync has thrown, it can be a network or a general error.
|
||||
if (/NetworkError/.test(e.message)) {
|
||||
reportStatus = UptakeTelemetry.STATUS.NETWORK_ERROR;
|
||||
} else if (/Backoff/.test(e.message)) {
|
||||
reportStatus = UptakeTelemetry.STATUS.BACKOFF;
|
||||
} else {
|
||||
reportStatus = UptakeTelemetry.STATUS.SYNC_ERROR;
|
||||
}
|
||||
}
|
||||
// Read local collection of records.
|
||||
const {data} = await collection.list();
|
||||
|
||||
// Handle the obtained records (ie. apply locally).
|
||||
try {
|
||||
await this.processCallback(data);
|
||||
} catch (e) {
|
||||
reportStatus = UptakeTelemetry.STATUS.APPLY_ERROR;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
// Read local collection of records.
|
||||
const {data} = await collection.list();
|
||||
|
||||
// Handle the obtained records (ie. apply locally).
|
||||
try {
|
||||
await this.processCallback(data);
|
||||
} catch (e) {
|
||||
reportStatus = UptakeTelemetry.STATUS.APPLY_ERROR;
|
||||
throw e;
|
||||
}
|
||||
// Track last update.
|
||||
this.updateLastCheck(serverTime);
|
||||
|
||||
// Track last update.
|
||||
this.updateLastCheck(serverTime);
|
||||
}, colOptions);
|
||||
} catch (e) {
|
||||
// No specific error was tracked, mark it as unknown.
|
||||
if (reportStatus === null) {
|
||||
|
@ -305,9 +324,6 @@ class BlocklistClient {
|
|||
}
|
||||
throw e;
|
||||
} finally {
|
||||
if (sqliteHandle) {
|
||||
await sqliteHandle.close();
|
||||
}
|
||||
// No error was reported, this is a success!
|
||||
if (reportStatus === null) {
|
||||
reportStatus = UptakeTelemetry.STATUS.SUCCESS;
|
||||
|
|
|
@ -3,31 +3,12 @@ const { Constructor: CC } = Components;
|
|||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const { OneCRLBlocklistClient } = Cu.import("resource://services-common/blocklist-clients.js", {});
|
||||
const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
|
||||
const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
|
||||
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream", "setInputStream");
|
||||
|
||||
let server;
|
||||
|
||||
// set up what we need to make storage adapters
|
||||
let sqliteHandle;
|
||||
const KINTO_FILENAME = "kinto.sqlite";
|
||||
|
||||
function do_get_kinto_collection(collectionName) {
|
||||
let config = {
|
||||
// Set the remote to be some server that will cause test failure when
|
||||
// hit since we should never hit the server directly, only via maybeSync()
|
||||
remote: "https://firefox.settings.services.mozilla.com/v1/",
|
||||
// Set up the adapter and bucket as normal
|
||||
adapter: FirefoxAdapter,
|
||||
adapterOptions: {sqliteHandle},
|
||||
bucket: "blocklists"
|
||||
};
|
||||
return new Kinto(config).collection(collectionName);
|
||||
}
|
||||
|
||||
// Some simple tests to demonstrate that the logic inside maybeSync works
|
||||
// correctly and that simple kinto operations are working as expected. There
|
||||
// are more tests for core Kinto.js (and its storage adapter) in the
|
||||
|
@ -67,14 +48,13 @@ add_task(async function test_something() {
|
|||
// Test an empty db populates
|
||||
await OneCRLBlocklistClient.maybeSync(2000, Date.now());
|
||||
|
||||
sqliteHandle = await FirefoxAdapter.openConnection({path: KINTO_FILENAME});
|
||||
const collection = do_get_kinto_collection("certificates");
|
||||
|
||||
// Open the collection, verify it's been populated:
|
||||
let list = await collection.list();
|
||||
// We know there will be initial values from the JSON dump.
|
||||
// (at least as many as in the dump shipped when this test was written).
|
||||
do_check_true(list.data.length >= 363);
|
||||
await OneCRLBlocklistClient.openCollection(async (collection) => {
|
||||
// Open the collection, verify it's been populated:
|
||||
const list = await collection.list();
|
||||
// We know there will be initial values from the JSON dump.
|
||||
// (at least as many as in the dump shipped when this test was written).
|
||||
do_check_true(list.data.length >= 363);
|
||||
});
|
||||
|
||||
// No sync will be intented if maybeSync() is up-to-date.
|
||||
Services.prefs.clearUserPref("services.settings.server");
|
||||
|
@ -86,26 +66,34 @@ add_task(async function test_something() {
|
|||
|
||||
// Restore server pref.
|
||||
Services.prefs.setCharPref("services.settings.server", dummyServerURL);
|
||||
// clear the collection, save a non-zero lastModified so we don't do
|
||||
// import of initial data when we sync again.
|
||||
await collection.clear();
|
||||
// a lastModified value of 1000 means we get a remote collection with a
|
||||
// single record
|
||||
await collection.db.saveLastModified(1000);
|
||||
|
||||
await OneCRLBlocklistClient.openCollection(async (collection) => {
|
||||
// clear the collection, save a non-zero lastModified so we don't do
|
||||
// import of initial data when we sync again.
|
||||
await collection.clear();
|
||||
// a lastModified value of 1000 means we get a remote collection with a
|
||||
// single record
|
||||
await collection.db.saveLastModified(1000);
|
||||
});
|
||||
|
||||
await OneCRLBlocklistClient.maybeSync(2000, Date.now());
|
||||
|
||||
// Open the collection, verify it's been updated:
|
||||
// Our test data now has two records; both should be in the local collection
|
||||
list = await collection.list();
|
||||
do_check_eq(list.data.length, 1);
|
||||
await OneCRLBlocklistClient.openCollection(async (collection) => {
|
||||
// Open the collection, verify it's been updated:
|
||||
// Our test data now has two records; both should be in the local collection
|
||||
const list = await collection.list();
|
||||
do_check_eq(list.data.length, 1);
|
||||
});
|
||||
|
||||
// Test the db is updated when we call again with a later lastModified value
|
||||
await OneCRLBlocklistClient.maybeSync(4000, Date.now());
|
||||
|
||||
// Open the collection, verify it's been updated:
|
||||
// Our test data now has two records; both should be in the local collection
|
||||
list = await collection.list();
|
||||
do_check_eq(list.data.length, 3);
|
||||
await OneCRLBlocklistClient.openCollection(async (collection) => {
|
||||
// Open the collection, verify it's been updated:
|
||||
// Our test data now has two records; both should be in the local collection
|
||||
const list = await collection.list();
|
||||
do_check_eq(list.data.length, 3);
|
||||
});
|
||||
|
||||
// Try to maybeSync with the current lastModified value - no connection
|
||||
// should be attempted.
|
||||
|
@ -143,7 +131,6 @@ function run_test() {
|
|||
|
||||
do_register_cleanup(function() {
|
||||
server.stop(() => { });
|
||||
return sqliteHandle.close();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,11 @@ Cu.import("resource://gre/modules/Timer.jsm");
|
|||
const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
|
||||
const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
|
||||
|
||||
const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
|
||||
const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
|
||||
const BlocklistClients = Cu.import("resource://services-common/blocklist-clients.js", {});
|
||||
const { UptakeTelemetry } = Cu.import("resource://services-common/uptake-telemetry.js", {});
|
||||
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream", "setInputStream");
|
||||
const kintoFilename = "kinto.sqlite";
|
||||
|
||||
const gBlocklistClients = [
|
||||
{client: BlocklistClients.AddonBlocklistClient, testData: ["i808", "i720", "i539"]},
|
||||
|
@ -24,18 +21,6 @@ const gBlocklistClients = [
|
|||
|
||||
let server;
|
||||
|
||||
function kintoCollection(collectionName, sqliteHandle) {
|
||||
const config = {
|
||||
// Set the remote to be some server that will cause test failure when
|
||||
// hit since we should never hit the server directly, only via maybeSync()
|
||||
remote: "https://firefox.settings.services.mozilla.com/v1/",
|
||||
adapter: FirefoxAdapter,
|
||||
adapterOptions: {sqliteHandle},
|
||||
bucket: "blocklists"
|
||||
};
|
||||
return new Kinto(config).collection(collectionName);
|
||||
}
|
||||
|
||||
async function readJSON(filepath) {
|
||||
const binaryData = await OS.File.read(filepath);
|
||||
const textData = (new TextDecoder()).decode(binaryData);
|
||||
|
@ -48,14 +33,9 @@ async function clear_state() {
|
|||
Services.prefs.clearUserPref(client.lastCheckTimePref);
|
||||
|
||||
// Clear local DB.
|
||||
let sqliteHandle;
|
||||
try {
|
||||
sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
|
||||
const collection = kintoCollection(client.collectionName, sqliteHandle);
|
||||
await client.openCollection(async (collection) => {
|
||||
await collection.clear();
|
||||
} finally {
|
||||
await sqliteHandle.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Remove JSON dumps folders in profile dir.
|
||||
const dumpFile = OS.Path.join(OS.Constants.Path.profileDir, client.filename);
|
||||
|
@ -120,11 +100,10 @@ add_task(async function test_initial_dump_is_loaded_as_synced_when_collection_is
|
|||
await client.maybeSync(1, Date.now());
|
||||
|
||||
// Open the collection, verify the loaded data has status to synced:
|
||||
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
|
||||
const collection = kintoCollection(client.collectionName, sqliteHandle);
|
||||
const list = await collection.list();
|
||||
equal(list.data[0]._status, "synced");
|
||||
await sqliteHandle.close();
|
||||
await client.openCollection(async (collection) => {
|
||||
const list = await collection.list();
|
||||
equal(list.data[0]._status, "synced");
|
||||
});
|
||||
}
|
||||
});
|
||||
add_task(clear_state);
|
||||
|
@ -136,11 +115,10 @@ add_task(async function test_records_obtained_from_server_are_stored_in_db() {
|
|||
|
||||
// Open the collection, verify it's been populated:
|
||||
// Our test data has a single record; it should be in the local collection
|
||||
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
|
||||
let collection = kintoCollection(client.collectionName, sqliteHandle);
|
||||
let list = await collection.list();
|
||||
equal(list.data.length, 1);
|
||||
await sqliteHandle.close();
|
||||
await client.openCollection(async (collection) => {
|
||||
const list = await collection.list();
|
||||
equal(list.data.length, 1);
|
||||
});
|
||||
}
|
||||
});
|
||||
add_task(clear_state);
|
||||
|
@ -149,14 +127,12 @@ add_task(async function test_records_changes_are_overwritten_by_server_changes()
|
|||
const {client} = gBlocklistClients[0];
|
||||
|
||||
// Create some local conflicting data, and make sure it syncs without error.
|
||||
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
|
||||
const collection = kintoCollection(client.collectionName, sqliteHandle);
|
||||
await collection.create({
|
||||
"versionRange": [],
|
||||
"id": "9d500963-d80e-3a91-6e74-66f3811b99cc"
|
||||
}, { useRecordId: true });
|
||||
await sqliteHandle.close();
|
||||
|
||||
await client.openCollection(async (collection) => {
|
||||
await collection.create({
|
||||
"versionRange": [],
|
||||
"id": "9d500963-d80e-3a91-6e74-66f3811b99cc"
|
||||
}, { useRecordId: true });
|
||||
});
|
||||
await client.maybeSync(2000, Date.now(), {loadDump: false});
|
||||
});
|
||||
add_task(clear_state);
|
||||
|
@ -284,10 +260,9 @@ add_task(async function test_telemetry_reports_if_sync_fails() {
|
|||
const {client} = gBlocklistClients[0];
|
||||
const serverTime = Date.now();
|
||||
|
||||
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
|
||||
const collection = kintoCollection(client.collectionName, sqliteHandle);
|
||||
await collection.db.saveLastModified(9999);
|
||||
await sqliteHandle.close();
|
||||
await client.openCollection(async (collection) => {
|
||||
await collection.db.saveLastModified(9999);
|
||||
});
|
||||
|
||||
const startHistogram = getUptakeTelemetrySnapshot(client.identifier);
|
||||
|
||||
|
@ -304,15 +279,15 @@ add_task(clear_state);
|
|||
add_task(async function test_telemetry_reports_unknown_errors() {
|
||||
const {client} = gBlocklistClients[0];
|
||||
const serverTime = Date.now();
|
||||
const backup = FirefoxAdapter.openConnection;
|
||||
FirefoxAdapter.openConnection = () => { throw new Error("Internal"); };
|
||||
const backup = client.openCollection;
|
||||
client.openCollection = () => { throw new Error("Internal"); };
|
||||
const startHistogram = getUptakeTelemetrySnapshot(client.identifier);
|
||||
|
||||
try {
|
||||
await client.maybeSync(2000, serverTime);
|
||||
} catch (e) {}
|
||||
|
||||
FirefoxAdapter.openConnection = backup;
|
||||
client.openCollection = backup;
|
||||
const endHistogram = getUptakeTelemetrySnapshot(client.identifier);
|
||||
const expectedIncrements = {[UptakeTelemetry.STATUS.UNKNOWN_ERROR]: 1};
|
||||
checkUptakeTelemetry(startHistogram, endHistogram, expectedIncrements);
|
||||
|
|
|
@ -4,16 +4,9 @@ const { Constructor: CC } = Components;
|
|||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
|
||||
const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
|
||||
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream", "setInputStream");
|
||||
|
||||
const PREF_BLOCKLIST_PINNING_COLLECTION = "services.blocklist.pinning.collection";
|
||||
const COLLECTION_NAME = "pins";
|
||||
const KINTO_STORAGE_PATH = "kinto.sqlite";
|
||||
|
||||
// First, we need to setup appInfo or we can't do version checks
|
||||
var id = "xpcshell@tests.mozilla.org";
|
||||
var appName = "XPCShell";
|
||||
|
@ -31,30 +24,9 @@ updateAppInfo({
|
|||
|
||||
let server;
|
||||
|
||||
|
||||
function do_get_kinto_collection(connection, collectionName) {
|
||||
let config = {
|
||||
// Set the remote to be some server that will cause test failure when
|
||||
// hit since we should never hit the server directly (any non-local
|
||||
// request causes failure in tests), only via maybeSync()
|
||||
remote: "https://firefox.settings.services.mozilla.com/v1/",
|
||||
// Set up the adapter and bucket as normal
|
||||
adapter: FirefoxAdapter,
|
||||
adapterOptions: {sqliteHandle: connection},
|
||||
bucket: "pinning"
|
||||
};
|
||||
let kintoClient = new Kinto(config);
|
||||
return kintoClient.collection(collectionName);
|
||||
}
|
||||
|
||||
// Some simple tests to demonstrate that the core preload sync operations work
|
||||
// correctly and that simple kinto operations are working as expected.
|
||||
add_task(async function test_something() {
|
||||
// set the collection name explicitly - since there will be version
|
||||
// specific collection names in prefs
|
||||
Services.prefs.setCharPref(PREF_BLOCKLIST_PINNING_COLLECTION,
|
||||
COLLECTION_NAME);
|
||||
|
||||
const { PinningPreloadClient } = Cu.import("resource://services-common/blocklist-clients.js", {});
|
||||
|
||||
const configPath = "/v1/";
|
||||
|
@ -106,13 +78,12 @@ add_task(async function test_something() {
|
|||
// Test an empty db populates
|
||||
await PinningPreloadClient.maybeSync(2000, Date.now());
|
||||
|
||||
let connection = await FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
|
||||
|
||||
// Open the collection, verify it's been populated:
|
||||
// Our test data has a single record; it should be in the local collection
|
||||
let collection = do_get_kinto_collection(connection, COLLECTION_NAME);
|
||||
let list = await collection.list();
|
||||
do_check_eq(list.data.length, 1);
|
||||
await PinningPreloadClient.openCollection(async (collection) => {
|
||||
const list = await collection.list();
|
||||
do_check_eq(list.data.length, 1);
|
||||
});
|
||||
|
||||
// check that a pin exists for one.example.com
|
||||
ok(sss.isSecureURI(sss.HEADER_HPKP,
|
||||
|
@ -123,10 +94,10 @@ add_task(async function test_something() {
|
|||
|
||||
// Open the collection, verify it's been updated:
|
||||
// Our data now has four new records; all should be in the local collection
|
||||
collection = do_get_kinto_collection(connection, COLLECTION_NAME);
|
||||
list = await collection.list();
|
||||
do_check_eq(list.data.length, 5);
|
||||
await connection.close();
|
||||
await PinningPreloadClient.openCollection(async (collection) => {
|
||||
const list = await collection.list();
|
||||
do_check_eq(list.data.length, 5);
|
||||
});
|
||||
|
||||
// check that a pin exists for two.example.com and three.example.com
|
||||
ok(sss.isSecureURI(sss.HEADER_HPKP,
|
||||
|
|
|
@ -3,25 +3,19 @@
|
|||
Cu.import("resource://services-common/blocklist-updater.js");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
|
||||
const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
|
||||
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
|
||||
const { OneCRLBlocklistClient } = Cu.import("resource://services-common/blocklist-clients.js", {});
|
||||
const { UptakeTelemetry } = Cu.import("resource://services-common/uptake-telemetry.js", {});
|
||||
|
||||
let server;
|
||||
|
||||
const PREF_BLOCKLIST_BUCKET = "services.blocklist.bucket";
|
||||
const PREF_BLOCKLIST_ENFORCE_SIGNING = "services.blocklist.signing.enforced";
|
||||
const PREF_BLOCKLIST_ONECRL_COLLECTION = "services.blocklist.onecrl.collection";
|
||||
const PREF_SETTINGS_SERVER = "services.settings.server";
|
||||
const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash";
|
||||
|
||||
// Telemetry reports.
|
||||
const TELEMETRY_HISTOGRAM_KEY = OneCRLBlocklistClient.identifier;
|
||||
|
||||
const kintoFilename = "kinto.sqlite";
|
||||
|
||||
const CERT_DIR = "test_blocklist_signatures/";
|
||||
const CHAIN_FILES =
|
||||
["collection_signing_ee.pem",
|
||||
|
@ -60,29 +54,11 @@ function getCertChain() {
|
|||
}
|
||||
|
||||
async function checkRecordCount(count) {
|
||||
// open the collection manually
|
||||
const base = Services.prefs.getCharPref(PREF_SETTINGS_SERVER);
|
||||
const bucket = Services.prefs.getCharPref(PREF_BLOCKLIST_BUCKET);
|
||||
const collectionName =
|
||||
Services.prefs.getCharPref(PREF_BLOCKLIST_ONECRL_COLLECTION);
|
||||
|
||||
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
|
||||
const config = {
|
||||
remote: base,
|
||||
bucket,
|
||||
adapter: FirefoxAdapter,
|
||||
adapterOptions: {sqliteHandle},
|
||||
};
|
||||
|
||||
const db = new Kinto(config);
|
||||
const collection = db.collection(collectionName);
|
||||
|
||||
// Check we have the expected number of records
|
||||
let records = await collection.list();
|
||||
do_check_eq(count, records.data.length);
|
||||
|
||||
// Close the collection so the test can exit cleanly
|
||||
await sqliteHandle.close();
|
||||
await OneCRLBlocklistClient.openCollection(async (collection) => {
|
||||
// Check we have the expected number of records
|
||||
const records = await collection.list();
|
||||
do_check_eq(count, records.data.length);
|
||||
});
|
||||
}
|
||||
|
||||
// Check to ensure maybeSync is called with correct values when a changes
|
||||
|
|
|
@ -57,7 +57,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
Kinto: "resource://services-common/kinto-offline-client.js",
|
||||
FirefoxAdapter: "resource://services-common/kinto-storage-adapter.js",
|
||||
Observers: "resource://services-common/observers.js",
|
||||
Sqlite: "resource://gre/modules/Sqlite.jsm",
|
||||
Utils: "resource://services-sync/util.js",
|
||||
});
|
||||
|
||||
|
@ -334,30 +333,25 @@ global.KeyRingEncryptionRemoteTransformer = KeyRingEncryptionRemoteTransformer;
|
|||
* - connection: a Sqlite connection. Meant for internal use only.
|
||||
* - kinto: a KintoBase object, suitable for using in Firefox. All
|
||||
* collections in this database will use the same Sqlite connection.
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
const storageSyncInit = (async function() {
|
||||
const path = "storage-sync.sqlite";
|
||||
const opts = {path, sharedMemoryCache: false};
|
||||
const connection = await Sqlite.openConnection(opts);
|
||||
await FirefoxAdapter._init(connection);
|
||||
return {
|
||||
connection,
|
||||
kinto: new Kinto({
|
||||
adapter: FirefoxAdapter,
|
||||
adapterOptions: {sqliteHandle: connection},
|
||||
timeout: KINTO_REQUEST_TIMEOUT,
|
||||
}),
|
||||
};
|
||||
})();
|
||||
|
||||
AsyncShutdown.profileBeforeChange.addBlocker(
|
||||
"ExtensionStorageSync: close Sqlite handle",
|
||||
async function() {
|
||||
const ret = await storageSyncInit;
|
||||
const {connection} = ret;
|
||||
await connection.close();
|
||||
async function storageSyncInit() {
|
||||
// Memoize the result to share the connection.
|
||||
if (storageSyncInit.result === undefined) {
|
||||
const path = "storage-sync.sqlite";
|
||||
const connection = await FirefoxAdapter.openConnection({path});
|
||||
storageSyncInit.result = {
|
||||
connection,
|
||||
kinto: new Kinto({
|
||||
adapter: FirefoxAdapter,
|
||||
adapterOptions: {sqliteHandle: connection},
|
||||
timeout: KINTO_REQUEST_TIMEOUT,
|
||||
}),
|
||||
};
|
||||
}
|
||||
);
|
||||
return storageSyncInit.result;
|
||||
}
|
||||
|
||||
// Kinto record IDs have two condtions:
|
||||
//
|
||||
// - They must contain only ASCII alphanumerics plus - and _. To fix
|
||||
|
@ -432,7 +426,7 @@ class CryptoCollection {
|
|||
|
||||
async getCollection() {
|
||||
throwIfNoFxA(this._fxaService, "tried to access cryptoCollection");
|
||||
const {kinto} = await storageSyncInit;
|
||||
const {kinto} = await storageSyncInit();
|
||||
return kinto.collection(STORAGE_SYNC_CRYPTO_COLLECTION_NAME, {
|
||||
idSchema: cryptoCollectionIdSchema,
|
||||
remoteTransformers: [new KeyRingEncryptionRemoteTransformer(this._fxaService)],
|
||||
|
@ -695,7 +689,7 @@ function cleanUpForContext(extension, context) {
|
|||
*/
|
||||
const openCollection = async function(cryptoCollection, extension, context) {
|
||||
let collectionId = extension.id;
|
||||
const {kinto} = await storageSyncInit;
|
||||
const {kinto} = await storageSyncInit();
|
||||
const remoteTransformers = [new CollectionKeyEncryptionRemoteTransformer(cryptoCollection, extension.id)];
|
||||
const coll = kinto.collection(collectionId, {
|
||||
idSchema: storageSyncIdSchema,
|
||||
|
@ -1230,7 +1224,17 @@ class ExtensionStorageSync {
|
|||
this.listeners.set(extension, listeners);
|
||||
|
||||
// Force opening the collection so that we will sync for this extension.
|
||||
return this.getCollection(extension, context);
|
||||
// This happens asynchronously, even though the surface API is synchronous.
|
||||
return this.getCollection(extension, context)
|
||||
.catch((e) => {
|
||||
// We can ignore failures that happen during shutdown here. First, we
|
||||
// can't report in any way. And second, a failure to open the collection
|
||||
// does not matter, because there won't be any message to listen to.
|
||||
// See Bug 1395215.
|
||||
if (!(/Kinto storage adapter connection closing/.test(e.message))) {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeOnChangedListener(extension, listener) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче