Bug 744627 - TokenServerClient should not call callbacks twice; r=rnewman
authorGregory Szorc <gps@mozilla.com>
Wed, 11 Apr 2012 18:51:48 -0700
changeset 94807 e72c21511787eed9b1523d832012a21b72bd58e2
parent 94806 54f5a1ebcf4315b23defb6bf3d6e914e971e21a5
child 94808 51f25cd0e1b40e47384447936f74f78777e564ec
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs744627
milestone14.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 744627 - TokenServerClient should not call callbacks twice; r=rnewman
services/common/tests/unit/test_tokenserverclient.js
services/common/tokenserverclient.js
--- a/services/common/tests/unit/test_tokenserverclient.js
+++ b/services/common/tests/unit/test_tokenserverclient.js
@@ -159,8 +159,48 @@ add_test(function test_rich_media_types(
   let url = TEST_SERVER_URL + "foo";
   let client = new TokenServerClient();
   client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) {
     do_check_eq(null, error);
 
     server.stop(run_next_test);
   });
 });
+
+add_test(function test_exception_during_callback() {
+  _("Ensure that exceptions thrown during callback handling are handled.");
+
+  let server = httpd_setup({
+    "/foo": function(request, response) {
+      response.setStatusLine(request.httpVersion, 200, "OK");
+      response.setHeader("Content-Type", "application/json");
+
+      let body = JSON.stringify({
+        id:           "id",
+        key:          "key",
+        api_endpoint: "foo",
+        uid:          "uid",
+      });
+      response.bodyOutputStream.write(body, body.length);
+    }
+  });
+
+  let url = TEST_SERVER_URL + "foo";
+  let client = new TokenServerClient();
+  let cb = Async.makeSpinningCallback();
+  let callbackCount = 0;
+
+  client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) {
+    do_check_eq(null, error);
+
+    cb();
+
+    callbackCount += 1;
+    throw new Error("I am a bad function!");
+  });
+
+  cb.wait();
+  // This relies on some heavy event loop magic. The error in the main
+  // callback should already have been raised at this point.
+  do_check_eq(callbackCount, 1);
+
+  server.stop(run_next_test);
+});
--- a/services/common/tokenserverclient.js
+++ b/services/common/tokenserverclient.js
@@ -11,16 +11,17 @@ const EXPORTED_SYMBOLS = [
   "TokenServerClientServerError"
 ];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-common/preferences.js");
 Cu.import("resource://services-common/rest.js");
+Cu.import("resource://services-common/utils.js");
 
 const Prefs = new Preferences("services.common.tokenserverclient.");
 
 /**
  * Represents a TokenServerClient error that occurred on the client.
  *
  * This is the base type for all errors raised by client operations.
  *
@@ -159,23 +160,42 @@ TokenServerClient.prototype = {
     req.setHeader("authorization", "Browser-ID " + assertion);
     let client = this;
     req.get(function onResponse(error) {
       if (error) {
         cb(new TokenServerClientNetworkError(error), null);
         return;
       }
 
+      let self = this;
+      function callCallback(error, result) {
+        if (!cb) {
+          self._log.warn("Callback already called! Did it throw?");
+          return;
+        }
+
+        try {
+          cb(error, result);
+        } catch (ex) {
+          self._log.warn("Exception when calling user-supplied callback: " +
+                         CommonUtils.exceptionStr(ex));
+        }
+
+        cb = null;
+      }
+
       try {
-        client._processTokenResponse(this.response, cb);
+        client._processTokenResponse(this.response, callCallback);
       } catch (ex) {
+        this._log.warn("Error processing token server response: " +
+                       CommonUtils.exceptionStr(ex));
+
         let error = new TokenServerClientError(ex);
         error.response = this.response;
-        cb(error, null);
-        return;
+        callCallback(error, null);
       }
     });
   },
 
   /**
    * Handler to process token request responses.
    *
    * @param response