Procházet zdrojové kódy

feat: detect broken XMPP WebSocket using ping

If ping timeouts two times in a row (25 seconds total)
the XMPP WebSocket connection will be killed and restarted.
master
paweldomas před 4 roky
rodič
revize
9018c34f7a
3 změnil soubory, kde provedl 40 přidání a 21 odebrání
  1. 28
    1
      modules/xmpp/XmppConnection.js
  2. 12
    14
      modules/xmpp/strophe.ping.js
  3. 0
    6
      modules/xmpp/xmpp.js

+ 28
- 1
modules/xmpp/XmppConnection.js Zobrazit soubor

@@ -6,6 +6,7 @@ import Listenable from '../util/Listenable';
6 6
 import { getJitterDelay } from '../util/Retry';
7 7
 
8 8
 import LastSuccessTracker from './StropheLastSuccess';
9
+import PingConnectionPlugin from './strophe.ping';
9 10
 
10 11
 const logger = getLogger(__filename);
11 12
 
@@ -80,6 +81,14 @@ export default class XmppConnection extends Listenable {
80 81
          * @private
81 82
          */
82 83
         this._deferredIQs = [];
84
+
85
+        // Ping plugin is mandatory for the Websocket mode to work correctly. It's used to detect when the connection
86
+        // is broken (WebSocket/TCP connection not closed gracefully).
87
+        this.addConnectionPlugin(
88
+            'ping',
89
+            new PingConnectionPlugin({
90
+                onPingThresholdExceeded: () => this._onPingErrorThresholdExceeded()
91
+            }));
83 92
     }
84 93
 
85 94
     /**
@@ -246,7 +255,10 @@ export default class XmppConnection extends Listenable {
246 255
             this._maybeEnableStreamResume();
247 256
             this._maybeStartWSKeepAlive();
248 257
             this._processDeferredIQs();
258
+            this.ping.startInterval(this.domain);
249 259
         } else if (status === Strophe.Status.DISCONNECTED) {
260
+            this.ping.stopInterval();
261
+
250 262
             // FIXME add RECONNECTING state instead of blocking the DISCONNECTED update
251 263
             blockCallback = this._tryResumingConnection();
252 264
             if (!blockCallback) {
@@ -278,7 +290,10 @@ export default class XmppConnection extends Listenable {
278 290
      * @returns {void}
279 291
      */
280 292
     closeWebsocket() {
281
-        this._stropheConn._proto && this._stropheConn._proto.socket && this._stropheConn._proto.socket.close();
293
+        if (this._stropheConn && this._stropheConn._proto) {
294
+            this._stropheConn._proto._closeSocket();
295
+            this._stropheConn._proto._onClose(null);
296
+        }
282 297
     }
283 298
 
284 299
     /**
@@ -459,6 +474,18 @@ export default class XmppConnection extends Listenable {
459 474
         });
460 475
     }
461 476
 
477
+    /**
478
+     * Called by the ping plugin when ping fails too many times.
479
+     *
480
+     * @returns {void}
481
+     */
482
+    _onPingErrorThresholdExceeded() {
483
+        if (this.isUsingWebSocket) {
484
+            logger.warn('Ping error threshold exceeded - killing the WebSocket');
485
+            this.closeWebsocket();
486
+        }
487
+    }
488
+
462 489
     /**
463 490
      *  Helper function to send presence stanzas. The main benefit is for sending presence stanzas for which you expect
464 491
      *  a responding presence stanza with the same id (for example when leaving a chat room).

+ 12
- 14
modules/xmpp/strophe.ping.js Zobrazit soubor

@@ -14,14 +14,16 @@ const logger = getLogger(__filename);
14 14
 const PING_INTERVAL = 10000;
15 15
 
16 16
 /**
17
- * Ping timeout error after 15 sec of waiting.
17
+ * Ping timeout error after 5 sec of waiting.
18 18
  */
19
-const PING_TIMEOUT = 15000;
19
+const PING_TIMEOUT = 5000;
20 20
 
21 21
 /**
22
- * Will close the connection after 3 consecutive ping errors.
22
+ * How many ping failures will be tolerated before the WebSocket connection is killed.
23
+ * The worst case scenario in case of ping timing out without a response is (25 seconds at the time of this writing):
24
+ * PING_THRESHOLD * PING_INTERVAL + PING_TIMEOUT
23 25
  */
24
-const PING_THRESHOLD = 3;
26
+const PING_THRESHOLD = 2;
25 27
 
26 28
 /**
27 29
  * The number of timestamps of send pings to keep.
@@ -38,14 +40,16 @@ const PING_TIMESTAMPS_TO_KEEP = 120000 / PING_INTERVAL;
38 40
 export default class PingConnectionPlugin extends ConnectionPlugin {
39 41
     /**
40 42
      * Contructs new object
41
-     * @param {XMPP} xmpp the xmpp module.
43
+     * @param {Object} options
44
+     * @param {Function} options.onPingThresholdExceeded - Callback called when ping fails too many times (controlled
45
+     * by the {@link PING_THRESHOLD} constant).
42 46
      * @constructor
43 47
      */
44
-    constructor(xmpp) {
48
+    constructor({ onPingThresholdExceeded }) {
45 49
         super();
46 50
         this.failedPings = 0;
47
-        this.xmpp = xmpp;
48 51
         this.pingExecIntervals = new Array(PING_TIMESTAMPS_TO_KEEP);
52
+        this._onPingThresholdExceeded = onPingThresholdExceeded;
49 53
     }
50 54
 
51 55
     /**
@@ -101,13 +105,7 @@ export default class PingConnectionPlugin extends ConnectionPlugin {
101 105
                 if (this.failedPings >= PING_THRESHOLD) {
102 106
                     GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
103 107
                     logger.error(errmsg, error);
104
-
105
-                    // FIXME it doesn't help to disconnect when 3rd PING
106
-                    // times out, it only stops Strophe from retrying.
107
-                    // Not really sure what's the right thing to do in that
108
-                    // situation, but just closing the connection makes no
109
-                    // sense.
110
-                    // self.connection.disconnect();
108
+                    this._onPingThresholdExceeded && this._onPingThresholdExceeded();
111 109
                 } else {
112 110
                     logger.warn(errmsg, error);
113 111
                 }

+ 0
- 6
modules/xmpp/xmpp.js Zobrazit soubor

@@ -17,7 +17,6 @@ import XmppConnection from './XmppConnection';
17 17
 import MucConnectionPlugin from './strophe.emuc';
18 18
 import JingleConnectionPlugin from './strophe.jingle';
19 19
 import initStropheLogger from './strophe.logger';
20
-import PingConnectionPlugin from './strophe.ping';
21 20
 import RayoConnectionPlugin from './strophe.rayo';
22 21
 import initStropheUtil from './strophe.util';
23 22
 
@@ -229,10 +228,6 @@ export default class XMPP extends Listenable {
229 228
                             `Ping NOT supported by ${pingJid} - please enable ping in your XMPP server config`);
230 229
                     }
231 230
 
232
-                    // It counterintuitive to start ping task when it's not supported, but since PING is now mandatory
233
-                    // it's done on purpose in order to print error logs and bring more attention.
234
-                    this.connection.ping.startInterval(pingJid);
235
-
236 231
                     // check for speakerstats
237 232
                     identities.forEach(identity => {
238 233
                         if (identity.type === 'speakerstats') {
@@ -646,7 +641,6 @@ export default class XMPP extends Listenable {
646 641
 
647 642
         this.connection.addConnectionPlugin('emuc', new MucConnectionPlugin(this));
648 643
         this.connection.addConnectionPlugin('jingle', new JingleConnectionPlugin(this, this.eventEmitter, iceConfig));
649
-        this.connection.addConnectionPlugin('ping', new PingConnectionPlugin(this));
650 644
         this.connection.addConnectionPlugin('rayo', new RayoConnectionPlugin());
651 645
     }
652 646
 

Načítá se…
Zrušit
Uložit