@webex/internal-plugin-mercury 3.0.0-beta.3 → 3.0.0-beta.300
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 +1 -3
- package/dist/config.js +0 -7
- package/dist/config.js.map +1 -1
- package/dist/errors.js +0 -44
- package/dist/errors.js.map +1 -1
- package/dist/index.js +1 -20
- package/dist/index.js.map +1 -1
- package/dist/mercury.js +67 -178
- package/dist/mercury.js.map +1 -1
- package/dist/socket/index.js +0 -4
- package/dist/socket/index.js.map +1 -1
- package/dist/socket/socket-base.js +58 -143
- package/dist/socket/socket-base.js.map +1 -1
- package/dist/socket/socket.js +1 -7
- package/dist/socket/socket.js.map +1 -1
- package/dist/socket/socket.shim.js +2 -7
- package/dist/socket/socket.shim.js.map +1 -1
- package/package.json +14 -14
- package/src/config.js +2 -2
- package/src/errors.js +7 -5
- package/src/index.js +2 -2
- package/src/mercury.js +108 -84
- package/src/socket/socket-base.js +93 -71
- package/src/socket/socket.shim.js +6 -8
- package/test/integration/spec/mercury.js +49 -39
- package/test/integration/spec/sharable-mercury.js +19 -15
- package/test/integration/spec/webex.js +10 -8
- package/test/unit/spec/mercury-events.js +51 -60
- package/test/unit/spec/mercury.js +191 -153
- package/test/unit/spec/socket.js +246 -202
package/src/mercury.js
CHANGED
|
@@ -15,16 +15,11 @@ import {
|
|
|
15
15
|
Forbidden,
|
|
16
16
|
NotAuthorized,
|
|
17
17
|
UnknownResponse,
|
|
18
|
-
ConnectionError
|
|
18
|
+
ConnectionError,
|
|
19
19
|
// NotFound
|
|
20
20
|
} from './errors';
|
|
21
21
|
|
|
22
|
-
const normalReconnectReasons = [
|
|
23
|
-
'idle',
|
|
24
|
-
'done (forced)',
|
|
25
|
-
'pong not received',
|
|
26
|
-
'pong mismatch'
|
|
27
|
-
];
|
|
22
|
+
const normalReconnectReasons = ['idle', 'done (forced)', 'pong not received', 'pong mismatch'];
|
|
28
23
|
|
|
29
24
|
const Mercury = WebexPlugin.extend({
|
|
30
25
|
namespace: 'Mercury',
|
|
@@ -32,14 +27,14 @@ const Mercury = WebexPlugin.extend({
|
|
|
32
27
|
session: {
|
|
33
28
|
connected: {
|
|
34
29
|
default: false,
|
|
35
|
-
type: 'boolean'
|
|
30
|
+
type: 'boolean',
|
|
36
31
|
},
|
|
37
32
|
connecting: {
|
|
38
33
|
default: false,
|
|
39
|
-
type: 'boolean'
|
|
34
|
+
type: 'boolean',
|
|
40
35
|
},
|
|
41
36
|
socket: 'object',
|
|
42
|
-
localClusterServiceUrls: 'object'
|
|
37
|
+
localClusterServiceUrls: 'object',
|
|
43
38
|
},
|
|
44
39
|
|
|
45
40
|
derived: {
|
|
@@ -47,42 +42,41 @@ const Mercury = WebexPlugin.extend({
|
|
|
47
42
|
deps: ['connected'],
|
|
48
43
|
fn() {
|
|
49
44
|
return this.connected;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
45
|
+
},
|
|
46
|
+
},
|
|
52
47
|
},
|
|
53
48
|
|
|
54
49
|
@oneFlight
|
|
55
50
|
connect(webSocketUrl) {
|
|
56
51
|
if (this.connected) {
|
|
57
|
-
this.logger.info(
|
|
52
|
+
this.logger.info(`${this.namespace}: already connected, will not connect again`);
|
|
58
53
|
|
|
59
54
|
return Promise.resolve();
|
|
60
55
|
}
|
|
61
56
|
|
|
62
57
|
this.connecting = true;
|
|
63
58
|
|
|
64
|
-
return Promise.resolve(
|
|
65
|
-
.
|
|
66
|
-
|
|
59
|
+
return Promise.resolve(
|
|
60
|
+
this.webex.internal.device.registered || this.webex.internal.device.register()
|
|
61
|
+
).then(() => {
|
|
62
|
+
this.logger.info(`${this.namespace}: connecting`);
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
return this._connectWithBackoff(webSocketUrl);
|
|
65
|
+
});
|
|
70
66
|
},
|
|
71
67
|
|
|
72
68
|
@oneFlight
|
|
73
69
|
disconnect() {
|
|
74
70
|
return new Promise((resolve) => {
|
|
75
71
|
if (this.backoffCall) {
|
|
76
|
-
this.logger.info(
|
|
72
|
+
this.logger.info(`${this.namespace}: aborting connection`);
|
|
77
73
|
this.backoffCall.abort();
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
if (this.socket) {
|
|
81
77
|
this.socket.removeAllListeners('message');
|
|
82
78
|
this.once('offline', resolve);
|
|
83
|
-
this.socket.close();
|
|
84
|
-
|
|
85
|
-
return;
|
|
79
|
+
resolve(this.socket.close());
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
resolve();
|
|
@@ -121,7 +115,8 @@ const Mercury = WebexPlugin.extend({
|
|
|
121
115
|
webSocketUrl = this.webex.internal.device.webSocketUrl;
|
|
122
116
|
}
|
|
123
117
|
|
|
124
|
-
return this.webex.internal.feature
|
|
118
|
+
return this.webex.internal.feature
|
|
119
|
+
.getFeature('developer', 'web-high-availability')
|
|
125
120
|
.then((haMessagingEnabled) => {
|
|
126
121
|
if (haMessagingEnabled) {
|
|
127
122
|
return this.webex.internal.services.convertUrlToPriorityHostUrl(webSocketUrl);
|
|
@@ -138,13 +133,13 @@ const Mercury = WebexPlugin.extend({
|
|
|
138
133
|
Object.assign(webSocketUrl.query, {
|
|
139
134
|
outboundWireFormat: 'text',
|
|
140
135
|
bufferStates: true,
|
|
141
|
-
aliasHttpStatus: true
|
|
136
|
+
aliasHttpStatus: true,
|
|
142
137
|
});
|
|
143
138
|
|
|
144
139
|
if (webSharedMercury) {
|
|
145
140
|
Object.assign(webSocketUrl.query, {
|
|
146
141
|
mercuryRegistrationStatus: true,
|
|
147
|
-
isRegistrationRefreshEnabled: true
|
|
142
|
+
isRegistrationRefreshEnabled: true,
|
|
148
143
|
});
|
|
149
144
|
Reflect.deleteProperty(webSocketUrl.query, 'bufferStates');
|
|
150
145
|
}
|
|
@@ -168,7 +163,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
168
163
|
Promise.all([this._prepareUrl(socketUrl), this.webex.credentials.getUserToken()])
|
|
169
164
|
.then(([webSocketUrl, token]) => {
|
|
170
165
|
if (!this.backoffCall) {
|
|
171
|
-
const msg =
|
|
166
|
+
const msg = `${this.namespace}: prevent socket open when backoffCall no longer defined`;
|
|
172
167
|
|
|
173
168
|
this.logger.info(msg);
|
|
174
169
|
|
|
@@ -183,12 +178,12 @@ const Mercury = WebexPlugin.extend({
|
|
|
183
178
|
pongTimeout: this.config.pongTimeout,
|
|
184
179
|
token: token.toString(),
|
|
185
180
|
trackingId: `${this.webex.sessionId}_${Date.now()}`,
|
|
186
|
-
logger: this.logger
|
|
181
|
+
logger: this.logger,
|
|
187
182
|
};
|
|
188
183
|
|
|
189
184
|
// if the consumer has supplied request options use them
|
|
190
185
|
if (this.webex.config.defaultMercuryOptions) {
|
|
191
|
-
this.logger.info(
|
|
186
|
+
this.logger.info(`${this.namespace}: setting custom options`);
|
|
192
187
|
options = {...options, ...this.webex.config.defaultMercuryOptions};
|
|
193
188
|
}
|
|
194
189
|
|
|
@@ -196,21 +191,25 @@ const Mercury = WebexPlugin.extend({
|
|
|
196
191
|
// the socket if it is in the process of being opened.
|
|
197
192
|
this.socket = socket;
|
|
198
193
|
|
|
194
|
+
this.logger.info(`${this.namespace} connection url: ${webSocketUrl}`);
|
|
195
|
+
|
|
199
196
|
return socket.open(webSocketUrl, options);
|
|
200
197
|
})
|
|
201
198
|
.then(() => {
|
|
202
199
|
this.webex.internal.metrics.submitClientMetrics('web-ha-mercury', {
|
|
203
200
|
fields: {
|
|
204
|
-
success: true
|
|
201
|
+
success: true,
|
|
205
202
|
},
|
|
206
203
|
tags: {
|
|
204
|
+
namespace: this.namespace,
|
|
207
205
|
action: 'connected',
|
|
208
|
-
url: attemptWSUrl
|
|
209
|
-
}
|
|
206
|
+
url: attemptWSUrl,
|
|
207
|
+
},
|
|
210
208
|
});
|
|
211
209
|
callback();
|
|
212
210
|
|
|
213
|
-
return this.webex.internal.feature
|
|
211
|
+
return this.webex.internal.feature
|
|
212
|
+
.getFeature('developer', 'web-high-availability')
|
|
214
213
|
.then((haMessagingEnabled) => {
|
|
215
214
|
if (haMessagingEnabled) {
|
|
216
215
|
return this.webex.internal.device.refresh();
|
|
@@ -227,21 +226,21 @@ const Mercury = WebexPlugin.extend({
|
|
|
227
226
|
if (reason.code !== 1006 && this.backoffCall && this.backoffCall.getNumRetries() > 0) {
|
|
228
227
|
this._emit('connection_failed', reason, {retries: this.backoffCall.getNumRetries()});
|
|
229
228
|
}
|
|
230
|
-
this.logger.info(
|
|
229
|
+
this.logger.info(`${this.namespace}: connection attempt failed`, reason);
|
|
231
230
|
// UnknownResponse is produced by IE for any 4XXX; treated it like a bad
|
|
232
231
|
// web socket url and let WDM handle the token checking
|
|
233
232
|
if (reason instanceof UnknownResponse) {
|
|
234
|
-
this.logger.info(
|
|
233
|
+
this.logger.info(
|
|
234
|
+
`${this.namespace}: received unknown response code, refreshing device registration`
|
|
235
|
+
);
|
|
235
236
|
|
|
236
|
-
return this.webex.internal.device.refresh()
|
|
237
|
-
.then(() => callback(reason));
|
|
237
|
+
return this.webex.internal.device.refresh().then(() => callback(reason));
|
|
238
238
|
}
|
|
239
239
|
// NotAuthorized implies expired token
|
|
240
240
|
if (reason instanceof NotAuthorized) {
|
|
241
|
-
this.logger.info(
|
|
241
|
+
this.logger.info(`${this.namespace}: received authorization error, reauthorizing`);
|
|
242
242
|
|
|
243
|
-
return this.webex.credentials.refresh({force: true})
|
|
244
|
-
.then(() => callback(reason));
|
|
243
|
+
return this.webex.credentials.refresh({force: true}).then(() => callback(reason));
|
|
245
244
|
}
|
|
246
245
|
// // NotFound implies expired web socket url
|
|
247
246
|
// else if (reason instanceof NotFound) {
|
|
@@ -252,25 +251,29 @@ const Mercury = WebexPlugin.extend({
|
|
|
252
251
|
// BadRequest implies current credentials are for a Service Account
|
|
253
252
|
// Forbidden implies current user is not entitle for Webex
|
|
254
253
|
if (reason instanceof BadRequest || reason instanceof Forbidden) {
|
|
255
|
-
this.logger.warn(
|
|
254
|
+
this.logger.warn(`${this.namespace}: received unrecoverable response from mercury`);
|
|
256
255
|
this.backoffCall.abort();
|
|
257
256
|
|
|
258
257
|
return callback(reason);
|
|
259
258
|
}
|
|
260
259
|
if (reason instanceof ConnectionError) {
|
|
261
|
-
return this.webex.internal.feature
|
|
260
|
+
return this.webex.internal.feature
|
|
261
|
+
.getFeature('developer', 'web-high-availability')
|
|
262
262
|
.then((haMessagingEnabled) => {
|
|
263
263
|
if (haMessagingEnabled) {
|
|
264
|
-
this.logger.info(
|
|
264
|
+
this.logger.info(
|
|
265
|
+
`${this.namespace}: received a generic connection error, will try to connect to another datacenter`
|
|
266
|
+
);
|
|
265
267
|
this.webex.internal.metrics.submitClientMetrics('web-ha-mercury', {
|
|
266
268
|
fields: {
|
|
267
|
-
success: false
|
|
269
|
+
success: false,
|
|
268
270
|
},
|
|
269
271
|
tags: {
|
|
272
|
+
namespace: this.namespace,
|
|
270
273
|
action: 'failed',
|
|
271
274
|
error: reason.message,
|
|
272
|
-
url: attemptWSUrl
|
|
273
|
-
}
|
|
275
|
+
url: attemptWSUrl,
|
|
276
|
+
},
|
|
274
277
|
});
|
|
275
278
|
|
|
276
279
|
return this.webex.internal.services.markFailedUrl(attemptWSUrl);
|
|
@@ -284,7 +287,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
284
287
|
return callback(reason);
|
|
285
288
|
})
|
|
286
289
|
.catch((reason) => {
|
|
287
|
-
this.logger.error(
|
|
290
|
+
this.logger.error(`${this.namespace}: failed to handle connection failure`, reason);
|
|
288
291
|
callback(reason);
|
|
289
292
|
});
|
|
290
293
|
},
|
|
@@ -299,7 +302,11 @@ const Mercury = WebexPlugin.extend({
|
|
|
299
302
|
|
|
300
303
|
this.backoffCall = undefined;
|
|
301
304
|
if (err) {
|
|
302
|
-
this.logger.info(
|
|
305
|
+
this.logger.info(
|
|
306
|
+
`${
|
|
307
|
+
this.namespace
|
|
308
|
+
}: failed to connect after ${call.getNumRetries()} retries; log statement about next retry was inaccurate; ${err}`
|
|
309
|
+
);
|
|
303
310
|
|
|
304
311
|
return reject(err);
|
|
305
312
|
}
|
|
@@ -311,21 +318,23 @@ const Mercury = WebexPlugin.extend({
|
|
|
311
318
|
|
|
312
319
|
// eslint-disable-next-line prefer-reflect
|
|
313
320
|
call = backoff.call((callback) => {
|
|
314
|
-
this.logger.info(
|
|
321
|
+
this.logger.info(`${this.namespace}: executing connection attempt ${call.getNumRetries()}`);
|
|
315
322
|
this._attemptConnection(webSocketUrl, callback);
|
|
316
323
|
}, onComplete);
|
|
317
324
|
|
|
318
|
-
call.setStrategy(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
325
|
+
call.setStrategy(
|
|
326
|
+
new backoff.ExponentialStrategy({
|
|
327
|
+
initialDelay: this.config.backoffTimeReset,
|
|
328
|
+
maxDelay: this.config.backoffTimeMax,
|
|
329
|
+
})
|
|
330
|
+
);
|
|
322
331
|
|
|
323
332
|
if (this.config.maxRetries) {
|
|
324
333
|
call.failAfter(this.config.maxRetries);
|
|
325
334
|
}
|
|
326
335
|
|
|
327
336
|
call.on('abort', () => {
|
|
328
|
-
this.logger.info(
|
|
337
|
+
this.logger.info(`${this.namespace}: connection aborted`);
|
|
329
338
|
reject(new Error('Mercury Connection Aborted'));
|
|
330
339
|
});
|
|
331
340
|
|
|
@@ -334,15 +343,17 @@ const Mercury = WebexPlugin.extend({
|
|
|
334
343
|
const number = call.getNumRetries();
|
|
335
344
|
const delay = Math.min(call.strategy_.nextBackoffDelay_, this.config.backoffTimeMax);
|
|
336
345
|
|
|
337
|
-
this.logger.info(
|
|
346
|
+
this.logger.info(
|
|
347
|
+
`${this.namespace}: failed to connect; attempting retry ${number + 1} in ${delay} ms`
|
|
348
|
+
);
|
|
338
349
|
/* istanbul ignore if */
|
|
339
350
|
if (process.env.NODE_ENV === 'development') {
|
|
340
|
-
this.logger.debug(
|
|
351
|
+
this.logger.debug(`${this.namespace}: `, err, err.stack);
|
|
341
352
|
}
|
|
342
353
|
|
|
343
354
|
return;
|
|
344
355
|
}
|
|
345
|
-
this.logger.info(
|
|
356
|
+
this.logger.info(`${this.namespace}: connected`);
|
|
346
357
|
});
|
|
347
358
|
|
|
348
359
|
call.start();
|
|
@@ -354,9 +365,11 @@ const Mercury = WebexPlugin.extend({
|
|
|
354
365
|
_emit(...args) {
|
|
355
366
|
try {
|
|
356
367
|
this.trigger(...args);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
368
|
+
} catch (error) {
|
|
369
|
+
this.logger.error(`${this.namespace}: error occurred in event handler`, {
|
|
370
|
+
error,
|
|
371
|
+
arguments: args,
|
|
372
|
+
});
|
|
360
373
|
}
|
|
361
374
|
},
|
|
362
375
|
|
|
@@ -373,7 +386,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
373
386
|
if ((this.webex[namespace] || this.webex.internal[namespace])[handlerName]) {
|
|
374
387
|
handlers.push({
|
|
375
388
|
name: handlerName,
|
|
376
|
-
namespace
|
|
389
|
+
namespace,
|
|
377
390
|
});
|
|
378
391
|
}
|
|
379
392
|
|
|
@@ -395,20 +408,22 @@ const Mercury = WebexPlugin.extend({
|
|
|
395
408
|
|
|
396
409
|
switch (event.code) {
|
|
397
410
|
case 1003:
|
|
398
|
-
|
|
399
|
-
this.logger.info(
|
|
411
|
+
// metric: disconnect
|
|
412
|
+
this.logger.info(
|
|
413
|
+
`${this.namespace}: Mercury service rejected last message; will not reconnect: ${event.reason}`
|
|
414
|
+
);
|
|
400
415
|
this._emit('offline.permanent', event);
|
|
401
416
|
break;
|
|
402
417
|
case 4000:
|
|
403
418
|
// metric: disconnect
|
|
404
|
-
this.logger.info(
|
|
419
|
+
this.logger.info(`${this.namespace}: socket replaced; will not reconnect`);
|
|
405
420
|
this._emit('offline.replaced', event);
|
|
406
421
|
break;
|
|
407
422
|
case 1001:
|
|
408
423
|
case 1005:
|
|
409
424
|
case 1006:
|
|
410
425
|
case 1011:
|
|
411
|
-
this.logger.info(
|
|
426
|
+
this.logger.info(`${this.namespace}: socket disconnected; reconnecting`);
|
|
412
427
|
this._emit('offline.transient', event);
|
|
413
428
|
this._reconnect(socketUrl);
|
|
414
429
|
// metric: disconnect
|
|
@@ -416,25 +431,25 @@ const Mercury = WebexPlugin.extend({
|
|
|
416
431
|
break;
|
|
417
432
|
case 1000:
|
|
418
433
|
if (normalReconnectReasons.includes(reason)) {
|
|
419
|
-
this.logger.info(
|
|
434
|
+
this.logger.info(`${this.namespace}: socket disconnected; reconnecting`);
|
|
420
435
|
this._emit('offline.transient', event);
|
|
421
436
|
this._reconnect(socketUrl);
|
|
422
437
|
// metric: disconnect
|
|
423
438
|
// if (reason === done forced) metric: force closure
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
this.logger.info('mercury: socket disconnected; will not reconnect');
|
|
439
|
+
} else {
|
|
440
|
+
this.logger.info(`${this.namespace}: socket disconnected; will not reconnect`);
|
|
427
441
|
this._emit('offline.permanent', event);
|
|
428
442
|
}
|
|
429
443
|
break;
|
|
430
444
|
default:
|
|
431
|
-
this.logger.info(
|
|
445
|
+
this.logger.info(
|
|
446
|
+
`${this.namespace}: socket disconnected unexpectedly; will not reconnect`
|
|
447
|
+
);
|
|
432
448
|
// unexpected disconnect
|
|
433
449
|
this._emit('offline.permanent', event);
|
|
434
450
|
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
this.logger.error('mercury: error occurred in close handler', error);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
this.logger.error(`${this.namespace}: error occurred in close handler`, error);
|
|
438
453
|
}
|
|
439
454
|
},
|
|
440
455
|
|
|
@@ -442,7 +457,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
442
457
|
const envelope = event.data;
|
|
443
458
|
|
|
444
459
|
if (process.env.ENABLE_MERCURY_LOGGING) {
|
|
445
|
-
this.logger.debug(
|
|
460
|
+
this.logger.debug(`${this.namespace}: message envelope: `, envelope);
|
|
446
461
|
}
|
|
447
462
|
|
|
448
463
|
const {data} = envelope;
|
|
@@ -450,34 +465,43 @@ const Mercury = WebexPlugin.extend({
|
|
|
450
465
|
this._applyOverrides(data);
|
|
451
466
|
|
|
452
467
|
return this._getEventHandlers(data.eventType)
|
|
453
|
-
.reduce(
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
468
|
+
.reduce(
|
|
469
|
+
(promise, handler) =>
|
|
470
|
+
promise.then(() => {
|
|
471
|
+
const {namespace, name} = handler;
|
|
472
|
+
|
|
473
|
+
return new Promise((resolve) =>
|
|
474
|
+
resolve((this.webex[namespace] || this.webex.internal[namespace])[name](data))
|
|
475
|
+
).catch((reason) =>
|
|
476
|
+
this.logger.error(
|
|
477
|
+
`${this.namespace}: error occurred in autowired event handler for ${data.eventType}`,
|
|
478
|
+
reason
|
|
479
|
+
)
|
|
480
|
+
);
|
|
481
|
+
}),
|
|
482
|
+
Promise.resolve()
|
|
483
|
+
)
|
|
459
484
|
.then(() => {
|
|
460
485
|
this._emit('event', event.data);
|
|
461
486
|
const [namespace] = data.eventType.split('.');
|
|
462
487
|
|
|
463
488
|
if (namespace === data.eventType) {
|
|
464
489
|
this._emit(`event:${namespace}`, envelope);
|
|
465
|
-
}
|
|
466
|
-
else {
|
|
490
|
+
} else {
|
|
467
491
|
this._emit(`event:${namespace}`, envelope);
|
|
468
492
|
this._emit(`event:${data.eventType}`, envelope);
|
|
469
493
|
}
|
|
470
494
|
})
|
|
471
495
|
.catch((reason) => {
|
|
472
|
-
this.logger.error(
|
|
496
|
+
this.logger.error(`${this.namespace}: error occurred processing socket message`, reason);
|
|
473
497
|
});
|
|
474
498
|
},
|
|
475
499
|
|
|
476
500
|
_reconnect(webSocketUrl) {
|
|
477
|
-
this.logger.info(
|
|
501
|
+
this.logger.info(`${this.namespace}: reconnecting`);
|
|
478
502
|
|
|
479
503
|
return this.connect(webSocketUrl);
|
|
480
|
-
}
|
|
504
|
+
},
|
|
481
505
|
});
|
|
482
506
|
|
|
483
507
|
export default Mercury;
|