Bug 1539455 - Replace string comparison by Remote Settings errors classes r=glasserc
authorMathieu Leplatre <mathieu@mozilla.com>
Thu, 11 Apr 2019 15:08:40 +0000
changeset 469014 c18ecd346791c201683180481173ea1af23c4594
parent 469013 4ec02d2be99e933a9599075f4e45c25687744111
child 469015 70ed6fa7f92135943a370c39dffb5c0895350b01
push id35856
push usercsabou@mozilla.com
push dateFri, 12 Apr 2019 03:19:48 +0000
treeherdermozilla-central@940684cd1065 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglasserc
bugs1539455
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1539455 - Replace string comparison by Remote Settings errors classes r=glasserc Replace string comparison by Remote Settings errors classes Differential Revision: https://phabricator.services.mozilla.com/D27072
services/settings/RemoteSettingsClient.jsm
--- a/services/settings/RemoteSettingsClient.jsm
+++ b/services/settings/RemoteSettingsClient.jsm
@@ -26,19 +26,16 @@ ChromeUtils.defineModuleGetter(this, "Ut
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 // IndexedDB name.
 const DB_NAME = "remote-settings";
 
 const TELEMETRY_COMPONENT = "remotesettings";
 
-const INVALID_SIGNATURE_MSG = "Invalid content signature";
-const MISSING_SIGNATURE_MSG = "Missing signature";
-
 XPCOMUtils.defineLazyPreferenceGetter(this, "gServerURL",
                                       "services.settings.server");
 XPCOMUtils.defineLazyPreferenceGetter(this, "gChangesPath",
                                       "services.settings.changes.path");
 XPCOMUtils.defineLazyPreferenceGetter(this, "gVerifySignature",
                                       "services.settings.verify_signature", true);
 
 /**
@@ -80,17 +77,17 @@ class ClientEnvironment extends ClientEn
  * @returns {Promise<{String, String}>}
  */
 async function fetchCollectionSignature(bucket, collection, expectedTimestamp) {
   const client = new KintoHttpClient(gServerURL);
   const { signature: signaturePayload } = await client.bucket(bucket)
     .collection(collection)
     .getData({ query: { _expected: expectedTimestamp } });
   if (!signaturePayload) {
-    throw new Error(MISSING_SIGNATURE_MSG);
+    throw new RemoteSettingsClient.MissingSignatureError(`${bucket}/${collection}`);
   }
   const { x5u, signature } = signaturePayload;
   const certChainResponse = await fetch(x5u);
   const certChain = await certChainResponse.text();
 
   return { signature, certChain };
 }
 
@@ -160,18 +157,34 @@ class EventEmitter {
     if (i < 0) {
       throw new Error(`Unknown callback`);
     } else {
       callbacks.splice(i, 1);
     }
   }
 }
 
+class InvalidSignatureError extends Error {
+  constructor(cid) {
+    super(`Invalid content signature (${cid})`);
+    this.name = "InvalidSignatureError";
+  }
+}
+
+class MissingSignatureError extends Error {
+  constructor(cid) {
+    super(`Missing signature (${cid})`);
+    this.name = "MissingSignatureError";
+  }
+}
 
 class RemoteSettingsClient extends EventEmitter {
+  static get InvalidSignatureError() { return InvalidSignatureError; }
+  static get MissingSignatureError() { return MissingSignatureError; }
+
   constructor(collectionName, { bucketNamePref, signerName, filterFunc, localFields = [], lastCheckTimePref }) {
     super(["sync"]); // emitted events
 
     this.collectionName = collectionName;
     this.signerName = signerName;
     this.filterFunc = filterFunc;
     this.localFields = localFields;
     this._lastCheckTimePref = lastCheckTimePref;
@@ -331,34 +344,34 @@ class RemoteSettingsClient extends Event
         // Fetch changes from server, and make sure we overwrite local data.
         const strategy = Kinto.syncStrategy.SERVER_WINS;
         syncResult = await kintoCollection.sync({ remote: gServerURL, strategy, expectedTimestamp });
         if (!syncResult.ok) {
           // With SERVER_WINS, there cannot be any conflicts, but don't silent it anyway.
           throw new Error("Synced failed");
         }
       } catch (e) {
-        if (e.message.includes(INVALID_SIGNATURE_MSG)) {
+        if (e instanceof RemoteSettingsClient.InvalidSignatureError) {
           // Signature verification failed during synchronization.
           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.
           try {
             syncResult = await this._retrySyncFromScratch(kintoCollection, expectedTimestamp);
           } catch (e) {
             // If the signature fails again, or if an error occured during wiping out the
             // local data, then we report it as a *signature retry* error.
             reportStatus = UptakeTelemetry.STATUS.SIGNATURE_RETRY_ERROR;
             throw e;
           }
         } else {
           // The sync has thrown, it can be related to metadata, network or a general error.
-          if (e.message == MISSING_SIGNATURE_MSG) {
+          if (e instanceof RemoteSettingsClient.MissingSignatureError) {
             // Collection metadata has no signature info, no need to retry.
             reportStatus = UptakeTelemetry.STATUS.SIGNATURE_ERROR;
           } else if (/unparseable/.test(e.message)) {
             reportStatus = UptakeTelemetry.STATUS.PARSE_ERROR;
           } else if (/NetworkError/.test(e.message)) {
             reportStatus = UptakeTelemetry.STATUS.NETWORK_ERROR;
           } else if (/Timeout/.test(e.message)) {
             reportStatus = UptakeTelemetry.STATUS.TIMEOUT_ERROR;
@@ -439,17 +452,17 @@ class RemoteSettingsClient extends Event
                                                                      remoteRecords,
                                                                      timestamp);
     const verifier = Cc["@mozilla.org/security/contentsignatureverifier;1"]
       .createInstance(Ci.nsIContentSignatureVerifier);
     if (!verifier.verifyContentSignature(serialized,
                                          "p384ecdsa=" + signature,
                                          certChain,
                                          this.signerName)) {
-      throw new Error(`${INVALID_SIGNATURE_MSG} (${bucket}/${collection})`);
+      throw new RemoteSettingsClient.InvalidSignatureError(`${bucket}/${collection}`);
     }
   }
 
   /**
    * Fetch the whole list of records from the server, verify the signature again
    * and then compute a synchronization result as if the diff-based sync happened.
    * And eventually, wipe out the local data.
    *