@webex/internal-plugin-mercury 3.12.0-auth-prejoin-fetch.1 → 3.12.0-llmrefactor.10
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/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/mercury-plugin.js +125 -0
- package/dist/mercury-plugin.js.map +1 -0
- package/dist/mercury.js +241 -419
- package/dist/mercury.js.map +1 -1
- package/package.json +17 -17
- package/src/index.js +3 -2
- package/src/mercury-plugin.ts +79 -0
- package/src/mercury.js +198 -404
- package/test/unit/spec/mercury-events.js +2 -5
- package/test/unit/spec/mercury-plugin.js +280 -0
- package/test/unit/spec/mercury.js +233 -282
package/src/mercury.js
CHANGED
|
@@ -26,7 +26,6 @@ const MERCURY_CLOSE_4000_METRIC = 'JS_SDK_MERCURY_CLOSE_4000';
|
|
|
26
26
|
const Mercury = WebexPlugin.extend({
|
|
27
27
|
namespace: 'Mercury',
|
|
28
28
|
lastError: undefined,
|
|
29
|
-
defaultSessionId: 'mercury-default-session',
|
|
30
29
|
|
|
31
30
|
session: {
|
|
32
31
|
connected: {
|
|
@@ -41,27 +40,19 @@ const Mercury = WebexPlugin.extend({
|
|
|
41
40
|
default: false,
|
|
42
41
|
type: 'boolean',
|
|
43
42
|
},
|
|
44
|
-
|
|
45
|
-
default: () => new Map(),
|
|
46
|
-
type: 'object',
|
|
47
|
-
},
|
|
48
|
-
sessionWebSocketUrls: {
|
|
49
|
-
default: () => new Map(),
|
|
50
|
-
type: 'object',
|
|
51
|
-
},
|
|
52
|
-
backoffCalls: {
|
|
53
|
-
default: () => new Map(),
|
|
54
|
-
type: 'object',
|
|
55
|
-
},
|
|
56
|
-
_shutdownSwitchoverBackoffCalls: {
|
|
57
|
-
default: () => new Map(),
|
|
58
|
-
type: 'object',
|
|
59
|
-
},
|
|
43
|
+
socket: 'object',
|
|
60
44
|
localClusterServiceUrls: 'object',
|
|
61
45
|
mercuryTimeOffset: {
|
|
62
46
|
default: undefined,
|
|
63
47
|
type: 'number',
|
|
64
48
|
},
|
|
49
|
+
backoffCall: 'object',
|
|
50
|
+
_connectPromise: 'object',
|
|
51
|
+
_shutdownSwitchoverBackoffCall: 'object',
|
|
52
|
+
_shutdownSwitchoverId: 'string',
|
|
53
|
+
// Store the resolved (pre-proxy) URL so reconnection uses a catalog-valid host
|
|
54
|
+
// instead of the potentially rewritten socket.url from an interceptor.
|
|
55
|
+
resolvedWebSocketUrl: 'string',
|
|
65
56
|
},
|
|
66
57
|
|
|
67
58
|
derived: {
|
|
@@ -116,63 +107,50 @@ const Mercury = WebexPlugin.extend({
|
|
|
116
107
|
/**
|
|
117
108
|
* Attach event listeners to a socket.
|
|
118
109
|
* @param {Socket} socket - The socket to attach listeners to
|
|
119
|
-
* @param {sessionId} sessionId - The socket related session ID
|
|
120
110
|
* @returns {void}
|
|
121
111
|
*/
|
|
122
|
-
_attachSocketEventListeners(socket
|
|
123
|
-
socket.on('close', (event) => this._onclose(
|
|
124
|
-
socket.on('message', (...args) => this._onmessage(
|
|
125
|
-
socket.on('pong', (...args) => this._setTimeOffset(
|
|
126
|
-
socket.on('sequence-mismatch', (...args) =>
|
|
127
|
-
|
|
128
|
-
);
|
|
129
|
-
socket.on('ping-pong-latency', (...args) =>
|
|
130
|
-
this._emit(sessionId, 'ping-pong-latency', ...args)
|
|
131
|
-
);
|
|
112
|
+
_attachSocketEventListeners(socket) {
|
|
113
|
+
socket.on('close', (event) => this._onclose(event, socket));
|
|
114
|
+
socket.on('message', (...args) => this._onmessage(...args));
|
|
115
|
+
socket.on('pong', (...args) => this._setTimeOffset(...args));
|
|
116
|
+
socket.on('sequence-mismatch', (...args) => this._emit('sequence-mismatch', ...args));
|
|
117
|
+
socket.on('ping-pong-latency', (...args) => this._emit('ping-pong-latency', ...args));
|
|
132
118
|
},
|
|
133
119
|
|
|
134
120
|
/**
|
|
135
121
|
* Handle imminent shutdown by establishing a new connection while keeping
|
|
136
122
|
* the current one alive (make-before-break).
|
|
137
123
|
* Idempotent: will no-op if already in progress.
|
|
138
|
-
* @param {string} sessionId - The session ID for which the shutdown is imminent
|
|
139
124
|
* @returns {void}
|
|
140
125
|
*/
|
|
141
|
-
_handleImminentShutdown(
|
|
142
|
-
const oldSocket = this.
|
|
126
|
+
_handleImminentShutdown() {
|
|
127
|
+
const oldSocket = this.socket;
|
|
143
128
|
|
|
144
129
|
try {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (this._shutdownSwitchoverBackoffCalls.get(sessionId)) {
|
|
148
|
-
this.logger.info(
|
|
149
|
-
`${this.namespace}: [shutdown] switchover already in progress for ${sessionId}`
|
|
150
|
-
);
|
|
130
|
+
if (this._shutdownSwitchoverBackoffCall) {
|
|
131
|
+
this.logger.info(`${this.namespace}: [shutdown] switchover already in progress`);
|
|
151
132
|
|
|
152
133
|
return;
|
|
153
134
|
}
|
|
154
|
-
|
|
155
135
|
this._shutdownSwitchoverId = `${Date.now()}`;
|
|
156
136
|
this.logger.info(
|
|
157
|
-
`${this.namespace}: [shutdown] switchover start, id=${this._shutdownSwitchoverId}
|
|
137
|
+
`${this.namespace}: [shutdown] switchover start, id=${this._shutdownSwitchoverId}`
|
|
158
138
|
);
|
|
159
139
|
|
|
160
|
-
this._connectWithBackoff(undefined,
|
|
140
|
+
this._connectWithBackoff(undefined, {
|
|
161
141
|
isShutdownSwitchover: true,
|
|
162
142
|
attemptOptions: {
|
|
163
143
|
isShutdownSwitchover: true,
|
|
164
144
|
onSuccess: (newSocket, webSocketUrl) => {
|
|
165
145
|
this.logger.info(
|
|
166
|
-
`${this.namespace}: [shutdown] switchover connected, url: ${webSocketUrl}
|
|
146
|
+
`${this.namespace}: [shutdown] switchover connected, url: ${webSocketUrl}`
|
|
167
147
|
);
|
|
168
148
|
|
|
169
149
|
// Atomically switch active socket reference
|
|
170
|
-
this.socket =
|
|
171
|
-
this.connected =
|
|
150
|
+
this.socket = newSocket;
|
|
151
|
+
this.connected = true; // remain connected throughout
|
|
172
152
|
|
|
173
|
-
this._emit(
|
|
174
|
-
url: webSocketUrl,
|
|
175
|
-
});
|
|
153
|
+
this._emit('event:mercury_shutdown_switchover_complete', {url: webSocketUrl});
|
|
176
154
|
|
|
177
155
|
if (oldSocket) {
|
|
178
156
|
this.logger.info(
|
|
@@ -183,25 +161,20 @@ const Mercury = WebexPlugin.extend({
|
|
|
183
161
|
},
|
|
184
162
|
})
|
|
185
163
|
.then(() => {
|
|
186
|
-
this.logger.info(
|
|
187
|
-
`${this.namespace}: [shutdown] switchover completed successfully for ${sessionId}`
|
|
188
|
-
);
|
|
164
|
+
this.logger.info(`${this.namespace}: [shutdown] switchover completed successfully`);
|
|
189
165
|
})
|
|
190
166
|
.catch((err) => {
|
|
191
167
|
this.logger.info(
|
|
192
|
-
`${this.namespace}: [shutdown] switchover exhausted retries; will fall back to normal reconnection
|
|
168
|
+
`${this.namespace}: [shutdown] switchover exhausted retries; will fall back to normal reconnection`,
|
|
193
169
|
err
|
|
194
170
|
);
|
|
195
|
-
this._emit(
|
|
171
|
+
this._emit('event:mercury_shutdown_switchover_failed', {reason: err});
|
|
196
172
|
// Old socket will eventually close with 4001, triggering normal reconnection
|
|
197
173
|
});
|
|
198
174
|
} catch (e) {
|
|
199
|
-
this.logger.error(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
);
|
|
203
|
-
this._shutdownSwitchoverBackoffCalls.delete(sessionId);
|
|
204
|
-
this._emit(sessionId, 'event:mercury_shutdown_switchover_failed', {reason: e});
|
|
175
|
+
this.logger.error(`${this.namespace}: [shutdown] error during switchover`, e);
|
|
176
|
+
this._shutdownSwitchoverBackoffCall = undefined;
|
|
177
|
+
this._emit('event:mercury_shutdown_switchover_failed', {reason: e});
|
|
205
178
|
}
|
|
206
179
|
},
|
|
207
180
|
|
|
@@ -214,118 +187,49 @@ const Mercury = WebexPlugin.extend({
|
|
|
214
187
|
},
|
|
215
188
|
|
|
216
189
|
/**
|
|
217
|
-
* Get
|
|
218
|
-
* @returns {
|
|
190
|
+
* Get the underlying WebSocket.
|
|
191
|
+
* @returns {any} The socket instance, or undefined if not connected.
|
|
219
192
|
*/
|
|
220
|
-
|
|
221
|
-
return this.
|
|
193
|
+
getSocket() {
|
|
194
|
+
return this.socket;
|
|
222
195
|
},
|
|
223
196
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
* @returns {Socket|undefined} The socket instance or undefined if not found
|
|
228
|
-
*/
|
|
229
|
-
getSocket(sessionId = this.defaultSessionId) {
|
|
230
|
-
return this.sockets.get(sessionId);
|
|
231
|
-
},
|
|
197
|
+
connect(webSocketUrl) {
|
|
198
|
+
if (this.connected) {
|
|
199
|
+
this.logger.info(`${this.namespace}: already connected, will not connect again`);
|
|
232
200
|
|
|
233
|
-
|
|
234
|
-
* Check if a socket is connected
|
|
235
|
-
* @param {string} [sessionId=this.defaultSessionId] - The session identifier
|
|
236
|
-
* @returns {boolean|undefined} True if the socket is connected
|
|
237
|
-
*/
|
|
238
|
-
hasConnectedSockets(sessionId = this.defaultSessionId) {
|
|
239
|
-
const socket = this.sockets.get(sessionId || this.defaultSessionId);
|
|
240
|
-
|
|
241
|
-
return socket?.connected;
|
|
242
|
-
},
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Check if any sockets are connecting
|
|
246
|
-
* @param {string} [sessionId=this.defaultSessionId] - The session identifier
|
|
247
|
-
* @returns {boolean|undefined} True if the socket is connecting
|
|
248
|
-
*/
|
|
249
|
-
hasConnectingSockets(sessionId = this.defaultSessionId) {
|
|
250
|
-
const socket = this.sockets.get(sessionId || this.defaultSessionId);
|
|
251
|
-
|
|
252
|
-
return socket?.connecting;
|
|
253
|
-
},
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Connect to Mercury for a specific session.
|
|
257
|
-
* @param {string} [webSocketUrl] - Optional websocket URL override. Falls back to the device websocket URL.
|
|
258
|
-
* @param {string} [sessionId=this.defaultSessionId] - The session identifier for this connection.
|
|
259
|
-
* @returns {Promise<void>} Resolves when connection flow completes for the session.
|
|
260
|
-
*/
|
|
261
|
-
connect(webSocketUrl, sessionId = this.defaultSessionId) {
|
|
262
|
-
if (!this._connectPromises) this._connectPromises = new Map();
|
|
263
|
-
|
|
264
|
-
// First check if there's already a connection promise for this session
|
|
265
|
-
if (this._connectPromises.has(sessionId)) {
|
|
266
|
-
this.logger.info(
|
|
267
|
-
`${this.namespace}: connection ${sessionId} already in progress, returning existing promise`
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
return this._connectPromises.get(sessionId);
|
|
201
|
+
return Promise.resolve();
|
|
271
202
|
}
|
|
272
203
|
|
|
273
|
-
|
|
274
|
-
if (
|
|
204
|
+
// If already connecting, return the existing promise so callers can await it
|
|
205
|
+
if (this._connectPromise) {
|
|
275
206
|
this.logger.info(
|
|
276
|
-
`${this.namespace}: connection
|
|
207
|
+
`${this.namespace}: connection already in progress, returning existing promise`
|
|
277
208
|
);
|
|
278
209
|
|
|
279
|
-
return
|
|
210
|
+
return this._connectPromise;
|
|
280
211
|
}
|
|
281
212
|
|
|
282
213
|
this.connecting = true;
|
|
283
214
|
|
|
284
|
-
this.logger.info(`${this.namespace}: starting connection attempt
|
|
285
|
-
this.logger.info(
|
|
286
|
-
`${this.namespace}: debug_mercury_logging stack: `,
|
|
287
|
-
new Error('debug_mercury_logging').stack
|
|
288
|
-
);
|
|
215
|
+
this.logger.info(`${this.namespace}: starting connection attempt`);
|
|
289
216
|
|
|
290
|
-
|
|
217
|
+
this._connectPromise = Promise.resolve(
|
|
291
218
|
this.webex.internal.device.registered || this.webex.internal.device.register()
|
|
292
219
|
)
|
|
293
220
|
.then(() => {
|
|
294
|
-
this.logger.info(`${this.namespace}: connecting
|
|
221
|
+
this.logger.info(`${this.namespace}: connecting`);
|
|
295
222
|
|
|
296
|
-
return this._connectWithBackoff(webSocketUrl
|
|
223
|
+
return this._connectWithBackoff(webSocketUrl);
|
|
297
224
|
})
|
|
298
225
|
.finally(() => {
|
|
299
|
-
this.
|
|
226
|
+
this._connectPromise = null;
|
|
300
227
|
});
|
|
301
228
|
|
|
302
|
-
this.
|
|
303
|
-
|
|
304
|
-
return connectPromise;
|
|
305
|
-
},
|
|
306
|
-
|
|
307
|
-
logout() {
|
|
308
|
-
this.logger.info(`${this.namespace}: logout() called`);
|
|
309
|
-
this.logger.info(
|
|
310
|
-
`${this.namespace}: debug_mercury_logging stack: `,
|
|
311
|
-
new Error('debug_mercury_logging').stack
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
return this.disconnectAll(
|
|
315
|
-
this.config.beforeLogoutOptionsCloseReason &&
|
|
316
|
-
!normalReconnectReasons.includes(this.config.beforeLogoutOptionsCloseReason)
|
|
317
|
-
? {code: 3050, reason: this.config.beforeLogoutOptionsCloseReason}
|
|
318
|
-
: undefined
|
|
319
|
-
);
|
|
229
|
+
return this._connectPromise;
|
|
320
230
|
},
|
|
321
231
|
|
|
322
|
-
|
|
323
|
-
* Disconnect a Mercury socket for a specific session.
|
|
324
|
-
* @param {object} [options] - Optional websocket close options (for example: `{code, reason}`).
|
|
325
|
-
* @param {string} [sessionId=this.defaultSessionId] - The session identifier to disconnect.
|
|
326
|
-
* @returns {Promise<void>} Resolves after disconnect cleanup and close handling are initiated/completed.
|
|
327
|
-
*/
|
|
328
|
-
disconnect(options, sessionId = this.defaultSessionId) {
|
|
232
|
+
disconnect(options) {
|
|
329
233
|
this.logger.info(
|
|
330
234
|
`${this.namespace}#disconnect: connecting state: ${this.connecting}, connected state: ${
|
|
331
235
|
this.connected
|
|
@@ -333,61 +237,28 @@ const Mercury = WebexPlugin.extend({
|
|
|
333
237
|
);
|
|
334
238
|
|
|
335
239
|
return new Promise((resolve) => {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
this.
|
|
339
|
-
backoffCall.abort();
|
|
340
|
-
this.backoffCalls.delete(sessionId);
|
|
341
|
-
}
|
|
342
|
-
const shutdownSwitchoverBackoffCall = this._shutdownSwitchoverBackoffCalls.get(sessionId);
|
|
343
|
-
if (shutdownSwitchoverBackoffCall) {
|
|
344
|
-
this.logger.info(`${this.namespace}: aborting shutdown switchover connection ${sessionId}`);
|
|
345
|
-
shutdownSwitchoverBackoffCall.abort();
|
|
346
|
-
this._shutdownSwitchoverBackoffCalls.delete(sessionId);
|
|
347
|
-
}
|
|
348
|
-
// Clean up any pending connection promises
|
|
349
|
-
if (this._connectPromises) {
|
|
350
|
-
this._connectPromises.delete(sessionId);
|
|
240
|
+
if (this.backoffCall) {
|
|
241
|
+
this.logger.info(`${this.namespace}: aborting connection`);
|
|
242
|
+
this.backoffCall.abort();
|
|
351
243
|
}
|
|
352
244
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (sessionSocket) {
|
|
357
|
-
sessionSocket.removeAllListeners('message');
|
|
358
|
-
sessionSocket.connecting = false;
|
|
359
|
-
sessionSocket.connected = false;
|
|
360
|
-
this.once(sessionId === this.defaultSessionId ? 'offline' : `offline${suffix}`, resolve);
|
|
361
|
-
resolve(sessionSocket.close(options || undefined));
|
|
245
|
+
if (this._shutdownSwitchoverBackoffCall) {
|
|
246
|
+
this.logger.info(`${this.namespace}: aborting shutdown switchover`);
|
|
247
|
+
this._shutdownSwitchoverBackoffCall.abort();
|
|
362
248
|
}
|
|
363
|
-
resolve();
|
|
364
249
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
* @param {object} options - Close options
|
|
373
|
-
* @returns {Promise} Promise that resolves when all connections are closed
|
|
374
|
-
*/
|
|
375
|
-
disconnectAll(options) {
|
|
376
|
-
const disconnectPromises = [];
|
|
377
|
-
|
|
378
|
-
for (const sessionId of this.sockets.keys()) {
|
|
379
|
-
disconnectPromises.push(this.disconnect(options, sessionId));
|
|
380
|
-
}
|
|
250
|
+
if (this.socket) {
|
|
251
|
+
this.socket.removeAllListeners('message');
|
|
252
|
+
this.once('offline', () => {
|
|
253
|
+
this._emit('disconnected');
|
|
254
|
+
resolve();
|
|
255
|
+
});
|
|
256
|
+
this.socket.close(options);
|
|
381
257
|
|
|
382
|
-
|
|
383
|
-
this.connected = false;
|
|
384
|
-
this.sockets.clear();
|
|
385
|
-
this.sessionWebSocketUrls.clear();
|
|
386
|
-
this.backoffCalls.clear();
|
|
387
|
-
// Clear connection promises to prevent stale promises
|
|
388
|
-
if (this._connectPromises) {
|
|
389
|
-
this._connectPromises.clear();
|
|
258
|
+
return;
|
|
390
259
|
}
|
|
260
|
+
this._emit('disconnected');
|
|
261
|
+
resolve();
|
|
391
262
|
});
|
|
392
263
|
},
|
|
393
264
|
|
|
@@ -479,29 +350,24 @@ const Mercury = WebexPlugin.extend({
|
|
|
479
350
|
}
|
|
480
351
|
|
|
481
352
|
webSocketUrl.query.clientTimestamp = Date.now();
|
|
353
|
+
|
|
482
354
|
delete webSocketUrl.search;
|
|
483
355
|
|
|
484
356
|
return url.format(webSocketUrl);
|
|
485
357
|
});
|
|
486
358
|
},
|
|
487
359
|
|
|
488
|
-
_attemptConnection(socketUrl,
|
|
360
|
+
_attemptConnection(socketUrl, callback, options = {}) {
|
|
489
361
|
const {isShutdownSwitchover = false, onSuccess = null} = options;
|
|
490
362
|
|
|
491
363
|
const socket = new Socket();
|
|
492
|
-
socket.connecting = true;
|
|
493
364
|
let newWSUrl;
|
|
494
365
|
|
|
495
|
-
this._attachSocketEventListeners(socket
|
|
496
|
-
|
|
497
|
-
const backoffCall = isShutdownSwitchover
|
|
498
|
-
? this._shutdownSwitchoverBackoffCalls.get(sessionId)
|
|
499
|
-
: this.backoffCalls.get(sessionId);
|
|
366
|
+
this._attachSocketEventListeners(socket);
|
|
500
367
|
|
|
501
368
|
// Check appropriate backoff call based on connection type
|
|
502
|
-
if (!
|
|
503
|
-
const
|
|
504
|
-
const msg = `${this.namespace}: prevent socket open when ${mode} no longer defined for ${sessionId}`;
|
|
369
|
+
if (isShutdownSwitchover && !this._shutdownSwitchoverBackoffCall) {
|
|
370
|
+
const msg = `${this.namespace}: prevent socket open when switchover backoff call no longer defined`;
|
|
505
371
|
const err = new Error(msg);
|
|
506
372
|
|
|
507
373
|
this.logger.info(msg);
|
|
@@ -512,20 +378,32 @@ const Mercury = WebexPlugin.extend({
|
|
|
512
378
|
return Promise.reject(err);
|
|
513
379
|
}
|
|
514
380
|
|
|
381
|
+
if (!isShutdownSwitchover && !this.backoffCall) {
|
|
382
|
+
const msg = `${this.namespace}: prevent socket open when backoffCall no longer defined`;
|
|
383
|
+
const err = new Error(msg);
|
|
384
|
+
|
|
385
|
+
this.logger.info(msg);
|
|
386
|
+
|
|
387
|
+
// Call the callback with the error before rejecting
|
|
388
|
+
callback(err);
|
|
389
|
+
|
|
390
|
+
// eslint-disable-next-line no-unreachable
|
|
391
|
+
return Promise.reject(err);
|
|
392
|
+
}
|
|
393
|
+
|
|
515
394
|
// For shutdown switchover, don't set socket yet (make-before-break)
|
|
516
395
|
// For normal connection, set socket before opening to allow disconnect() to close it
|
|
517
396
|
if (!isShutdownSwitchover) {
|
|
518
|
-
this.
|
|
397
|
+
this.socket = socket;
|
|
519
398
|
}
|
|
520
399
|
|
|
521
|
-
return this._prepareAndOpenSocket(socket, socketUrl,
|
|
400
|
+
return this._prepareAndOpenSocket(socket, socketUrl, isShutdownSwitchover)
|
|
522
401
|
.then((webSocketUrl) => {
|
|
523
402
|
newWSUrl = webSocketUrl;
|
|
524
|
-
|
|
525
403
|
this.logger.info(
|
|
526
404
|
`${this.namespace}: ${
|
|
527
405
|
isShutdownSwitchover ? '[shutdown] switchover' : ''
|
|
528
|
-
} connected to mercury, success, action: connected
|
|
406
|
+
} connected to mercury, success, action: connected, url: ${newWSUrl}`
|
|
529
407
|
);
|
|
530
408
|
|
|
531
409
|
// Custom success handler for shutdown switchover
|
|
@@ -552,10 +430,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
552
430
|
.catch((reason) => {
|
|
553
431
|
// For shutdown, simpler error handling - just callback for retry
|
|
554
432
|
if (isShutdownSwitchover) {
|
|
555
|
-
this.logger.info(
|
|
556
|
-
`${this.namespace}: [shutdown] switchover attempt failed for ${sessionId}`,
|
|
557
|
-
reason
|
|
558
|
-
);
|
|
433
|
+
this.logger.info(`${this.namespace}: [shutdown] switchover attempt failed`, reason);
|
|
559
434
|
|
|
560
435
|
return callback(reason);
|
|
561
436
|
}
|
|
@@ -563,36 +438,30 @@ const Mercury = WebexPlugin.extend({
|
|
|
563
438
|
// Normal connection error handling (existing complex logic)
|
|
564
439
|
this.lastError = reason; // remember the last error
|
|
565
440
|
|
|
566
|
-
const backoffCallNormal = this.backoffCalls.get(sessionId);
|
|
567
441
|
// Suppress connection errors that appear to be network related. This
|
|
568
442
|
// may end up suppressing metrics during outages, but we might not care
|
|
569
443
|
// (especially since many of our outages happen in a way that client
|
|
570
444
|
// metrics can't be trusted).
|
|
571
|
-
if (reason.code !== 1006 &&
|
|
572
|
-
this._emit(
|
|
573
|
-
sessionId,
|
|
574
|
-
retries: backoffCallNormal?.getNumRetries(),
|
|
575
|
-
});
|
|
445
|
+
if (reason.code !== 1006 && this.backoffCall && this.backoffCall?.getNumRetries() > 0) {
|
|
446
|
+
this._emit('connection_failed', reason, {retries: this.backoffCall?.getNumRetries()});
|
|
576
447
|
}
|
|
577
448
|
this.logger.info(
|
|
578
|
-
`${this.namespace}: connection attempt failed
|
|
449
|
+
`${this.namespace}: connection attempt failed`,
|
|
579
450
|
reason,
|
|
580
|
-
|
|
451
|
+
this.backoffCall?.getNumRetries() === 0 ? reason.stack : ''
|
|
581
452
|
);
|
|
582
453
|
// UnknownResponse is produced by IE for any 4XXX; treated it like a bad
|
|
583
454
|
// web socket url and let WDM handle the token checking
|
|
584
455
|
if (reason instanceof UnknownResponse) {
|
|
585
456
|
this.logger.info(
|
|
586
|
-
`${this.namespace}: received unknown response code
|
|
457
|
+
`${this.namespace}: received unknown response code, refreshing device registration`
|
|
587
458
|
);
|
|
588
459
|
|
|
589
460
|
return this.webex.internal.device.refresh().then(() => callback(reason));
|
|
590
461
|
}
|
|
591
462
|
// NotAuthorized implies expired token
|
|
592
463
|
if (reason instanceof NotAuthorized) {
|
|
593
|
-
this.logger.info(
|
|
594
|
-
`${this.namespace}: received authorization error for ${sessionId}, reauthorizing`
|
|
595
|
-
);
|
|
464
|
+
this.logger.info(`${this.namespace}: received authorization error, reauthorizing`);
|
|
596
465
|
|
|
597
466
|
return this.webex.credentials.refresh({force: true}).then(() => callback(reason));
|
|
598
467
|
}
|
|
@@ -605,10 +474,8 @@ const Mercury = WebexPlugin.extend({
|
|
|
605
474
|
// BadRequest implies current credentials are for a Service Account
|
|
606
475
|
// Forbidden implies current user is not entitle for Webex
|
|
607
476
|
if (reason instanceof BadRequest || reason instanceof Forbidden) {
|
|
608
|
-
this.logger.warn(
|
|
609
|
-
|
|
610
|
-
);
|
|
611
|
-
backoffCallNormal?.abort();
|
|
477
|
+
this.logger.warn(`${this.namespace}: received unrecoverable response from mercury`);
|
|
478
|
+
this.backoffCall.abort();
|
|
612
479
|
|
|
613
480
|
return callback(reason);
|
|
614
481
|
}
|
|
@@ -620,19 +487,19 @@ const Mercury = WebexPlugin.extend({
|
|
|
620
487
|
|
|
621
488
|
if (haMessagingEnabled) {
|
|
622
489
|
this.logger.info(
|
|
623
|
-
`${this.namespace}: received a generic connection error
|
|
490
|
+
`${this.namespace}: received a generic connection error, will try to connect to another datacenter. failed, action: 'failed', url: ${wsUrl} error: ${reason.message}`
|
|
624
491
|
);
|
|
625
492
|
|
|
626
493
|
if (wsUrl) {
|
|
627
494
|
this.logger.info(
|
|
628
|
-
`${this.namespace}: marking ${wsUrl} as failed
|
|
495
|
+
`${this.namespace}: marking ${wsUrl} as failed due to connection error`
|
|
629
496
|
);
|
|
630
497
|
|
|
631
498
|
return this.webex.internal.services.markFailedUrl(wsUrl);
|
|
632
499
|
}
|
|
633
500
|
|
|
634
501
|
this.logger.info(
|
|
635
|
-
`${this.namespace}: no socket url available to mark as failed
|
|
502
|
+
`${this.namespace}: no socket url available to mark as failed due to connection error`
|
|
636
503
|
);
|
|
637
504
|
}
|
|
638
505
|
|
|
@@ -644,15 +511,12 @@ const Mercury = WebexPlugin.extend({
|
|
|
644
511
|
return callback(reason);
|
|
645
512
|
})
|
|
646
513
|
.catch((reason) => {
|
|
647
|
-
this.logger.error(
|
|
648
|
-
`${this.namespace}: failed to handle connection failure for ${sessionId}`,
|
|
649
|
-
reason
|
|
650
|
-
);
|
|
514
|
+
this.logger.error(`${this.namespace}: failed to handle connection failure`, reason);
|
|
651
515
|
callback(reason);
|
|
652
516
|
});
|
|
653
517
|
},
|
|
654
518
|
|
|
655
|
-
_prepareAndOpenSocket(socket, socketUrl,
|
|
519
|
+
_prepareAndOpenSocket(socket, socketUrl, isShutdownSwitchover = false) {
|
|
656
520
|
const logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
657
521
|
|
|
658
522
|
return Promise.all([this._prepareUrl(socketUrl), this.webex.credentials.getUserToken()]).then(
|
|
@@ -675,20 +539,14 @@ const Mercury = WebexPlugin.extend({
|
|
|
675
539
|
options = {...options, ...this.webex.config.defaultMercuryOptions};
|
|
676
540
|
}
|
|
677
541
|
|
|
678
|
-
|
|
679
|
-
// the socket if it is in the process of being opened.
|
|
680
|
-
this.sockets.set(sessionId, socket);
|
|
681
|
-
this.socket = this.sockets.get(this.defaultSessionId);
|
|
682
|
-
|
|
683
|
-
this.logger.info(`${this.namespace} ${logPrefix} url for ${sessionId}: ${webSocketUrl}`);
|
|
542
|
+
this.logger.info(`${this.namespace}: ${logPrefix} url: ${webSocketUrl}`);
|
|
684
543
|
|
|
685
|
-
//
|
|
686
|
-
//
|
|
687
|
-
//
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
this.sessionWebSocketUrls.set(sessionId, webSocketUrl);
|
|
544
|
+
// Store the resolved URL before socket.open(), which may be rewritten by
|
|
545
|
+
// an interceptor (e.g. same-site websocket proxy). On reconnection we need
|
|
546
|
+
// the original catalog-valid URL, not the post-proxy one from socket.url.
|
|
547
|
+
if (!isShutdownSwitchover) {
|
|
548
|
+
this.resolvedWebSocketUrl = webSocketUrl;
|
|
549
|
+
}
|
|
692
550
|
|
|
693
551
|
return socket
|
|
694
552
|
.open(webSocketUrl, options)
|
|
@@ -702,20 +560,22 @@ const Mercury = WebexPlugin.extend({
|
|
|
702
560
|
);
|
|
703
561
|
},
|
|
704
562
|
|
|
705
|
-
_connectWithBackoff(webSocketUrl,
|
|
563
|
+
_connectWithBackoff(webSocketUrl, context = {}) {
|
|
706
564
|
const {isShutdownSwitchover = false, attemptOptions = {}} = context;
|
|
707
565
|
|
|
708
566
|
return new Promise((resolve, reject) => {
|
|
709
|
-
// eslint gets confused about whether call is actually used
|
|
567
|
+
// eslint gets confused about whether or not call is actually used
|
|
710
568
|
// eslint-disable-next-line prefer-const
|
|
711
569
|
let call;
|
|
712
|
-
const onComplete = (err
|
|
570
|
+
const onComplete = (err) => {
|
|
571
|
+
// Clear state flags based on connection type
|
|
713
572
|
if (isShutdownSwitchover) {
|
|
714
|
-
this.
|
|
573
|
+
this._shutdownSwitchoverBackoffCall = undefined;
|
|
715
574
|
} else {
|
|
716
|
-
this.
|
|
575
|
+
this.connecting = false;
|
|
576
|
+
this.backoffCall = undefined;
|
|
717
577
|
}
|
|
718
|
-
|
|
578
|
+
|
|
719
579
|
if (err) {
|
|
720
580
|
const msg = isShutdownSwitchover
|
|
721
581
|
? `[shutdown] switchover failed after ${call.getNumRetries()} retries`
|
|
@@ -724,45 +584,29 @@ const Mercury = WebexPlugin.extend({
|
|
|
724
584
|
this.logger.info(
|
|
725
585
|
`${this.namespace}: ${msg}; log statement about next retry was inaccurate; ${err}`
|
|
726
586
|
);
|
|
727
|
-
if (sessionSocket) {
|
|
728
|
-
sessionSocket.connecting = false;
|
|
729
|
-
sessionSocket.connected = false;
|
|
730
|
-
}
|
|
731
587
|
|
|
732
588
|
return reject(err);
|
|
733
589
|
}
|
|
734
590
|
|
|
735
|
-
// Update overall connected status
|
|
736
|
-
if (sessionSocket) {
|
|
737
|
-
sessionSocket.connecting = false;
|
|
738
|
-
sessionSocket.connected = true;
|
|
739
|
-
}
|
|
740
591
|
// Default success handling for normal connections
|
|
741
592
|
if (!isShutdownSwitchover) {
|
|
742
|
-
this.
|
|
743
|
-
this.connected = this.hasConnectedSockets();
|
|
593
|
+
this.connected = true;
|
|
744
594
|
this.hasEverConnected = true;
|
|
745
|
-
this._emit(
|
|
746
|
-
|
|
747
|
-
this.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(true);
|
|
748
|
-
}
|
|
595
|
+
this._emit('online');
|
|
596
|
+
this.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(true);
|
|
749
597
|
}
|
|
750
598
|
|
|
751
599
|
return resolve();
|
|
752
600
|
};
|
|
601
|
+
|
|
753
602
|
// eslint-disable-next-line prefer-reflect
|
|
754
|
-
call = backoff.call(
|
|
755
|
-
(
|
|
756
|
-
|
|
757
|
-
const logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
603
|
+
call = backoff.call((callback) => {
|
|
604
|
+
const attemptNum = call.getNumRetries();
|
|
605
|
+
const logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
758
606
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
this._attemptConnection(webSocketUrl, sessionId, callback, attemptOptions);
|
|
763
|
-
},
|
|
764
|
-
(err) => onComplete(err, sessionId)
|
|
765
|
-
);
|
|
607
|
+
this.logger.info(`${this.namespace}: executing ${logPrefix} attempt ${attemptNum}`);
|
|
608
|
+
this._attemptConnection(webSocketUrl, callback, attemptOptions);
|
|
609
|
+
}, onComplete);
|
|
766
610
|
|
|
767
611
|
call.setStrategy(
|
|
768
612
|
new backoff.ExponentialStrategy({
|
|
@@ -781,32 +625,23 @@ const Mercury = WebexPlugin.extend({
|
|
|
781
625
|
call.failAfter(this.config.maxRetries);
|
|
782
626
|
}
|
|
783
627
|
|
|
784
|
-
// Store the call BEFORE setting up event handlers to prevent race conditions
|
|
785
|
-
// Store backoff call reference BEFORE starting (so it's available in _attemptConnection)
|
|
786
|
-
if (isShutdownSwitchover) {
|
|
787
|
-
this._shutdownSwitchoverBackoffCalls.set(sessionId, call);
|
|
788
|
-
} else {
|
|
789
|
-
this.backoffCalls.set(sessionId, call);
|
|
790
|
-
}
|
|
791
|
-
|
|
792
628
|
call.on('abort', () => {
|
|
793
629
|
const msg = isShutdownSwitchover ? 'Shutdown Switchover' : 'Connection';
|
|
794
630
|
|
|
795
|
-
this.logger.info(`${this.namespace}: ${msg} aborted
|
|
796
|
-
reject(new Error(`Mercury ${msg} Aborted
|
|
631
|
+
this.logger.info(`${this.namespace}: ${msg} aborted`);
|
|
632
|
+
reject(new Error(`Mercury ${msg} Aborted`));
|
|
797
633
|
});
|
|
798
634
|
|
|
799
635
|
call.on('callback', (err) => {
|
|
800
636
|
if (err) {
|
|
801
637
|
const number = call.getNumRetries();
|
|
802
638
|
const delay = Math.min(call.strategy_.nextBackoffDelay_, this.config.backoffTimeMax);
|
|
803
|
-
|
|
804
639
|
const logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : '';
|
|
805
640
|
|
|
806
641
|
this.logger.info(
|
|
807
642
|
`${this.namespace}: ${logPrefix} failed to connect; attempting retry ${
|
|
808
643
|
number + 1
|
|
809
|
-
} in ${delay} ms
|
|
644
|
+
} in ${delay} ms`
|
|
810
645
|
);
|
|
811
646
|
/* istanbul ignore if */
|
|
812
647
|
if (process.env.NODE_ENV === 'development') {
|
|
@@ -815,36 +650,24 @@ const Mercury = WebexPlugin.extend({
|
|
|
815
650
|
|
|
816
651
|
return;
|
|
817
652
|
}
|
|
818
|
-
this.logger.info(`${this.namespace}: connected
|
|
653
|
+
this.logger.info(`${this.namespace}: connected`);
|
|
819
654
|
});
|
|
820
655
|
|
|
656
|
+
// Store backoff call reference BEFORE starting (so it's available in _attemptConnection)
|
|
657
|
+
if (isShutdownSwitchover) {
|
|
658
|
+
this._shutdownSwitchoverBackoffCall = call;
|
|
659
|
+
} else {
|
|
660
|
+
this.backoffCall = call;
|
|
661
|
+
}
|
|
662
|
+
|
|
821
663
|
call.start();
|
|
822
664
|
});
|
|
823
665
|
},
|
|
824
666
|
|
|
825
667
|
_emit(...args) {
|
|
826
668
|
try {
|
|
827
|
-
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
// New signature: _emit(sessionId, eventName, ...rest)
|
|
832
|
-
// Backwards compatibility: if the first arg isn't a known sessionId (or defaultSessionId),
|
|
833
|
-
// treat the call as the old signature and forward directly to trigger(...)
|
|
834
|
-
const [first, second, ...rest] = args;
|
|
835
|
-
|
|
836
|
-
if (typeof first === 'string' && typeof second === 'string') {
|
|
837
|
-
const sessionId = first;
|
|
838
|
-
const eventName = second;
|
|
839
|
-
const suffix = sessionId === this.defaultSessionId ? '' : `:${sessionId}`;
|
|
840
|
-
|
|
841
|
-
this.trigger(`${eventName}${suffix}`, ...rest);
|
|
842
|
-
} else {
|
|
843
|
-
// Old usage: _emit(eventName, ...args)
|
|
844
|
-
this.trigger(...args);
|
|
845
|
-
}
|
|
669
|
+
this.trigger(...args);
|
|
846
670
|
} catch (error) {
|
|
847
|
-
// Safely handle errors without causing additional issues during cleanup
|
|
848
671
|
try {
|
|
849
672
|
this.logger.error(
|
|
850
673
|
`${this.namespace}: error occurred in event handler:`,
|
|
@@ -864,6 +687,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
864
687
|
if (!eventType) {
|
|
865
688
|
return [];
|
|
866
689
|
}
|
|
690
|
+
|
|
867
691
|
const [namespace, name] = eventType.split('.');
|
|
868
692
|
const handlers = [];
|
|
869
693
|
|
|
@@ -883,7 +707,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
883
707
|
return handlers;
|
|
884
708
|
},
|
|
885
709
|
|
|
886
|
-
_submitMercuryClose4000Metric(
|
|
710
|
+
_submitMercuryClose4000Metric(event, options) {
|
|
887
711
|
const {action, isActiveSocket, messageType} = options;
|
|
888
712
|
|
|
889
713
|
try {
|
|
@@ -894,7 +718,6 @@ const Mercury = WebexPlugin.extend({
|
|
|
894
718
|
close_reason: event.reason || '',
|
|
895
719
|
is_active_socket: isActiveSocket,
|
|
896
720
|
message_type: messageType,
|
|
897
|
-
session_id: sessionId,
|
|
898
721
|
},
|
|
899
722
|
tags: {
|
|
900
723
|
action,
|
|
@@ -906,45 +729,31 @@ const Mercury = WebexPlugin.extend({
|
|
|
906
729
|
}
|
|
907
730
|
},
|
|
908
731
|
|
|
909
|
-
_onclose(
|
|
732
|
+
_onclose(event, sourceSocket) {
|
|
910
733
|
// I don't see any way to avoid the complexity or statement count in here.
|
|
911
734
|
/* eslint complexity: [0] */
|
|
912
735
|
|
|
913
736
|
try {
|
|
737
|
+
const isActiveSocket = sourceSocket === this.socket;
|
|
914
738
|
const reason = event.reason && event.reason.toLowerCase();
|
|
915
|
-
const sessionSocket = this.sockets.get(sessionId);
|
|
916
|
-
event.sessionId = sessionId;
|
|
917
739
|
|
|
918
|
-
|
|
919
|
-
//
|
|
920
|
-
|
|
921
|
-
// sourceSocket.url: that reflects the URL the native WebSocket was
|
|
922
|
-
// actually opened with, which a lower layer may have rewritten
|
|
923
|
-
// (e.g. via interceptors) into a host that is not in the SDK host
|
|
924
|
-
// catalog. Feeding such a URL back into _prepareUrl fails host-catalog
|
|
925
|
-
// validation and permanently blocks reconnection.
|
|
926
|
-
const socketUrl = this.sessionWebSocketUrls.get(sessionId);
|
|
927
|
-
|
|
928
|
-
this.sockets.delete(sessionId);
|
|
740
|
+
// Use the stored resolved URL for reconnection instead of sourceSocket.url,
|
|
741
|
+
// which may have been rewritten by an interceptor to a non-catalog host.
|
|
742
|
+
const socketUrl = this.resolvedWebSocketUrl || (sourceSocket && sourceSocket.url);
|
|
929
743
|
|
|
930
744
|
if (isActiveSocket) {
|
|
931
745
|
// Only tear down state if the currently active socket closed
|
|
932
|
-
if (
|
|
933
|
-
|
|
934
|
-
if (sessionId === this.defaultSessionId) this.unset('socket');
|
|
935
|
-
this._emit(sessionId, 'offline', event);
|
|
936
|
-
}
|
|
937
|
-
// Update overall connected status
|
|
938
|
-
this.connecting = this.hasConnectingSockets();
|
|
939
|
-
this.connected = this.hasConnectedSockets();
|
|
940
|
-
|
|
941
|
-
if (!this.connected) {
|
|
942
|
-
this.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(false);
|
|
746
|
+
if (this.socket) {
|
|
747
|
+
this.socket.removeAllListeners();
|
|
943
748
|
}
|
|
749
|
+
this.unset('socket');
|
|
750
|
+
this.connected = false;
|
|
751
|
+
this._emit('offline', event);
|
|
752
|
+
this.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(false);
|
|
944
753
|
} else {
|
|
945
754
|
// Old socket closed; do not flip connection state
|
|
946
755
|
this.logger.info(
|
|
947
|
-
`${this.namespace}: [shutdown] non-active socket closed, code=${event.code}
|
|
756
|
+
`${this.namespace}: [shutdown] non-active socket closed, code=${event.code}`
|
|
948
757
|
);
|
|
949
758
|
// Clean up listeners from old socket now that it's closed
|
|
950
759
|
if (sourceSocket) {
|
|
@@ -956,32 +765,32 @@ const Mercury = WebexPlugin.extend({
|
|
|
956
765
|
case 1003:
|
|
957
766
|
// metric: disconnect
|
|
958
767
|
this.logger.info(
|
|
959
|
-
`${this.namespace}: Mercury service rejected last message
|
|
768
|
+
`${this.namespace}: Mercury service rejected last message; will not reconnect: ${event.reason}`
|
|
960
769
|
);
|
|
961
|
-
if (isActiveSocket) this._emit(
|
|
770
|
+
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
962
771
|
break;
|
|
963
772
|
case 4000:
|
|
964
|
-
// metric: disconnect
|
|
773
|
+
// metric: disconnect, check reason
|
|
965
774
|
if (reason === 'replaced') {
|
|
966
|
-
this._submitMercuryClose4000Metric(
|
|
775
|
+
this._submitMercuryClose4000Metric(event, {
|
|
967
776
|
action: 'no_action',
|
|
968
777
|
isActiveSocket,
|
|
969
778
|
messageType: 'replaced',
|
|
970
779
|
});
|
|
971
|
-
this.logger.info(`${this.namespace}: socket
|
|
972
|
-
if (isActiveSocket) this._emit(
|
|
780
|
+
this.logger.info(`${this.namespace}: socket replaced; will not reconnect`);
|
|
781
|
+
if (isActiveSocket) this._emit('offline.replaced', event);
|
|
973
782
|
} else {
|
|
974
|
-
this._submitMercuryClose4000Metric(
|
|
783
|
+
this._submitMercuryClose4000Metric(event, {
|
|
975
784
|
action: isActiveSocket ? 'reconnect' : 'ignore_non_active',
|
|
976
785
|
isActiveSocket,
|
|
977
786
|
messageType: 'other',
|
|
978
787
|
});
|
|
979
788
|
this.logger.info(
|
|
980
|
-
`${this.namespace}: socket
|
|
789
|
+
`${this.namespace}: socket disconnected with 4000: ${event.reason}; reconnecting`
|
|
981
790
|
);
|
|
982
791
|
if (isActiveSocket) {
|
|
983
|
-
this._emit(
|
|
984
|
-
this._reconnect(socketUrl
|
|
792
|
+
this._emit('offline.transient', event);
|
|
793
|
+
this._reconnect(socketUrl);
|
|
985
794
|
}
|
|
986
795
|
}
|
|
987
796
|
break;
|
|
@@ -992,28 +801,26 @@ const Mercury = WebexPlugin.extend({
|
|
|
992
801
|
// to be replaced, but the switchover in _handleImminentShutdown failed.
|
|
993
802
|
// This is a permanent failure - do not reconnect.
|
|
994
803
|
this.logger.warn(
|
|
995
|
-
`${this.namespace}: active socket closed with 4001; shutdown switchover failed
|
|
804
|
+
`${this.namespace}: active socket closed with 4001; shutdown switchover failed`
|
|
996
805
|
);
|
|
997
|
-
this._emit(
|
|
806
|
+
this._emit('offline.permanent', event);
|
|
998
807
|
} else {
|
|
999
808
|
// Expected: old socket closed after successful switchover
|
|
1000
809
|
this.logger.info(
|
|
1001
|
-
`${this.namespace}: old socket closed with 4001 (replaced during shutdown); no reconnect needed
|
|
810
|
+
`${this.namespace}: old socket closed with 4001 (replaced during shutdown); no reconnect needed`
|
|
1002
811
|
);
|
|
1003
|
-
this._emit(
|
|
812
|
+
this._emit('offline.replaced', event);
|
|
1004
813
|
}
|
|
1005
814
|
break;
|
|
1006
815
|
case 1001:
|
|
1007
816
|
case 1005:
|
|
1008
817
|
case 1006:
|
|
1009
818
|
case 1011:
|
|
1010
|
-
this.logger.info(`${this.namespace}: socket
|
|
819
|
+
this.logger.info(`${this.namespace}: socket disconnected; reconnecting`);
|
|
1011
820
|
if (isActiveSocket) {
|
|
1012
|
-
this._emit(
|
|
1013
|
-
this.logger.info(
|
|
1014
|
-
|
|
1015
|
-
);
|
|
1016
|
-
this._reconnect(socketUrl, sessionId);
|
|
821
|
+
this._emit('offline.transient', event);
|
|
822
|
+
this.logger.info(`${this.namespace}: [shutdown] reconnecting active socket to recover`);
|
|
823
|
+
this._reconnect(socketUrl);
|
|
1017
824
|
}
|
|
1018
825
|
// metric: disconnect
|
|
1019
826
|
// if (code == 1011 && reason !== ping error) metric: unexpected disconnect
|
|
@@ -1021,71 +828,61 @@ const Mercury = WebexPlugin.extend({
|
|
|
1021
828
|
case 1000:
|
|
1022
829
|
case 3050: // 3050 indicates logout form of closure, default to old behavior, use config reason defined by consumer to proceed with the permanent block
|
|
1023
830
|
if (normalReconnectReasons.includes(reason)) {
|
|
1024
|
-
this.logger.info(`${this.namespace}: socket
|
|
831
|
+
this.logger.info(`${this.namespace}: socket disconnected; reconnecting`);
|
|
1025
832
|
if (isActiveSocket) {
|
|
1026
|
-
this._emit(
|
|
1027
|
-
this.logger.info(
|
|
1028
|
-
|
|
1029
|
-
);
|
|
1030
|
-
this._reconnect(socketUrl, sessionId);
|
|
833
|
+
this._emit('offline.transient', event);
|
|
834
|
+
this.logger.info(`${this.namespace}: [shutdown] reconnecting due to normal close`);
|
|
835
|
+
this._reconnect(socketUrl);
|
|
1031
836
|
}
|
|
1032
837
|
// metric: disconnect
|
|
1033
838
|
// if (reason === done forced) metric: force closure
|
|
1034
839
|
} else {
|
|
1035
840
|
this.logger.info(
|
|
1036
|
-
`${this.namespace}: socket
|
|
841
|
+
`${this.namespace}: socket disconnected; will not reconnect: ${event.reason}`
|
|
1037
842
|
);
|
|
1038
|
-
if (isActiveSocket) this._emit(
|
|
843
|
+
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
1039
844
|
}
|
|
1040
845
|
break;
|
|
1041
846
|
default:
|
|
1042
847
|
this.logger.info(
|
|
1043
|
-
`${this.namespace}: socket
|
|
848
|
+
`${this.namespace}: socket disconnected unexpectedly; will not reconnect`
|
|
1044
849
|
);
|
|
1045
850
|
// unexpected disconnect
|
|
1046
|
-
if (isActiveSocket) this._emit(
|
|
851
|
+
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
1047
852
|
}
|
|
1048
853
|
} catch (error) {
|
|
1049
|
-
this.logger.error(
|
|
1050
|
-
`${this.namespace}: error occurred in close handler for ${sessionId}`,
|
|
1051
|
-
error
|
|
1052
|
-
);
|
|
854
|
+
this.logger.error(`${this.namespace}: error occurred in close handler`, error);
|
|
1053
855
|
}
|
|
1054
856
|
},
|
|
1055
857
|
|
|
1056
|
-
_onmessage(
|
|
1057
|
-
this._setTimeOffset(
|
|
858
|
+
_onmessage(event) {
|
|
859
|
+
this._setTimeOffset(event);
|
|
1058
860
|
const envelope = event.data;
|
|
1059
861
|
|
|
1060
862
|
if (process.env.ENABLE_MERCURY_LOGGING) {
|
|
1061
|
-
this.logger.debug(`${this.namespace}: message envelope
|
|
863
|
+
this.logger.debug(`${this.namespace}: message envelope: `, envelope);
|
|
1062
864
|
}
|
|
1063
865
|
|
|
1064
|
-
envelope.sessionId = sessionId;
|
|
1065
|
-
|
|
1066
866
|
// Handle shutdown message shape: { type: 'shutdown' }
|
|
1067
867
|
if (envelope && envelope.type === 'shutdown') {
|
|
1068
|
-
this.logger.info(
|
|
1069
|
-
|
|
1070
|
-
);
|
|
1071
|
-
this._emit(sessionId, 'event:mercury_shutdown_imminent', envelope);
|
|
868
|
+
this.logger.info(`${this.namespace}: [shutdown] imminent shutdown message received`);
|
|
869
|
+
this._emit('event:mercury_shutdown_imminent', envelope);
|
|
1072
870
|
|
|
1073
|
-
this._handleImminentShutdown(
|
|
871
|
+
this._handleImminentShutdown();
|
|
1074
872
|
|
|
1075
873
|
return Promise.resolve();
|
|
1076
874
|
}
|
|
1077
875
|
|
|
1078
|
-
envelope.sessionId = sessionId;
|
|
1079
876
|
const {data} = envelope;
|
|
1080
877
|
|
|
1081
|
-
this._applyOverrides(data);
|
|
1082
|
-
|
|
1083
878
|
if (!data || !data.eventType) {
|
|
1084
|
-
this._emit(
|
|
879
|
+
this._emit('event', envelope);
|
|
1085
880
|
|
|
1086
881
|
return Promise.resolve();
|
|
1087
882
|
}
|
|
1088
883
|
|
|
884
|
+
this._applyOverrides(data);
|
|
885
|
+
|
|
1089
886
|
return this._getEventHandlers(data.eventType)
|
|
1090
887
|
.reduce(
|
|
1091
888
|
(promise, handler) =>
|
|
@@ -1096,7 +893,7 @@ const Mercury = WebexPlugin.extend({
|
|
|
1096
893
|
resolve((this.webex[namespace] || this.webex.internal[namespace])[name](data))
|
|
1097
894
|
).catch((reason) =>
|
|
1098
895
|
this.logger.error(
|
|
1099
|
-
`${this.namespace}: error occurred in autowired event handler for ${data.eventType}
|
|
896
|
+
`${this.namespace}: error occurred in autowired event handler for ${data.eventType}`,
|
|
1100
897
|
reason
|
|
1101
898
|
)
|
|
1102
899
|
);
|
|
@@ -1104,35 +901,32 @@ const Mercury = WebexPlugin.extend({
|
|
|
1104
901
|
Promise.resolve()
|
|
1105
902
|
)
|
|
1106
903
|
.then(() => {
|
|
1107
|
-
this._emit(
|
|
1108
|
-
const [namespace] = data.eventType.split('.');
|
|
904
|
+
this._emit('event', event.data);
|
|
905
|
+
const [namespace] = data.eventType ? data.eventType.split('.') : [];
|
|
1109
906
|
|
|
1110
907
|
if (namespace === data.eventType) {
|
|
1111
|
-
this._emit(
|
|
908
|
+
this._emit(`event:${namespace}`, envelope);
|
|
1112
909
|
} else {
|
|
1113
|
-
this._emit(
|
|
1114
|
-
this._emit(
|
|
910
|
+
this._emit(`event:${namespace}`, envelope);
|
|
911
|
+
this._emit(`event:${data.eventType}`, envelope);
|
|
1115
912
|
}
|
|
1116
913
|
})
|
|
1117
914
|
.catch((reason) => {
|
|
1118
|
-
this.logger.error(
|
|
1119
|
-
`${this.namespace}: error occurred processing socket message from ${sessionId}`,
|
|
1120
|
-
reason
|
|
1121
|
-
);
|
|
915
|
+
this.logger.error(`${this.namespace}: error occurred processing socket message`, reason);
|
|
1122
916
|
});
|
|
1123
917
|
},
|
|
1124
918
|
|
|
1125
|
-
_setTimeOffset(
|
|
919
|
+
_setTimeOffset(event) {
|
|
1126
920
|
const {wsWriteTimestamp} = event.data;
|
|
1127
921
|
if (typeof wsWriteTimestamp === 'number' && wsWriteTimestamp > 0) {
|
|
1128
922
|
this.mercuryTimeOffset = Date.now() - wsWriteTimestamp;
|
|
1129
923
|
}
|
|
1130
924
|
},
|
|
1131
925
|
|
|
1132
|
-
_reconnect(webSocketUrl
|
|
1133
|
-
this.logger.info(`${this.namespace}: reconnecting
|
|
926
|
+
_reconnect(webSocketUrl) {
|
|
927
|
+
this.logger.info(`${this.namespace}: reconnecting`);
|
|
1134
928
|
|
|
1135
|
-
return this.connect(webSocketUrl
|
|
929
|
+
return this.connect(webSocketUrl);
|
|
1136
930
|
},
|
|
1137
931
|
});
|
|
1138
932
|
|