@webex/internal-plugin-mercury 3.0.0-beta.41 → 3.0.0-beta.411
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/mercury.js +64 -53
- package/dist/mercury.js.map +1 -1
- package/dist/socket/socket-base.js +43 -27
- package/dist/socket/socket-base.js.map +1 -1
- package/package.json +14 -14
- package/src/mercury.js +63 -52
- package/src/socket/socket-base.js +61 -28
- package/test/integration/spec/webex.js +3 -2
- package/test/unit/spec/mercury.js +87 -9
- package/test/unit/spec/socket.js +17 -0
package/src/mercury.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable require-jsdoc */
|
|
1
2
|
/*!
|
|
2
3
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
4
|
*/
|
|
@@ -23,6 +24,7 @@ const normalReconnectReasons = ['idle', 'done (forced)', 'pong not received', 'p
|
|
|
23
24
|
|
|
24
25
|
const Mercury = WebexPlugin.extend({
|
|
25
26
|
namespace: 'Mercury',
|
|
27
|
+
lastError: undefined,
|
|
26
28
|
|
|
27
29
|
session: {
|
|
28
30
|
connected: {
|
|
@@ -33,6 +35,10 @@ const Mercury = WebexPlugin.extend({
|
|
|
33
35
|
default: false,
|
|
34
36
|
type: 'boolean',
|
|
35
37
|
},
|
|
38
|
+
hasEverConnected: {
|
|
39
|
+
default: false,
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
},
|
|
36
42
|
socket: 'object',
|
|
37
43
|
localClusterServiceUrls: 'object',
|
|
38
44
|
},
|
|
@@ -46,10 +52,18 @@ const Mercury = WebexPlugin.extend({
|
|
|
46
52
|
},
|
|
47
53
|
},
|
|
48
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Get the last error.
|
|
57
|
+
* @returns {any} The last error.
|
|
58
|
+
*/
|
|
59
|
+
getLastError() {
|
|
60
|
+
return this.lastError;
|
|
61
|
+
},
|
|
62
|
+
|
|
49
63
|
@oneFlight
|
|
50
64
|
connect(webSocketUrl) {
|
|
51
65
|
if (this.connected) {
|
|
52
|
-
this.logger.info(
|
|
66
|
+
this.logger.info(`${this.namespace}: already connected, will not connect again`);
|
|
53
67
|
|
|
54
68
|
return Promise.resolve();
|
|
55
69
|
}
|
|
@@ -59,7 +73,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
59
73
|
return Promise.resolve(
|
|
60
74
|
this.webex.internal.device.registered || this.webex.internal.device.register()
|
|
61
75
|
).then(() => {
|
|
62
|
-
this.logger.info(
|
|
76
|
+
this.logger.info(`${this.namespace}: connecting`);
|
|
63
77
|
|
|
64
78
|
return this._connectWithBackoff(webSocketUrl);
|
|
65
79
|
});
|
|
@@ -69,16 +83,14 @@ const Mercury = WebexPlugin.extend({
|
|
|
69
83
|
disconnect() {
|
|
70
84
|
return new Promise((resolve) => {
|
|
71
85
|
if (this.backoffCall) {
|
|
72
|
-
this.logger.info(
|
|
86
|
+
this.logger.info(`${this.namespace}: aborting connection`);
|
|
73
87
|
this.backoffCall.abort();
|
|
74
88
|
}
|
|
75
89
|
|
|
76
90
|
if (this.socket) {
|
|
77
91
|
this.socket.removeAllListeners('message');
|
|
78
92
|
this.once('offline', resolve);
|
|
79
|
-
this.socket.close();
|
|
80
|
-
|
|
81
|
-
return;
|
|
93
|
+
resolve(this.socket.close());
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
resolve();
|
|
@@ -161,11 +173,12 @@ const Mercury = WebexPlugin.extend({
|
|
|
161
173
|
socket.on('close', (...args) => this._onclose(...args));
|
|
162
174
|
socket.on('message', (...args) => this._onmessage(...args));
|
|
163
175
|
socket.on('sequence-mismatch', (...args) => this._emit('sequence-mismatch', ...args));
|
|
176
|
+
socket.on('ping-pong-latency', (...args) => this._emit('ping-pong-latency', ...args));
|
|
164
177
|
|
|
165
178
|
Promise.all([this._prepareUrl(socketUrl), this.webex.credentials.getUserToken()])
|
|
166
179
|
.then(([webSocketUrl, token]) => {
|
|
167
180
|
if (!this.backoffCall) {
|
|
168
|
-
const msg =
|
|
181
|
+
const msg = `${this.namespace}: prevent socket open when backoffCall no longer defined`;
|
|
169
182
|
|
|
170
183
|
this.logger.info(msg);
|
|
171
184
|
|
|
@@ -185,7 +198,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
185
198
|
|
|
186
199
|
// if the consumer has supplied request options use them
|
|
187
200
|
if (this.webex.config.defaultMercuryOptions) {
|
|
188
|
-
this.logger.info(
|
|
201
|
+
this.logger.info(`${this.namespace}: setting custom options`);
|
|
189
202
|
options = {...options, ...this.webex.config.defaultMercuryOptions};
|
|
190
203
|
}
|
|
191
204
|
|
|
@@ -193,18 +206,14 @@ const Mercury = WebexPlugin.extend({
|
|
|
193
206
|
// the socket if it is in the process of being opened.
|
|
194
207
|
this.socket = socket;
|
|
195
208
|
|
|
209
|
+
this.logger.info(`${this.namespace} connection url: ${webSocketUrl}`);
|
|
210
|
+
|
|
196
211
|
return socket.open(webSocketUrl, options);
|
|
197
212
|
})
|
|
198
213
|
.then(() => {
|
|
199
|
-
this.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
},
|
|
203
|
-
tags: {
|
|
204
|
-
action: 'connected',
|
|
205
|
-
url: attemptWSUrl,
|
|
206
|
-
},
|
|
207
|
-
});
|
|
214
|
+
this.logger.info(
|
|
215
|
+
`${this.namespace}: connected to mercury, success, action: connected, url: ${attemptWSUrl}`
|
|
216
|
+
);
|
|
208
217
|
callback();
|
|
209
218
|
|
|
210
219
|
return this.webex.internal.feature
|
|
@@ -218,6 +227,8 @@ const Mercury = WebexPlugin.extend({
|
|
|
218
227
|
});
|
|
219
228
|
})
|
|
220
229
|
.catch((reason) => {
|
|
230
|
+
this.lastError = reason; // remember the last error
|
|
231
|
+
|
|
221
232
|
// Suppress connection errors that appear to be network related. This
|
|
222
233
|
// may end up suppressing metrics during outages, but we might not care
|
|
223
234
|
// (especially since many of our outages happen in a way that client
|
|
@@ -225,19 +236,19 @@ const Mercury = WebexPlugin.extend({
|
|
|
225
236
|
if (reason.code !== 1006 && this.backoffCall && this.backoffCall.getNumRetries() > 0) {
|
|
226
237
|
this._emit('connection_failed', reason, {retries: this.backoffCall.getNumRetries()});
|
|
227
238
|
}
|
|
228
|
-
this.logger.info(
|
|
239
|
+
this.logger.info(`${this.namespace}: connection attempt failed`, reason);
|
|
229
240
|
// UnknownResponse is produced by IE for any 4XXX; treated it like a bad
|
|
230
241
|
// web socket url and let WDM handle the token checking
|
|
231
242
|
if (reason instanceof UnknownResponse) {
|
|
232
243
|
this.logger.info(
|
|
233
|
-
|
|
244
|
+
`${this.namespace}: received unknown response code, refreshing device registration`
|
|
234
245
|
);
|
|
235
246
|
|
|
236
247
|
return this.webex.internal.device.refresh().then(() => callback(reason));
|
|
237
248
|
}
|
|
238
249
|
// NotAuthorized implies expired token
|
|
239
250
|
if (reason instanceof NotAuthorized) {
|
|
240
|
-
this.logger.info(
|
|
251
|
+
this.logger.info(`${this.namespace}: received authorization error, reauthorizing`);
|
|
241
252
|
|
|
242
253
|
return this.webex.credentials.refresh({force: true}).then(() => callback(reason));
|
|
243
254
|
}
|
|
@@ -250,7 +261,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
250
261
|
// BadRequest implies current credentials are for a Service Account
|
|
251
262
|
// Forbidden implies current user is not entitle for Webex
|
|
252
263
|
if (reason instanceof BadRequest || reason instanceof Forbidden) {
|
|
253
|
-
this.logger.warn(
|
|
264
|
+
this.logger.warn(`${this.namespace}: received unrecoverable response from mercury`);
|
|
254
265
|
this.backoffCall.abort();
|
|
255
266
|
|
|
256
267
|
return callback(reason);
|
|
@@ -261,18 +272,8 @@ const Mercury = WebexPlugin.extend({
|
|
|
261
272
|
.then((haMessagingEnabled) => {
|
|
262
273
|
if (haMessagingEnabled) {
|
|
263
274
|
this.logger.info(
|
|
264
|
-
|
|
275
|
+
`${this.namespace}: received a generic connection error, will try to connect to another datacenter. failed, action: 'failed', url: ${attemptWSUrl} error: ${reason.message}`
|
|
265
276
|
);
|
|
266
|
-
this.webex.internal.metrics.submitClientMetrics('web-ha-mercury', {
|
|
267
|
-
fields: {
|
|
268
|
-
success: false,
|
|
269
|
-
},
|
|
270
|
-
tags: {
|
|
271
|
-
action: 'failed',
|
|
272
|
-
error: reason.message,
|
|
273
|
-
url: attemptWSUrl,
|
|
274
|
-
},
|
|
275
|
-
});
|
|
276
277
|
|
|
277
278
|
return this.webex.internal.services.markFailedUrl(attemptWSUrl);
|
|
278
279
|
}
|
|
@@ -285,7 +286,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
285
286
|
return callback(reason);
|
|
286
287
|
})
|
|
287
288
|
.catch((reason) => {
|
|
288
|
-
this.logger.error(
|
|
289
|
+
this.logger.error(`${this.namespace}: failed to handle connection failure`, reason);
|
|
289
290
|
callback(reason);
|
|
290
291
|
});
|
|
291
292
|
},
|
|
@@ -301,12 +302,15 @@ const Mercury = WebexPlugin.extend({
|
|
|
301
302
|
this.backoffCall = undefined;
|
|
302
303
|
if (err) {
|
|
303
304
|
this.logger.info(
|
|
304
|
-
|
|
305
|
+
`${
|
|
306
|
+
this.namespace
|
|
307
|
+
}: failed to connect after ${call.getNumRetries()} retries; log statement about next retry was inaccurate; ${err}`
|
|
305
308
|
);
|
|
306
309
|
|
|
307
310
|
return reject(err);
|
|
308
311
|
}
|
|
309
312
|
this.connected = true;
|
|
313
|
+
this.hasEverConnected = true;
|
|
310
314
|
this._emit('online');
|
|
311
315
|
|
|
312
316
|
return resolve();
|
|
@@ -314,7 +318,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
314
318
|
|
|
315
319
|
// eslint-disable-next-line prefer-reflect
|
|
316
320
|
call = backoff.call((callback) => {
|
|
317
|
-
this.logger.info(
|
|
321
|
+
this.logger.info(`${this.namespace}: executing connection attempt ${call.getNumRetries()}`);
|
|
318
322
|
this._attemptConnection(webSocketUrl, callback);
|
|
319
323
|
}, onComplete);
|
|
320
324
|
|
|
@@ -325,12 +329,14 @@ const Mercury = WebexPlugin.extend({
|
|
|
325
329
|
})
|
|
326
330
|
);
|
|
327
331
|
|
|
328
|
-
if (this.config.
|
|
332
|
+
if (this.config.initialConnectionMaxRetries && !this.hasEverConnected) {
|
|
333
|
+
call.failAfter(this.config.initialConnectionMaxRetries);
|
|
334
|
+
} else if (this.config.maxRetries) {
|
|
329
335
|
call.failAfter(this.config.maxRetries);
|
|
330
336
|
}
|
|
331
337
|
|
|
332
338
|
call.on('abort', () => {
|
|
333
|
-
this.logger.info(
|
|
339
|
+
this.logger.info(`${this.namespace}: connection aborted`);
|
|
334
340
|
reject(new Error('Mercury Connection Aborted'));
|
|
335
341
|
});
|
|
336
342
|
|
|
@@ -340,16 +346,16 @@ const Mercury = WebexPlugin.extend({
|
|
|
340
346
|
const delay = Math.min(call.strategy_.nextBackoffDelay_, this.config.backoffTimeMax);
|
|
341
347
|
|
|
342
348
|
this.logger.info(
|
|
343
|
-
|
|
349
|
+
`${this.namespace}: failed to connect; attempting retry ${number + 1} in ${delay} ms`
|
|
344
350
|
);
|
|
345
351
|
/* istanbul ignore if */
|
|
346
352
|
if (process.env.NODE_ENV === 'development') {
|
|
347
|
-
this.logger.debug(
|
|
353
|
+
this.logger.debug(`${this.namespace}: `, err, err.stack);
|
|
348
354
|
}
|
|
349
355
|
|
|
350
356
|
return;
|
|
351
357
|
}
|
|
352
|
-
this.logger.info(
|
|
358
|
+
this.logger.info(`${this.namespace}: connected`);
|
|
353
359
|
});
|
|
354
360
|
|
|
355
361
|
call.start();
|
|
@@ -362,7 +368,10 @@ const Mercury = WebexPlugin.extend({
|
|
|
362
368
|
try {
|
|
363
369
|
this.trigger(...args);
|
|
364
370
|
} catch (error) {
|
|
365
|
-
this.logger.error(
|
|
371
|
+
this.logger.error(`${this.namespace}: error occurred in event handler`, {
|
|
372
|
+
error,
|
|
373
|
+
arguments: args,
|
|
374
|
+
});
|
|
366
375
|
}
|
|
367
376
|
},
|
|
368
377
|
|
|
@@ -403,20 +412,20 @@ const Mercury = WebexPlugin.extend({
|
|
|
403
412
|
case 1003:
|
|
404
413
|
// metric: disconnect
|
|
405
414
|
this.logger.info(
|
|
406
|
-
|
|
415
|
+
`${this.namespace}: Mercury service rejected last message; will not reconnect: ${event.reason}`
|
|
407
416
|
);
|
|
408
417
|
this._emit('offline.permanent', event);
|
|
409
418
|
break;
|
|
410
419
|
case 4000:
|
|
411
420
|
// metric: disconnect
|
|
412
|
-
this.logger.info(
|
|
421
|
+
this.logger.info(`${this.namespace}: socket replaced; will not reconnect`);
|
|
413
422
|
this._emit('offline.replaced', event);
|
|
414
423
|
break;
|
|
415
424
|
case 1001:
|
|
416
425
|
case 1005:
|
|
417
426
|
case 1006:
|
|
418
427
|
case 1011:
|
|
419
|
-
this.logger.info(
|
|
428
|
+
this.logger.info(`${this.namespace}: socket disconnected; reconnecting`);
|
|
420
429
|
this._emit('offline.transient', event);
|
|
421
430
|
this._reconnect(socketUrl);
|
|
422
431
|
// metric: disconnect
|
|
@@ -424,23 +433,25 @@ const Mercury = WebexPlugin.extend({
|
|
|
424
433
|
break;
|
|
425
434
|
case 1000:
|
|
426
435
|
if (normalReconnectReasons.includes(reason)) {
|
|
427
|
-
this.logger.info(
|
|
436
|
+
this.logger.info(`${this.namespace}: socket disconnected; reconnecting`);
|
|
428
437
|
this._emit('offline.transient', event);
|
|
429
438
|
this._reconnect(socketUrl);
|
|
430
439
|
// metric: disconnect
|
|
431
440
|
// if (reason === done forced) metric: force closure
|
|
432
441
|
} else {
|
|
433
|
-
this.logger.info(
|
|
442
|
+
this.logger.info(`${this.namespace}: socket disconnected; will not reconnect`);
|
|
434
443
|
this._emit('offline.permanent', event);
|
|
435
444
|
}
|
|
436
445
|
break;
|
|
437
446
|
default:
|
|
438
|
-
this.logger.info(
|
|
447
|
+
this.logger.info(
|
|
448
|
+
`${this.namespace}: socket disconnected unexpectedly; will not reconnect`
|
|
449
|
+
);
|
|
439
450
|
// unexpected disconnect
|
|
440
451
|
this._emit('offline.permanent', event);
|
|
441
452
|
}
|
|
442
453
|
} catch (error) {
|
|
443
|
-
this.logger.error(
|
|
454
|
+
this.logger.error(`${this.namespace}: error occurred in close handler`, error);
|
|
444
455
|
}
|
|
445
456
|
},
|
|
446
457
|
|
|
@@ -448,7 +459,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
448
459
|
const envelope = event.data;
|
|
449
460
|
|
|
450
461
|
if (process.env.ENABLE_MERCURY_LOGGING) {
|
|
451
|
-
this.logger.debug(
|
|
462
|
+
this.logger.debug(`${this.namespace}: message envelope: `, envelope);
|
|
452
463
|
}
|
|
453
464
|
|
|
454
465
|
const {data} = envelope;
|
|
@@ -465,7 +476,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
465
476
|
resolve((this.webex[namespace] || this.webex.internal[namespace])[name](data))
|
|
466
477
|
).catch((reason) =>
|
|
467
478
|
this.logger.error(
|
|
468
|
-
|
|
479
|
+
`${this.namespace}: error occurred in autowired event handler for ${data.eventType}`,
|
|
469
480
|
reason
|
|
470
481
|
)
|
|
471
482
|
);
|
|
@@ -484,12 +495,12 @@ const Mercury = WebexPlugin.extend({
|
|
|
484
495
|
}
|
|
485
496
|
})
|
|
486
497
|
.catch((reason) => {
|
|
487
|
-
this.logger.error(
|
|
498
|
+
this.logger.error(`${this.namespace}: error occurred processing socket message`, reason);
|
|
488
499
|
});
|
|
489
500
|
},
|
|
490
501
|
|
|
491
502
|
_reconnect(webSocketUrl) {
|
|
492
|
-
this.logger.info(
|
|
503
|
+
this.logger.info(`${this.namespace}: reconnecting`);
|
|
493
504
|
|
|
494
505
|
return this.connect(webSocketUrl);
|
|
495
506
|
},
|
|
@@ -30,6 +30,7 @@ export default class Socket extends EventEmitter {
|
|
|
30
30
|
*/
|
|
31
31
|
constructor() {
|
|
32
32
|
super();
|
|
33
|
+
this._domain = 'unknown-domain';
|
|
33
34
|
this.onmessage = this.onmessage.bind(this);
|
|
34
35
|
this.onclose = this.onclose.bind(this);
|
|
35
36
|
}
|
|
@@ -111,10 +112,10 @@ export default class Socket extends EventEmitter {
|
|
|
111
112
|
return;
|
|
112
113
|
}
|
|
113
114
|
// logger is defined once open is called
|
|
114
|
-
this.logger.info(
|
|
115
|
+
this.logger.info(`socket,${this._domain}: closing`);
|
|
115
116
|
|
|
116
117
|
if (socket.readyState === 2 || socket.readyState === 3) {
|
|
117
|
-
this.logger.info(
|
|
118
|
+
this.logger.info(`socket,${this._domain}: already closed`);
|
|
118
119
|
resolve();
|
|
119
120
|
|
|
120
121
|
return;
|
|
@@ -134,7 +135,7 @@ export default class Socket extends EventEmitter {
|
|
|
134
135
|
|
|
135
136
|
const closeTimer = safeSetTimeout(() => {
|
|
136
137
|
try {
|
|
137
|
-
this.logger.info(
|
|
138
|
+
this.logger.info(`socket,${this._domain}: no close event received, forcing closure`);
|
|
138
139
|
resolve(
|
|
139
140
|
this.onclose({
|
|
140
141
|
code: 1000,
|
|
@@ -142,12 +143,12 @@ export default class Socket extends EventEmitter {
|
|
|
142
143
|
})
|
|
143
144
|
);
|
|
144
145
|
} catch (error) {
|
|
145
|
-
this.logger.warn(
|
|
146
|
+
this.logger.warn(`socket,${this._domain}: force-close failed`, error);
|
|
146
147
|
}
|
|
147
148
|
}, this.forceCloseDelay);
|
|
148
149
|
|
|
149
150
|
socket.onclose = (event) => {
|
|
150
|
-
this.logger.info(
|
|
151
|
+
this.logger.info(`socket,${this._domain}: close event fired`, event.code, event.reason);
|
|
151
152
|
clearTimeout(closeTimer);
|
|
152
153
|
this.onclose(event);
|
|
153
154
|
resolve(event);
|
|
@@ -171,6 +172,12 @@ export default class Socket extends EventEmitter {
|
|
|
171
172
|
* @returns {Promise}
|
|
172
173
|
*/
|
|
173
174
|
open(url, options) {
|
|
175
|
+
try {
|
|
176
|
+
this._domain = new URL(url).hostname;
|
|
177
|
+
} catch {
|
|
178
|
+
this._domain = url;
|
|
179
|
+
}
|
|
180
|
+
|
|
174
181
|
return new Promise((resolve, reject) => {
|
|
175
182
|
/* eslint complexity: [0] */
|
|
176
183
|
if (!url) {
|
|
@@ -201,7 +208,7 @@ export default class Socket extends EventEmitter {
|
|
|
201
208
|
|
|
202
209
|
const WebSocket = Socket.getWebSocketConstructor();
|
|
203
210
|
|
|
204
|
-
this.logger.info(
|
|
211
|
+
this.logger.info(`socket,${this._domain}: creating WebSocket`);
|
|
205
212
|
const socket = new WebSocket(url, [], options);
|
|
206
213
|
|
|
207
214
|
socket.binaryType = 'arraybuffer';
|
|
@@ -209,7 +216,7 @@ export default class Socket extends EventEmitter {
|
|
|
209
216
|
|
|
210
217
|
socket.onclose = (event) => {
|
|
211
218
|
event = this._fixCloseCode(event);
|
|
212
|
-
this.logger.info(
|
|
219
|
+
this.logger.info(`socket,${this._domain}: closed before open`, event.code, event.reason);
|
|
213
220
|
switch (event.code) {
|
|
214
221
|
case 1005:
|
|
215
222
|
// IE 11 doesn't seem to allow 4XXX codes, so if we get a 1005, assume
|
|
@@ -231,10 +238,10 @@ export default class Socket extends EventEmitter {
|
|
|
231
238
|
};
|
|
232
239
|
|
|
233
240
|
socket.onopen = () => {
|
|
234
|
-
this.logger.info(
|
|
241
|
+
this.logger.info(`socket,${this._domain}: connected`);
|
|
235
242
|
this._authorize()
|
|
236
243
|
.then(() => {
|
|
237
|
-
this.logger.info(
|
|
244
|
+
this.logger.info(`socket,${this._domain}: authorized`);
|
|
238
245
|
socket.onclose = this.onclose;
|
|
239
246
|
resolve();
|
|
240
247
|
})
|
|
@@ -242,11 +249,11 @@ export default class Socket extends EventEmitter {
|
|
|
242
249
|
};
|
|
243
250
|
|
|
244
251
|
socket.onerror = (event) => {
|
|
245
|
-
this.logger.warn(
|
|
252
|
+
this.logger.warn(`socket,${this._domain}: error event fired`, event);
|
|
246
253
|
};
|
|
247
254
|
|
|
248
255
|
sockets.set(this, socket);
|
|
249
|
-
this.logger.info(
|
|
256
|
+
this.logger.info(`socket,${this._domain}: waiting for server`);
|
|
250
257
|
});
|
|
251
258
|
}
|
|
252
259
|
|
|
@@ -256,7 +263,7 @@ export default class Socket extends EventEmitter {
|
|
|
256
263
|
* @returns {undefined}
|
|
257
264
|
*/
|
|
258
265
|
onclose(event) {
|
|
259
|
-
this.logger.info(
|
|
266
|
+
this.logger.info(`socket,${this._domain}: closed`, event.code, event.reason);
|
|
260
267
|
clearTimeout(this.pongTimer);
|
|
261
268
|
clearTimeout(this.pingTimer);
|
|
262
269
|
|
|
@@ -278,10 +285,10 @@ export default class Socket extends EventEmitter {
|
|
|
278
285
|
const data = JSON.parse(event.data);
|
|
279
286
|
const sequenceNumber = parseInt(data.sequenceNumber, 10);
|
|
280
287
|
|
|
281
|
-
this.logger.debug(
|
|
288
|
+
this.logger.debug(`socket,${this._domain}: sequence number: `, sequenceNumber);
|
|
282
289
|
if (this.expectedSequenceNumber && sequenceNumber !== this.expectedSequenceNumber) {
|
|
283
290
|
this.logger.debug(
|
|
284
|
-
`socket: sequence number mismatch indicates lost mercury message. expected: ${this.expectedSequenceNumber}, actual: ${sequenceNumber}`
|
|
291
|
+
`socket,${this._domain}: sequence number mismatch indicates lost mercury message. expected: ${this.expectedSequenceNumber}, actual: ${sequenceNumber}`
|
|
285
292
|
);
|
|
286
293
|
this.emit('sequence-mismatch', sequenceNumber, this.expectedSequenceNumber);
|
|
287
294
|
}
|
|
@@ -303,7 +310,7 @@ export default class Socket extends EventEmitter {
|
|
|
303
310
|
// message from Mercury. At this time, the only action we have is to
|
|
304
311
|
// ignore it and move on.
|
|
305
312
|
/* istanbul ignore next */
|
|
306
|
-
this.logger.warn(
|
|
313
|
+
this.logger.warn(`socket,${this._domain}: error while receiving WebSocket message`, error);
|
|
307
314
|
}
|
|
308
315
|
}
|
|
309
316
|
|
|
@@ -357,7 +364,7 @@ export default class Socket extends EventEmitter {
|
|
|
357
364
|
*/
|
|
358
365
|
_authorize() {
|
|
359
366
|
return new Promise((resolve) => {
|
|
360
|
-
this.logger.info(
|
|
367
|
+
this.logger.info(`socket,${this._domain}: authorizing`);
|
|
361
368
|
this.send({
|
|
362
369
|
id: uuid.v4(),
|
|
363
370
|
type: 'authorization',
|
|
@@ -395,12 +402,18 @@ export default class Socket extends EventEmitter {
|
|
|
395
402
|
if (event.code === 1005 && event.reason) {
|
|
396
403
|
switch (event.reason.toLowerCase()) {
|
|
397
404
|
case 'replaced':
|
|
398
|
-
this.logger.info(
|
|
405
|
+
this.logger.info(
|
|
406
|
+
`socket,${this._domain}: fixing CloseEvent code for reason: `,
|
|
407
|
+
event.reason
|
|
408
|
+
);
|
|
399
409
|
event.code = 4000;
|
|
400
410
|
break;
|
|
401
411
|
case 'authentication failed':
|
|
402
412
|
case 'authentication did not happen within the timeout window of 30000 seconds.':
|
|
403
|
-
this.logger.info(
|
|
413
|
+
this.logger.info(
|
|
414
|
+
`socket,${this._domain}: fixing CloseEvent code for reason: `,
|
|
415
|
+
event.reason
|
|
416
|
+
);
|
|
404
417
|
event.code = 1008;
|
|
405
418
|
break;
|
|
406
419
|
default:
|
|
@@ -420,10 +433,12 @@ export default class Socket extends EventEmitter {
|
|
|
420
433
|
_ping(id) {
|
|
421
434
|
const confirmPongId = (event) => {
|
|
422
435
|
try {
|
|
423
|
-
this.logger.debug(
|
|
436
|
+
this.logger.debug(`socket,${this._domain}: pong`, event.data.id);
|
|
424
437
|
if (event.data && event.data.id !== id) {
|
|
425
|
-
this.logger.info(
|
|
426
|
-
|
|
438
|
+
this.logger.info(
|
|
439
|
+
`socket,${this._domain}: received pong for wrong ping id, closing socket`
|
|
440
|
+
);
|
|
441
|
+
this.logger.debug(`socket,${this._domain}: expected`, id, 'received', event.data.id);
|
|
427
442
|
this.close({
|
|
428
443
|
code: 1000,
|
|
429
444
|
reason: 'Pong mismatch',
|
|
@@ -433,24 +448,29 @@ export default class Socket extends EventEmitter {
|
|
|
433
448
|
// This try/catch block was added as a debugging step; to the best of my
|
|
434
449
|
// knowledge, the above can never throw.
|
|
435
450
|
/* istanbul ignore next */
|
|
436
|
-
this.logger.error(
|
|
451
|
+
this.logger.error(`socket,${this._domain}: error occurred in confirmPongId`, error);
|
|
437
452
|
}
|
|
438
453
|
};
|
|
439
454
|
|
|
440
455
|
const onPongNotReceived = () => {
|
|
441
456
|
try {
|
|
442
|
-
this.logger.info(
|
|
457
|
+
this.logger.info(
|
|
458
|
+
`socket,${this._domain}: pong not receive in expected period, closing socket`
|
|
459
|
+
);
|
|
443
460
|
this.close({
|
|
444
461
|
code: 1000,
|
|
445
462
|
reason: 'Pong not received',
|
|
446
463
|
}).catch((reason) => {
|
|
447
|
-
this.logger.warn(
|
|
464
|
+
this.logger.warn(
|
|
465
|
+
`socket,${this._domain}: failed to close socket after missed pong`,
|
|
466
|
+
reason
|
|
467
|
+
);
|
|
448
468
|
});
|
|
449
469
|
} catch (error) {
|
|
450
470
|
// This try/catch block was added as a debugging step; to the best of my
|
|
451
471
|
// knowledge, the above can never throw.
|
|
452
472
|
/* istanbul ignore next */
|
|
453
|
-
this.logger.error(
|
|
473
|
+
this.logger.error(`socket,${this._domain}: error occurred in onPongNotReceived`, error);
|
|
454
474
|
}
|
|
455
475
|
};
|
|
456
476
|
|
|
@@ -462,16 +482,29 @@ export default class Socket extends EventEmitter {
|
|
|
462
482
|
// This try/catch block was added as a debugging step; to the best of my
|
|
463
483
|
// knowledge, the above can never throw.
|
|
464
484
|
/* istanbul ignore next */
|
|
465
|
-
this.logger.error(
|
|
485
|
+
this.logger.error(
|
|
486
|
+
`socket,${this._domain}: error occurred in scheduleNextPingAndCancelPongTimer`,
|
|
487
|
+
error
|
|
488
|
+
);
|
|
466
489
|
}
|
|
467
490
|
};
|
|
468
491
|
|
|
492
|
+
const calculateLatency = (pingTimestamp) => {
|
|
493
|
+
const now = performance.now();
|
|
494
|
+
const latency = now - pingTimestamp;
|
|
495
|
+
|
|
496
|
+
this.logger.debug(`socket,${this._domain}: latency: `, latency);
|
|
497
|
+
this.emit('ping-pong-latency', latency);
|
|
498
|
+
};
|
|
499
|
+
|
|
469
500
|
id = id || uuid.v4();
|
|
501
|
+
const pingTimestamp = performance.now();
|
|
502
|
+
|
|
470
503
|
this.pongTimer = safeSetTimeout(onPongNotReceived, this.pongTimeout);
|
|
471
504
|
this.once('pong', scheduleNextPingAndCancelPongTimer);
|
|
472
505
|
this.once('pong', confirmPongId);
|
|
473
|
-
|
|
474
|
-
this.logger.debug(`socket: ping ${id}`);
|
|
506
|
+
this.once('pong', () => calculateLatency(pingTimestamp));
|
|
507
|
+
this.logger.debug(`socket,${this._domain}: ping ${id}`);
|
|
475
508
|
|
|
476
509
|
return this.send({
|
|
477
510
|
id,
|
|
@@ -32,12 +32,13 @@ describe('plugin-mercury', function () {
|
|
|
32
32
|
);
|
|
33
33
|
|
|
34
34
|
describe('onBeforeLogout()', () => {
|
|
35
|
-
it('disconnects the web socket', () =>
|
|
35
|
+
it('disconnects the web socket', () => {
|
|
36
36
|
webex.logout({noRedirect: true}).then(() => {
|
|
37
37
|
assert.called(webex.internal.mercury.disconnect);
|
|
38
38
|
assert.isFalse(webex.internal.mercury.connected);
|
|
39
39
|
assert.called(webex.internal.device.unregister);
|
|
40
40
|
assert.isFalse(webex.internal.device.registered);
|
|
41
|
-
})
|
|
41
|
+
});
|
|
42
|
+
});
|
|
42
43
|
});
|
|
43
44
|
});
|