From a8ac3f9cf58046f2cec928c9f24ac7a10964b490 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Wed, 11 Apr 2012 18:51:48 -0700 Subject: [PATCH] Bug 744627 - TokenServerClient should not call callbacks twice; r=rnewman --- .../tests/unit/test_tokenserverclient.js | 40 +++++++++++++++++++ services/common/tokenserverclient.js | 26 ++++++++++-- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/services/common/tests/unit/test_tokenserverclient.js b/services/common/tests/unit/test_tokenserverclient.js index caf3f6cf9a5a..db1804de372b 100644 --- a/services/common/tests/unit/test_tokenserverclient.js +++ b/services/common/tests/unit/test_tokenserverclient.js @@ -164,3 +164,43 @@ add_test(function test_rich_media_types() { 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); +}); diff --git a/services/common/tokenserverclient.js b/services/common/tokenserverclient.js index d5cbd8674aaf..7a2d116c7bf1 100644 --- a/services/common/tokenserverclient.js +++ b/services/common/tokenserverclient.js @@ -16,6 +16,7 @@ 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."); @@ -164,13 +165,32 @@ TokenServerClient.prototype = { 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); } }); },