Bug 1331629 - Reuse the same kinto client instance accross syncs (r=mgoodwin)
authorMathieu Leplatre <mathieu@mozilla.com>
Mon, 06 Feb 2017 10:35:26 +0100
changeset 479603 dd195a6fabaf558c0089488af184265041ffc34b
parent 479602 ed583f4e5054748738c92e466fd8d2650c8d0888
child 479604 f0f5fdf96c80ed8ef55c7c8152976cd8c41a072a
push id44315
push usertnguyen@mozilla.com
push dateTue, 07 Feb 2017 02:03:39 +0000
reviewersmgoodwin
bugs1331629
milestone54.0a1
Bug 1331629 - Reuse the same kinto client instance accross syncs (r=mgoodwin) MozReview-Commit-ID: F8Fhy9TzPTG
services/common/blocklist-clients.js
--- a/services/common/blocklist-clients.js
+++ b/services/common/blocklist-clients.js
@@ -63,64 +63,53 @@ function mergeChanges(collection, localR
   return Object.values(records)
     // Filter out deleted records.
     .filter((record) => record.deleted != true)
     // Sort list by record id.
     .sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
 }
 
 
-function fetchCollectionMetadata(collection) {
-  const client = new KintoHttpClient(collection.api.remote);
+function fetchCollectionMetadata(remote, collection) {
+  const client = new KintoHttpClient(remote);
   return client.bucket(collection.bucket).collection(collection.name).getData()
     .then(result => {
       return result.signature;
     });
 }
 
-function fetchRemoteCollection(collection) {
-  const client = new KintoHttpClient(collection.api.remote);
+function fetchRemoteCollection(remote, collection) {
+  const client = new KintoHttpClient(remote);
   return client.bucket(collection.bucket)
            .collection(collection.name)
            .listRecords({sort: "id"});
 }
 
-/**
- * Helper to instantiate a Kinto client based on preferences for remote server
- * URL and bucket name. It uses the `FirefoxAdapter` which relies on SQLite to
- * persist the local DB.
- */
-function kintoClient(connection, bucket) {
-  const remote = Services.prefs.getCharPref(PREF_SETTINGS_SERVER);
-
-  const config = {
-    remote,
-    bucket,
-    adapter: FirefoxAdapter,
-    adapterOptions: {sqliteHandle: connection},
-  };
-
-  return new Kinto(config);
-}
-
 
 class BlocklistClient {
 
   constructor(collectionName, lastCheckTimePref, processCallback, bucketName, signerName) {
     this.collectionName = collectionName;
     this.lastCheckTimePref = lastCheckTimePref;
     this.processCallback = processCallback;
     this.bucketName = bucketName;
     this.signerName = signerName;
+
+    this._kinto = new Kinto({
+      bucket: bucketName,
+      adapter: FirefoxAdapter,
+    });
   }
 
-  validateCollectionSignature(payload, collection, ignoreLocal) {
+  validateCollectionSignature(remote, payload, collection, options = {}) {
+    const {ignoreLocal} = options;
+
     return Task.spawn((function* () {
       // this is a content-signature field from an autograph response.
-      const {x5u, signature} = yield fetchCollectionMetadata(collection);
+      const {x5u, signature} = yield fetchCollectionMetadata(remote, collection);
       const certChain = yield fetch(x5u).then((res) => res.text());
 
       const verifier = Cc["@mozilla.org/security/contentsignatureverifier;1"]
                          .createInstance(Ci.nsIContentSignatureVerifier);
 
       let toSerialize;
       if (ignoreLocal) {
         toSerialize = {
@@ -152,57 +141,63 @@ class BlocklistClient {
    * Synchronize from Kinto server, if necessary.
    *
    * @param {int}  lastModified the lastModified date (on the server) for
                                 the remote collection.
    * @param {Date} serverTime   the current date return by the server.
    * @return {Promise}          which rejects on sync or process failure.
    */
   maybeSync(lastModified, serverTime) {
-    const opts = {};
-    const enforceCollectionSigning =
+    const remote = Services.prefs.getCharPref(PREF_SETTINGS_SERVER);
+    let enforceCollectionSigning =
       Services.prefs.getBoolPref(PREF_BLOCKLIST_ENFORCE_SIGNING);
 
     // if there is a signerName and collection signing is enforced, add a
     // hook for incoming changes that validates the signature
+    let hooks;
     if (this.signerName && enforceCollectionSigning) {
-      opts.hooks = {
-        "incoming-changes": [this.validateCollectionSignature.bind(this)]
+      hooks = {
+        "incoming-changes": [(payload, collection) => {
+          return this.validateCollectionSignature(remote, payload, collection);
+        }]
       }
     }
 
-
     return Task.spawn((function* syncCollection() {
-      let connection;
+      let sqliteHandle;
       try {
-        connection = yield FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
-        const db = kintoClient(connection, this.bucketName);
-        const collection = db.collection(this.collectionName, opts);
+        // Synchronize remote data into a local Sqlite DB.
+        sqliteHandle = yield FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
+        const options = {
+          hooks,
+          adapterOptions: {sqliteHandle},
+        };
+        const collection = this._kinto.collection(this.collectionName, options);
 
         const collectionLastModified = yield collection.db.getLastModified();
         // 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);
           return;
         }
         // Fetch changes from server.
         try {
-          const {ok} = yield collection.sync();
+          const {ok} = yield collection.sync({remote});
           if (!ok) {
             throw new Error("Sync failed");
           }
         } catch (e) {
           if (e.message == INVALID_SIGNATURE) {
             // 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 = yield fetchRemoteCollection(collection);
-            yield this.validateCollectionSignature(payload, collection, true);
+            const payload = yield fetchRemoteCollection(remote, collection);
+            yield this.validateCollectionSignature(remote, payload, collection, {ignoreLocal: true});
             // 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 = yield collection.db.getLastModified();
             if (payload.last_modified >= localLastModified) {
               yield collection.clear();
               yield collection.loadDump(payload.data);
             }
@@ -213,17 +208,17 @@ class BlocklistClient {
         // Read local collection of records.
         const {data} = yield collection.list();
 
         yield this.processCallback(data);
 
         // Track last update.
         this.updateLastCheck(serverTime);
       } finally {
-        yield connection.close();
+        yield sqliteHandle.close();
       }
     }).bind(this));
   }
 
   /**
    * Save last time server was checked in users prefs.
    *
    * @param {Date} serverTime   the current date return by server.