Pārlūkot izejas kodu

feat: Reduce pings and adds xmpp ping config (#1389)

* feat: Skips pinging when there was a recent server response.

* feat: Adds options to control xmpp ping settings.

* fix: Fixes wrong default value.

* squash: Received messages in ping interval sets failed ping to 0
dev1
Дамян Минков 4 gadus atpakaļ
vecāks
revīzija
6bb0b86c0a
Revīzijas autora e-pasta adrese nav piesaistīta nevienam kontam
4 mainītis faili ar 76 papildinājumiem un 26 dzēšanām
  1. 4
    0
      doc/API.md
  2. 5
    2
      modules/xmpp/XmppConnection.js
  3. 60
    21
      modules/xmpp/strophe.ping.js
  4. 7
    3
      modules/xmpp/xmpp.js

+ 4
- 0
doc/API.md Parādīt failu

227
             - `anonymousdomain`
227
             - `anonymousdomain`
228
         4. `enableLipSync` - (optional) boolean property which enables the lipsync feature. Currently works only in Chrome and is disabled by default.
228
         4. `enableLipSync` - (optional) boolean property which enables the lipsync feature. Currently works only in Chrome and is disabled by default.
229
         5. `clientNode` - The name of client node advertised in XEP-0115 'c' stanza
229
         5. `clientNode` - The name of client node advertised in XEP-0115 'c' stanza
230
+        6. xmppPing - (optional) JS Object - xmpp ping options
231
+            - `interval` - how often to send ping requests, default: 10000 (10 seconds)
232
+            - `timeout` - the time to wait for ping responses, default: 5000 (5 seconds)
233
+            - `threshold` - how many ping failures will be tolerated before the connection is killed, default: 2
230
 
234
 
231
 2. `connect(options)` - establish server connection
235
 2. `connect(options)` - establish server connection
232
     - `options` - JS Object with `id` and `password` properties.
236
     - `options` - JS Object with `id` and `password` properties.

+ 5
- 2
modules/xmpp/XmppConnection.js Parādīt failu

45
      * default with jitter. Pass -1 to disable. The actual interval equation is:
45
      * default with jitter. Pass -1 to disable. The actual interval equation is:
46
      * jitterDelay = (interval * 0.2) + (0.8 * interval * Math.random())
46
      * jitterDelay = (interval * 0.2) + (0.8 * interval * Math.random())
47
      * The keep alive is HTTP GET request to the {@link options.serviceUrl}.
47
      * The keep alive is HTTP GET request to the {@link options.serviceUrl}.
48
+     * @param {Object} [options.xmppPing] - The xmpp ping settings.
48
      */
49
      */
49
-    constructor({ enableWebsocketResume, websocketKeepAlive, serviceUrl }) {
50
+    constructor({ enableWebsocketResume, websocketKeepAlive, serviceUrl, xmppPing }) {
50
         super();
51
         super();
51
         this._options = {
52
         this._options = {
52
             enableWebsocketResume: typeof enableWebsocketResume === 'undefined' ? true : enableWebsocketResume,
53
             enableWebsocketResume: typeof enableWebsocketResume === 'undefined' ? true : enableWebsocketResume,
83
         this.addConnectionPlugin(
84
         this.addConnectionPlugin(
84
             'ping',
85
             'ping',
85
             new PingConnectionPlugin({
86
             new PingConnectionPlugin({
86
-                onPingThresholdExceeded: () => this._onPingErrorThresholdExceeded()
87
+                getTimeSinceLastServerResponse: () => this.getTimeSinceLastSuccess(),
88
+                onPingThresholdExceeded: () => this._onPingErrorThresholdExceeded(),
89
+                pingOptions: xmppPing
87
             }));
90
             }));
88
     }
91
     }
89
 
92
 

+ 60
- 21
modules/xmpp/strophe.ping.js Parādīt failu

9
 const logger = getLogger(__filename);
9
 const logger = getLogger(__filename);
10
 
10
 
11
 /**
11
 /**
12
- * Ping every 10 sec
12
+ * Default ping every 10 sec
13
  */
13
  */
14
-const PING_INTERVAL = 10000;
14
+const PING_DEFAULT_INTERVAL = 10000;
15
 
15
 
16
 /**
16
 /**
17
- * Ping timeout error after 5 sec of waiting.
17
+ * Default ping timeout error after 5 sec of waiting.
18
  */
18
  */
19
-const PING_TIMEOUT = 5000;
19
+const PING_DEFAULT_TIMEOUT = 5000;
20
 
20
 
21
 /**
21
 /**
22
- * How many ping failures will be tolerated before the WebSocket connection is killed.
22
+ * Default value for 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):
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
24
  * PING_THRESHOLD * PING_INTERVAL + PING_TIMEOUT
25
  */
25
  */
26
-const PING_THRESHOLD = 2;
26
+const PING_DEFAULT_THRESHOLD = 2;
27
 
27
 
28
 /**
28
 /**
29
- * The number of timestamps of send pings to keep.
30
- * The current value is 2 minutes.
31
- * @type {number} number of timestamps.
29
+ * How often to send ping requests.
32
  */
30
  */
33
-const PING_TIMESTAMPS_TO_KEEP = 120000 / PING_INTERVAL;
31
+let pingInterval;
32
+
33
+/**
34
+ * The time to wait for ping responses.
35
+ */
36
+let pingTimeout;
37
+
38
+/**
39
+ * How many ping failures will be tolerated before the connection is killed.
40
+ */
41
+let pingThreshold;
34
 
42
 
35
 /**
43
 /**
36
  * XEP-0199 ping plugin.
44
  * XEP-0199 ping plugin.
39
  */
47
  */
40
 export default class PingConnectionPlugin extends ConnectionPlugin {
48
 export default class PingConnectionPlugin extends ConnectionPlugin {
41
     /**
49
     /**
42
-     * Contructs new object
50
+     * Constructs new object
43
      * @param {Object} options
51
      * @param {Object} options
44
      * @param {Function} options.onPingThresholdExceeded - Callback called when ping fails too many times (controlled
52
      * @param {Function} options.onPingThresholdExceeded - Callback called when ping fails too many times (controlled
45
      * by the {@link PING_THRESHOLD} constant).
53
      * by the {@link PING_THRESHOLD} constant).
54
+     * @param {Function} options._getTimeSinceLastServerResponse - A function to obtain the last seen
55
+     * response from the server.
56
+     * @param {Object} options.pingOptions - The ping options if any.
46
      * @constructor
57
      * @constructor
47
      */
58
      */
48
-    constructor({ onPingThresholdExceeded }) {
59
+    constructor({ getTimeSinceLastServerResponse, onPingThresholdExceeded, pingOptions = {} }) {
49
         super();
60
         super();
50
         this.failedPings = 0;
61
         this.failedPings = 0;
51
-        this.pingExecIntervals = new Array(PING_TIMESTAMPS_TO_KEEP);
52
         this._onPingThresholdExceeded = onPingThresholdExceeded;
62
         this._onPingThresholdExceeded = onPingThresholdExceeded;
63
+        this._getTimeSinceLastServerResponse = getTimeSinceLastServerResponse;
64
+
65
+        this.pingInterval = typeof pingOptions.interval === 'number' ? pingOptions.interval : PING_DEFAULT_INTERVAL;
66
+        this.pingTimeout = typeof pingOptions.timeout === 'number' ? pingOptions.timeout : PING_DEFAULT_TIMEOUT;
67
+        this.pingThreshold = typeof pingOptions.threshold === 'number'
68
+            ? pingOptions.threshold : PING_DEFAULT_THRESHOLD;
69
+
70
+        // The number of timestamps of send pings to keep.
71
+        // The current value is 2 minutes.
72
+        this.pingTimestampsToKeep = Math.round(120000 / this.pingInterval);
73
+        this.pingExecIntervals = new Array(this.pingTimestampsToKeep);
53
     }
74
     }
54
 
75
 
55
     /**
76
     /**
91
      * This plugin supports only one such task and <tt>stopInterval</tt>
112
      * This plugin supports only one such task and <tt>stopInterval</tt>
92
      * must be called before starting a new one.
113
      * must be called before starting a new one.
93
      * @param remoteJid remote JID to which ping requests will be sent to.
114
      * @param remoteJid remote JID to which ping requests will be sent to.
94
-     * @param interval task interval in ms.
95
      */
115
      */
96
-    startInterval(remoteJid, interval = PING_INTERVAL) {
116
+    startInterval(remoteJid) {
97
         clearInterval(this.intervalId);
117
         clearInterval(this.intervalId);
98
         this.intervalId = window.setInterval(() => {
118
         this.intervalId = window.setInterval(() => {
119
+
120
+            // when there were some server responses in the interval since the last time we checked (_lastServerCheck)
121
+            // let's skip the ping
122
+
123
+            // server response is measured on raw input and ping response time is measured after all the xmpp
124
+            // processing is done, and when the last server response is a ping there can be slight misalignment of the
125
+            // times, we give it 100ms for that processing.
126
+            if (this._getTimeSinceLastServerResponse() + 100 < new Date() - this._lastServerCheck) {
127
+                // do this just to keep in sync the intervals so we can detect suspended device
128
+                this._addPingExecutionTimestamp();
129
+
130
+                this._lastServerCheck = new Date();
131
+                this.failedPings = 0;
132
+
133
+                return;
134
+            }
135
+
99
             this.ping(remoteJid, () => {
136
             this.ping(remoteJid, () => {
137
+                this._lastServerCheck = new Date();
138
+
100
                 this.failedPings = 0;
139
                 this.failedPings = 0;
101
             }, error => {
140
             }, error => {
102
                 this.failedPings += 1;
141
                 this.failedPings += 1;
103
                 const errmsg = `Ping ${error ? 'error' : 'timeout'}`;
142
                 const errmsg = `Ping ${error ? 'error' : 'timeout'}`;
104
 
143
 
105
-                if (this.failedPings >= PING_THRESHOLD) {
144
+                if (this.failedPings >= pingThreshold) {
106
                     GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
145
                     GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
107
                     logger.error(errmsg, error);
146
                     logger.error(errmsg, error);
108
                     this._onPingThresholdExceeded && this._onPingThresholdExceeded();
147
                     this._onPingThresholdExceeded && this._onPingThresholdExceeded();
109
                 } else {
148
                 } else {
110
                     logger.warn(errmsg, error);
149
                     logger.warn(errmsg, error);
111
                 }
150
                 }
112
-            }, PING_TIMEOUT);
113
-        }, interval);
114
-        logger.info(`XMPP pings will be sent every ${interval} ms`);
151
+            }, pingTimeout);
152
+        }, this.pingInterval);
153
+        logger.info(`XMPP pings will be sent every ${this.pingInterval} ms`);
115
     }
154
     }
116
 
155
 
117
     /**
156
     /**
134
         this.pingExecIntervals.push(new Date().getTime());
173
         this.pingExecIntervals.push(new Date().getTime());
135
 
174
 
136
         // keep array length to PING_TIMESTAMPS_TO_KEEP
175
         // keep array length to PING_TIMESTAMPS_TO_KEEP
137
-        if (this.pingExecIntervals.length > PING_TIMESTAMPS_TO_KEEP) {
176
+        if (this.pingExecIntervals.length > this.pingTimestampsToKeep) {
138
             this.pingExecIntervals.shift();
177
             this.pingExecIntervals.shift();
139
         }
178
         }
140
     }
179
     }
172
         // remove the interval between the ping sent
211
         // remove the interval between the ping sent
173
         // this way in normal execution there is no suspend and the return
212
         // this way in normal execution there is no suspend and the return
174
         // will be 0 or close to 0.
213
         // will be 0 or close to 0.
175
-        maxInterval -= PING_INTERVAL;
214
+        maxInterval -= pingInterval;
176
 
215
 
177
         // make sure we do not return less than 0
216
         // make sure we do not return less than 0
178
         return Math.max(maxInterval, 0);
217
         return Math.max(maxInterval, 0);

+ 7
- 3
modules/xmpp/xmpp.js Parādīt failu

32
  * @param {string} options.serviceUrl - The service URL for XMPP connection.
32
  * @param {string} options.serviceUrl - The service URL for XMPP connection.
33
  * @param {string} options.enableWebsocketResume - True to enable stream resumption.
33
  * @param {string} options.enableWebsocketResume - True to enable stream resumption.
34
  * @param {number} [options.websocketKeepAlive] - See {@link XmppConnection} constructor.
34
  * @param {number} [options.websocketKeepAlive] - See {@link XmppConnection} constructor.
35
+ * @param {Object} [options.xmppPing] - See {@link XmppConnection} constructor.
35
  * @returns {XmppConnection}
36
  * @returns {XmppConnection}
36
  */
37
  */
37
-function createConnection({ enableWebsocketResume, serviceUrl = '/http-bind', token, websocketKeepAlive }) {
38
+function createConnection({ enableWebsocketResume, serviceUrl = '/http-bind', token, websocketKeepAlive, xmppPing }) {
38
     // Append token as URL param
39
     // Append token as URL param
39
     if (token) {
40
     if (token) {
40
         // eslint-disable-next-line no-param-reassign
41
         // eslint-disable-next-line no-param-reassign
44
     return new XmppConnection({
45
     return new XmppConnection({
45
         enableWebsocketResume,
46
         enableWebsocketResume,
46
         serviceUrl,
47
         serviceUrl,
47
-        websocketKeepAlive
48
+        websocketKeepAlive,
49
+        xmppPing
48
     });
50
     });
49
 }
51
 }
50
 
52
 
91
      * module try to resume the session in case the Websocket connection breaks.
93
      * module try to resume the session in case the Websocket connection breaks.
92
      * @param {number} [options.websocketKeepAlive] - The websocket keep alive interval. See {@link XmppConnection}
94
      * @param {number} [options.websocketKeepAlive] - The websocket keep alive interval. See {@link XmppConnection}
93
      * constructor for more details.
95
      * constructor for more details.
96
+     * @param {Object} [options.xmppPing] - The xmpp ping settings.
94
      * @param {Array<Object>} options.p2pStunServers see {@link JingleConnectionPlugin} for more details.
97
      * @param {Array<Object>} options.p2pStunServers see {@link JingleConnectionPlugin} for more details.
95
      * @param token
98
      * @param token
96
      */
99
      */
111
             // FIXME remove deprecated bosh option at some point
114
             // FIXME remove deprecated bosh option at some point
112
             serviceUrl: options.serviceUrl || options.bosh,
115
             serviceUrl: options.serviceUrl || options.bosh,
113
             token,
116
             token,
114
-            websocketKeepAlive: options.websocketKeepAlive
117
+            websocketKeepAlive: options.websocketKeepAlive,
118
+            xmppPing: options.xmppPing
115
         });
119
         });
116
 
120
 
117
         this._initStrophePlugins();
121
         this._initStrophePlugins();

Notiek ielāde…
Atcelt
Saglabāt