|
|
@@ -38,10 +38,17 @@ export default class XmppConnection extends Listenable {
|
|
38
|
38
|
* @param {Object} options
|
|
39
|
39
|
* @param {String} options.serviceUrl - The BOSH or WebSocket service URL.
|
|
40
|
40
|
* @param {String} options.enableWebsocketResume - True to enable stream resumption.
|
|
|
41
|
+ * @param {Number} [options.websocketKeepAlive=240000] - The websocket keep alive interval. It's 4 minutes by
|
|
|
42
|
+ * default with jitter. Pass -1 to disable. The actual interval equation is:
|
|
|
43
|
+ * jitterDelay = (interval * 0.2) + (0.8 * interval * Math.random())
|
|
|
44
|
+ * The keep alive is HTTP GET request to the {@link options.serviceUrl}.
|
|
41
|
45
|
*/
|
|
42
|
|
- constructor({ enableWebsocketResume, serviceUrl }) {
|
|
|
46
|
+ constructor({ enableWebsocketResume, websocketKeepAlive, serviceUrl }) {
|
|
43
|
47
|
super();
|
|
44
|
|
- this._options = { enableWebsocketResume };
|
|
|
48
|
+ this._options = {
|
|
|
49
|
+ enableWebsocketResume,
|
|
|
50
|
+ websocketKeepAlive: typeof websocketKeepAlive === 'undefined' ? 4 * 60 * 1000 : Number(websocketKeepAlive)
|
|
|
51
|
+ };
|
|
45
|
52
|
this._stropheConn = new Strophe.Connection(serviceUrl);
|
|
46
|
53
|
this._usesWebsocket = serviceUrl.startsWith('ws:') || serviceUrl.startsWith('wss:');
|
|
47
|
54
|
|
|
|
@@ -218,9 +225,13 @@ export default class XmppConnection extends Listenable {
|
|
218
|
225
|
|
|
219
|
226
|
if (status === Strophe.Status.CONNECTED) {
|
|
220
|
227
|
this._maybeEnableStreamResume();
|
|
|
228
|
+ this._maybeStartWSKeepAlive();
|
|
221
|
229
|
} else if (status === Strophe.Status.DISCONNECTED) {
|
|
222
|
230
|
// FIXME add RECONNECTING state instead of blocking the DISCONNECTED update
|
|
223
|
231
|
blockCallback = this._tryResumingConnection();
|
|
|
232
|
+ if (!blockCallback) {
|
|
|
233
|
+ clearTimeout(this._wsKeepAlive);
|
|
|
234
|
+ }
|
|
224
|
235
|
}
|
|
225
|
236
|
|
|
226
|
237
|
if (!blockCallback) {
|
|
|
@@ -248,6 +259,7 @@ export default class XmppConnection extends Listenable {
|
|
248
|
259
|
*/
|
|
249
|
260
|
disconnect(...args) {
|
|
250
|
261
|
clearTimeout(this._resumeTimeout);
|
|
|
262
|
+ clearTimeout(this._wsKeepAlive);
|
|
251
|
263
|
this._stropheConn.disconnect(...args);
|
|
252
|
264
|
}
|
|
253
|
265
|
|
|
|
@@ -296,6 +308,36 @@ export default class XmppConnection extends Listenable {
|
|
296
|
308
|
}
|
|
297
|
309
|
}
|
|
298
|
310
|
|
|
|
311
|
+ /**
|
|
|
312
|
+ * Starts the Websocket keep alive if enabled.
|
|
|
313
|
+ *
|
|
|
314
|
+ * @private
|
|
|
315
|
+ * @returns {void}
|
|
|
316
|
+ */
|
|
|
317
|
+ _maybeStartWSKeepAlive() {
|
|
|
318
|
+ const { websocketKeepAlive } = this._options;
|
|
|
319
|
+
|
|
|
320
|
+ if (this._usesWebsocket && websocketKeepAlive > 0) {
|
|
|
321
|
+ this._wsKeepAlive || logger.info(`WebSocket keep alive interval: ${websocketKeepAlive}ms`);
|
|
|
322
|
+ clearTimeout(this._wsKeepAlive);
|
|
|
323
|
+
|
|
|
324
|
+ const intervalWithJitter
|
|
|
325
|
+ = /* base */ (websocketKeepAlive * 0.2) + /* jitter */ (Math.random() * 0.8 * websocketKeepAlive);
|
|
|
326
|
+
|
|
|
327
|
+ logger.debug(`Scheduling next WebSocket keep-alive in ${intervalWithJitter}ms`);
|
|
|
328
|
+
|
|
|
329
|
+ this._wsKeepAlive = setTimeout(() => {
|
|
|
330
|
+ const url = this.service.replace('wss', 'https').replace('ws', 'http');
|
|
|
331
|
+
|
|
|
332
|
+ fetch(url).catch(
|
|
|
333
|
+ error => {
|
|
|
334
|
+ logger.error(`Websocket Keep alive failed for url: ${url}`, { error });
|
|
|
335
|
+ })
|
|
|
336
|
+ .then(() => this._maybeStartWSKeepAlive());
|
|
|
337
|
+ }, intervalWithJitter);
|
|
|
338
|
+ }
|
|
|
339
|
+ }
|
|
|
340
|
+
|
|
299
|
341
|
/**
|
|
300
|
342
|
* Send a stanza. This function is called to push data onto the send queue to go out over the wire.
|
|
301
|
343
|
*
|