Bug 1218591 - Reset the WebSocket retry counter when the server replies. r=dragana
authorKit Cambridge <kcambridge@mozilla.com>
Wed, 28 Oct 2015 17:07:11 -0700
changeset 305308 820fa1592bfa156281023a22e8aab04196117452
parent 305307 b82942655a6364a50060fd310d3caae450ac005a
child 305309 6b094871e0b0a282035dda1a09b88bb4c97dcf20
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1218591
milestone44.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 1218591 - Reset the WebSocket retry counter when the server replies. r=dragana
dom/push/PushServiceWebSocket.jsm
dom/push/test/xpcshell/head.js
dom/push/test/xpcshell/test_retry_ws.js
dom/push/test/xpcshell/xpcshell.ini
--- a/dom/push/PushServiceWebSocket.jsm
+++ b/dom/push/PushServiceWebSocket.jsm
@@ -1122,19 +1122,16 @@ this.PushServiceWebSocket = {
     this._releaseWakeLock();
 
     if (this._currentState != STATE_WAITING_FOR_WS_START) {
       debug("NOT in STATE_WAITING_FOR_WS_START. Current state " +
             this._currentState + ". Skipping");
       return;
     }
 
-    // Since we've had a successful connection reset the retry fail count.
-    this._retryFailCount = 0;
-
     let data = {
       messageType: "hello",
       use_webpush: true,
     };
 
     if (this._UAID) {
       data.uaid = this._UAID;
     }
@@ -1191,22 +1188,20 @@ this.PushServiceWebSocket = {
     let reply;
     try {
       reply = JSON.parse(message);
     } catch(e) {
       debug("Parsing JSON failed. text : " + message);
       return;
     }
 
-    // If we are not waiting for a hello message, reset the retry fail count
-    if (this._currentState != STATE_WAITING_FOR_HELLO) {
-      debug('Reseting _retryFailCount and _pingIntervalRetryTimes');
-      this._retryFailCount = 0;
-      this._pingIntervalRetryTimes = {};
-    }
+    // If we receive a message, we know the connection succeeded. Reset the
+    // connection attempt and ping interval counters.
+    this._retryFailCount = 0;
+    this._pingIntervalRetryTimes = {};
 
     let doNotHandle = false;
     if ((message === '{}') ||
         (reply.messageType === undefined) ||
         (reply.messageType === "ping") ||
         (typeof reply.messageType != "string")) {
       debug('Pong received');
       this._calculateAdaptivePing(false);
--- a/dom/push/test/xpcshell/head.js
+++ b/dom/push/test/xpcshell/head.js
@@ -373,17 +373,21 @@ MockWebSocket.prototype = {
   serverClose(statusCode, reason = '') {
     if (!isFinite(statusCode)) {
       statusCode = WEBSOCKET_CLOSE_GOING_AWAY;
     }
     waterfall(
       () => this._listener.onServerClose(this._context, statusCode, reason),
       () => this._listener.onStop(this._context, Cr.NS_BASE_STREAM_CLOSED)
     );
-  }
+  },
+
+  serverInterrupt(result = Cr.NS_ERROR_NET_RESET) {
+    waterfall(() => this._listener.onStop(this._context, result));
+  },
 };
 
 /**
  * Creates an object that exposes the same interface as NetworkInfo, used
  * to simulate network status changes on Desktop. All methods returns empty
  * carrier data.
  */
 function MockDesktopNetworkInfo() {}
new file mode 100644
--- /dev/null
+++ b/dom/push/test/xpcshell/test_retry_ws.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
+
+const userAgentID = '05f7b940-51b6-4b6f-8032-b83ebb577ded';
+
+function run_test() {
+  do_get_profile();
+  setPrefs({
+    userAgentID: userAgentID,
+    pingInterval: 10000,
+    retryBaseInterval: 25,
+  });
+  run_next_test();
+}
+
+add_task(function* test_ws_retry() {
+  let db = PushServiceWebSocket.newPushDB();
+  do_register_cleanup(() => {return db.drop().then(_ => db.close());});
+
+  yield db.put({
+    channelID: '61770ba9-2d57-4134-b949-d40404630d5b',
+    pushEndpoint: 'https://example.org/push/1',
+    scope: 'https://example.net/push/1',
+    version: 1,
+    originAttributes: '',
+    quota: Infinity,
+  });
+
+  let alarmDelays = [];
+  let setAlarm = PushService.setAlarm;
+  PushService.setAlarm = function(delay) {
+    alarmDelays.push(delay);
+    setAlarm.apply(this, arguments);
+  };
+
+  let handshakeDone;
+  let handshakePromise = new Promise(resolve => handshakeDone = resolve);
+  PushService.init({
+    serverURI: "wss://push.example.org/",
+    networkInfo: new MockDesktopNetworkInfo(),
+    makeWebSocket(uri) {
+      return new MockWebSocket(uri, {
+        onHello(request) {
+          if (alarmDelays.length == 10) {
+            PushService.setAlarm = setAlarm;
+            this.serverSendMsg(JSON.stringify({
+              messageType: 'hello',
+              status: 200,
+              uaid: userAgentID,
+            }));
+            handshakeDone();
+            return;
+          }
+          this.serverInterrupt();
+        },
+      });
+    },
+  });
+
+  yield waitForPromise(
+    handshakePromise,
+    45000,
+    'Timed out waiting for successful handshake'
+  );
+  deepEqual(alarmDelays, [25, 50, 100, 200, 400, 800, 1600, 3200, 6400, 10000],
+    'Wrong reconnect alarm delays');
+});
--- a/dom/push/test/xpcshell/xpcshell.ini
+++ b/dom/push/test/xpcshell/xpcshell.ini
@@ -35,16 +35,17 @@ run-sequentially = This will delete all 
 [test_unregister_empty_scope.js]
 [test_unregister_error.js]
 [test_unregister_invalid_json.js]
 [test_unregister_not_found.js]
 [test_unregister_success.js]
 [test_webapps_cleardata.js]
 [test_updateRecordNoEncryptionKeys_ws.js]
 [test_reconnect_retry.js]
+[test_retry_ws.js]
 #http2 test
 [test_resubscribe_4xxCode_http2.js]
 [test_resubscribe_5xxCode_http2.js]
 [test_resubscribe_listening_for_msg_error_http2.js]
 [test_register_5xxCode_http2.js]
 [test_updateRecordNoEncryptionKeys_http2.js]
 [test_register_success_http2.js]
 skip-if = !hasNode