@webex/internal-plugin-mercury 3.9.0 → 3.10.0
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/mercury.js +341 -134
- package/dist/mercury.js.map +1 -1
- package/package.json +18 -18
- package/src/mercury.js +311 -71
- package/test/unit/spec/mercury.js +684 -1
- package/test/unit/spec/socket.js +6 -6
package/dist/mercury.js
CHANGED
|
@@ -11,11 +11,11 @@ _Object$defineProperty(exports, "__esModule", {
|
|
|
11
11
|
value: true
|
|
12
12
|
});
|
|
13
13
|
exports.default = void 0;
|
|
14
|
+
var _now = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/date/now"));
|
|
14
15
|
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
|
|
15
16
|
var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
|
|
16
17
|
var _assign = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/assign"));
|
|
17
18
|
var _deleteProperty = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/reflect/delete-property"));
|
|
18
|
-
var _now = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/date/now"));
|
|
19
19
|
var _getOwnPropertyDescriptor = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor"));
|
|
20
20
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
|
|
21
21
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
|
|
@@ -79,6 +79,109 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
79
79
|
_this.webex.internal.feature.updateFeature(envelope.data.featureToggle);
|
|
80
80
|
}
|
|
81
81
|
});
|
|
82
|
+
/*
|
|
83
|
+
* When Cluster Migrations, notify clients using ActiveClusterStatusEvent via mercury
|
|
84
|
+
* https://wwwin-github.cisco.com/pages/Webex/crr-docs/techdocs/rr-002.html#wip-notifying-clients-of-cluster-migrations
|
|
85
|
+
* */
|
|
86
|
+
this.on('event:ActiveClusterStatusEvent', function (envelope) {
|
|
87
|
+
var _this$webex$internal$;
|
|
88
|
+
if (typeof ((_this$webex$internal$ = _this.webex.internal.services) === null || _this$webex$internal$ === void 0 ? void 0 : _this$webex$internal$.switchActiveClusterIds) === 'function' && envelope && envelope.data) {
|
|
89
|
+
var _envelope$data;
|
|
90
|
+
_this.webex.internal.services.switchActiveClusterIds((_envelope$data = envelope.data) === null || _envelope$data === void 0 ? void 0 : _envelope$data.activeClusters);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
/*
|
|
94
|
+
* Using cache-invalidation via mercury to instead the method of polling via the new /timestamp endpoint from u2c
|
|
95
|
+
* https://wwwin-github.cisco.com/pages/Webex/crr-docs/techdocs/rr-005.html#websocket-notifications
|
|
96
|
+
* */
|
|
97
|
+
this.on('event:u2c.cache-invalidation', function (envelope) {
|
|
98
|
+
var _this$webex$internal$2;
|
|
99
|
+
if (typeof ((_this$webex$internal$2 = _this.webex.internal.services) === null || _this$webex$internal$2 === void 0 ? void 0 : _this$webex$internal$2.invalidateCache) === 'function' && envelope && envelope.data) {
|
|
100
|
+
var _envelope$data2;
|
|
101
|
+
_this.webex.internal.services.invalidateCache((_envelope$data2 = envelope.data) === null || _envelope$data2 === void 0 ? void 0 : _envelope$data2.timestamp);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* Attach event listeners to a socket.
|
|
107
|
+
* @param {Socket} socket - The socket to attach listeners to
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
110
|
+
_attachSocketEventListeners: function _attachSocketEventListeners(socket) {
|
|
111
|
+
var _this2 = this;
|
|
112
|
+
socket.on('close', function (event) {
|
|
113
|
+
return _this2._onclose(event, socket);
|
|
114
|
+
});
|
|
115
|
+
socket.on('message', function () {
|
|
116
|
+
return _this2._onmessage.apply(_this2, arguments);
|
|
117
|
+
});
|
|
118
|
+
socket.on('pong', function () {
|
|
119
|
+
return _this2._setTimeOffset.apply(_this2, arguments);
|
|
120
|
+
});
|
|
121
|
+
socket.on('sequence-mismatch', function () {
|
|
122
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
123
|
+
args[_key] = arguments[_key];
|
|
124
|
+
}
|
|
125
|
+
return _this2._emit.apply(_this2, ['sequence-mismatch'].concat(args));
|
|
126
|
+
});
|
|
127
|
+
socket.on('ping-pong-latency', function () {
|
|
128
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
129
|
+
args[_key2] = arguments[_key2];
|
|
130
|
+
}
|
|
131
|
+
return _this2._emit.apply(_this2, ['ping-pong-latency'].concat(args));
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
/**
|
|
135
|
+
* Handle imminent shutdown by establishing a new connection while keeping
|
|
136
|
+
* the current one alive (make-before-break).
|
|
137
|
+
* Idempotent: will no-op if already in progress.
|
|
138
|
+
* @returns {void}
|
|
139
|
+
*/
|
|
140
|
+
_handleImminentShutdown: function _handleImminentShutdown() {
|
|
141
|
+
var _this3 = this;
|
|
142
|
+
try {
|
|
143
|
+
if (this._shutdownSwitchoverInProgress) {
|
|
144
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] switchover already in progress"));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
this._shutdownSwitchoverInProgress = true;
|
|
148
|
+
this._shutdownSwitchoverId = "".concat((0, _now.default)());
|
|
149
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] switchover start, id=").concat(this._shutdownSwitchoverId));
|
|
150
|
+
this._connectWithBackoff(undefined, {
|
|
151
|
+
isShutdownSwitchover: true,
|
|
152
|
+
attemptOptions: {
|
|
153
|
+
isShutdownSwitchover: true,
|
|
154
|
+
onSuccess: function onSuccess(newSocket, webSocketUrl) {
|
|
155
|
+
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] switchover connected, url: ").concat(webSocketUrl));
|
|
156
|
+
var oldSocket = _this3.socket;
|
|
157
|
+
// Atomically switch active socket reference
|
|
158
|
+
_this3.socket = newSocket;
|
|
159
|
+
_this3.connected = true; // remain connected throughout
|
|
160
|
+
|
|
161
|
+
_this3._emit('event:mercury_shutdown_switchover_complete', {
|
|
162
|
+
url: webSocketUrl
|
|
163
|
+
});
|
|
164
|
+
if (oldSocket) {
|
|
165
|
+
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] old socket retained; server will close with 4001"));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}).then(function () {
|
|
170
|
+
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] switchover completed successfully"));
|
|
171
|
+
}).catch(function (err) {
|
|
172
|
+
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] switchover exhausted retries; will fall back to normal reconnection"), err);
|
|
173
|
+
_this3._emit('event:mercury_shutdown_switchover_failed', {
|
|
174
|
+
reason: err
|
|
175
|
+
});
|
|
176
|
+
// Old socket will eventually close with 4001, triggering normal reconnection
|
|
177
|
+
});
|
|
178
|
+
} catch (e) {
|
|
179
|
+
this.logger.error("".concat(this.namespace, ": [shutdown] error during switchover"), e);
|
|
180
|
+
this._shutdownSwitchoverInProgress = false;
|
|
181
|
+
this._emit('event:mercury_shutdown_switchover_failed', {
|
|
182
|
+
reason: e
|
|
183
|
+
});
|
|
184
|
+
}
|
|
82
185
|
},
|
|
83
186
|
/**
|
|
84
187
|
* Get the last error.
|
|
@@ -88,7 +191,7 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
88
191
|
return this.lastError;
|
|
89
192
|
},
|
|
90
193
|
connect: function connect(webSocketUrl) {
|
|
91
|
-
var
|
|
194
|
+
var _this4 = this;
|
|
92
195
|
if (this.connected) {
|
|
93
196
|
this.logger.info("".concat(this.namespace, ": already connected, will not connect again"));
|
|
94
197
|
return _promise.default.resolve();
|
|
@@ -97,8 +200,8 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
97
200
|
this.logger.info("".concat(this.namespace, ": starting connection attempt"));
|
|
98
201
|
this.logger.info("".concat(this.namespace, ": debug_mercury_logging stack: "), new Error('debug_mercury_logging').stack);
|
|
99
202
|
return _promise.default.resolve(this.webex.internal.device.registered || this.webex.internal.device.register()).then(function () {
|
|
100
|
-
|
|
101
|
-
return
|
|
203
|
+
_this4.logger.info("".concat(_this4.namespace, ": connecting"));
|
|
204
|
+
return _this4._connectWithBackoff(webSocketUrl);
|
|
102
205
|
});
|
|
103
206
|
},
|
|
104
207
|
logout: function logout() {
|
|
@@ -110,16 +213,20 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
110
213
|
} : undefined);
|
|
111
214
|
},
|
|
112
215
|
disconnect: function disconnect(options) {
|
|
113
|
-
var
|
|
216
|
+
var _this5 = this;
|
|
114
217
|
return new _promise.default(function (resolve) {
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
218
|
+
if (_this5.backoffCall) {
|
|
219
|
+
_this5.logger.info("".concat(_this5.namespace, ": aborting connection"));
|
|
220
|
+
_this5.backoffCall.abort();
|
|
221
|
+
}
|
|
222
|
+
if (_this5._shutdownSwitchoverBackoffCall) {
|
|
223
|
+
_this5.logger.info("".concat(_this5.namespace, ": aborting shutdown switchover"));
|
|
224
|
+
_this5._shutdownSwitchoverBackoffCall.abort();
|
|
118
225
|
}
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
resolve(
|
|
226
|
+
if (_this5.socket) {
|
|
227
|
+
_this5.socket.removeAllListeners('message');
|
|
228
|
+
_this5.once('offline', resolve);
|
|
229
|
+
resolve(_this5.socket.close(options || undefined));
|
|
123
230
|
}
|
|
124
231
|
resolve();
|
|
125
232
|
});
|
|
@@ -145,19 +252,19 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
145
252
|
});
|
|
146
253
|
},
|
|
147
254
|
_prepareUrl: function _prepareUrl(webSocketUrl) {
|
|
148
|
-
var
|
|
255
|
+
var _this6 = this;
|
|
149
256
|
if (!webSocketUrl) {
|
|
150
257
|
webSocketUrl = this.webex.internal.device.webSocketUrl;
|
|
151
258
|
}
|
|
152
259
|
return this.webex.internal.feature.getFeature('developer', 'web-high-availability').then(function (haMessagingEnabled) {
|
|
153
260
|
if (haMessagingEnabled) {
|
|
154
|
-
return
|
|
261
|
+
return _this6.webex.internal.services.convertUrlToPriorityHostUrl(webSocketUrl);
|
|
155
262
|
}
|
|
156
263
|
return webSocketUrl;
|
|
157
264
|
}).then(function (wsUrl) {
|
|
158
265
|
webSocketUrl = wsUrl;
|
|
159
266
|
}).then(function () {
|
|
160
|
-
return
|
|
267
|
+
return _this6.webex.internal.feature.getFeature('developer', 'web-shared-mercury');
|
|
161
268
|
}).then(function (webSharedMercury) {
|
|
162
269
|
webSocketUrl = _url.default.parse(webSocketUrl, true);
|
|
163
270
|
(0, _assign.default)(webSocketUrl.query, {
|
|
@@ -172,7 +279,7 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
172
279
|
});
|
|
173
280
|
(0, _deleteProperty.default)(webSocketUrl.query, 'bufferStates');
|
|
174
281
|
}
|
|
175
|
-
if ((0, _lodash.get)(
|
|
282
|
+
if ((0, _lodash.get)(_this6, 'webex.config.device.ephemeral', false)) {
|
|
176
283
|
webSocketUrl.query.multipleConnections = true;
|
|
177
284
|
}
|
|
178
285
|
webSocketUrl.query.clientTimestamp = (0, _now.default)();
|
|
@@ -180,96 +287,94 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
180
287
|
});
|
|
181
288
|
},
|
|
182
289
|
_attemptConnection: function _attemptConnection(socketUrl, callback) {
|
|
183
|
-
var
|
|
290
|
+
var _this7 = this;
|
|
291
|
+
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
292
|
+
var _options$isShutdownSw = options.isShutdownSwitchover,
|
|
293
|
+
isShutdownSwitchover = _options$isShutdownSw === void 0 ? false : _options$isShutdownSw,
|
|
294
|
+
_options$onSuccess = options.onSuccess,
|
|
295
|
+
onSuccess = _options$onSuccess === void 0 ? null : _options$onSuccess;
|
|
184
296
|
var socket = new _socket.default();
|
|
185
|
-
var
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
attemptWSUrl = webSocketUrl;
|
|
217
|
-
var options = {
|
|
218
|
-
forceCloseDelay: _this5.config.forceCloseDelay,
|
|
219
|
-
pingInterval: _this5.config.pingInterval,
|
|
220
|
-
pongTimeout: _this5.config.pongTimeout,
|
|
221
|
-
token: token.toString(),
|
|
222
|
-
trackingId: "".concat(_this5.webex.sessionId, "_").concat((0, _now.default)()),
|
|
223
|
-
logger: _this5.logger
|
|
224
|
-
};
|
|
297
|
+
var newWSUrl;
|
|
298
|
+
this._attachSocketEventListeners(socket);
|
|
299
|
+
|
|
300
|
+
// Check appropriate backoff call based on connection type
|
|
301
|
+
if (isShutdownSwitchover && !this._shutdownSwitchoverBackoffCall) {
|
|
302
|
+
var msg = "".concat(this.namespace, ": prevent socket open when switchover backoff call no longer defined");
|
|
303
|
+
var err = new Error(msg);
|
|
304
|
+
this.logger.info(msg);
|
|
305
|
+
|
|
306
|
+
// Call the callback with the error before rejecting
|
|
307
|
+
callback(err);
|
|
308
|
+
return _promise.default.reject(err);
|
|
309
|
+
}
|
|
310
|
+
if (!isShutdownSwitchover && !this.backoffCall) {
|
|
311
|
+
var _msg = "".concat(this.namespace, ": prevent socket open when backoffCall no longer defined");
|
|
312
|
+
var _err = new Error(_msg);
|
|
313
|
+
this.logger.info(_msg);
|
|
314
|
+
|
|
315
|
+
// Call the callback with the error before rejecting
|
|
316
|
+
callback(_err);
|
|
317
|
+
return _promise.default.reject(_err);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// For shutdown switchover, don't set socket yet (make-before-break)
|
|
321
|
+
// For normal connection, set socket before opening to allow disconnect() to close it
|
|
322
|
+
if (!isShutdownSwitchover) {
|
|
323
|
+
this.socket = socket;
|
|
324
|
+
}
|
|
325
|
+
return this._prepareAndOpenSocket(socket, socketUrl, isShutdownSwitchover).then(function (webSocketUrl) {
|
|
326
|
+
newWSUrl = webSocketUrl;
|
|
327
|
+
_this7.logger.info("".concat(_this7.namespace, ": ").concat(isShutdownSwitchover ? '[shutdown] switchover' : '', " connected to mercury, success, action: connected, url: ").concat(newWSUrl));
|
|
225
328
|
|
|
226
|
-
//
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
329
|
+
// Custom success handler for shutdown switchover
|
|
330
|
+
if (onSuccess) {
|
|
331
|
+
onSuccess(socket, webSocketUrl);
|
|
332
|
+
callback();
|
|
333
|
+
return _promise.default.resolve();
|
|
230
334
|
}
|
|
231
335
|
|
|
232
|
-
//
|
|
233
|
-
// the socket if it is in the process of being opened.
|
|
234
|
-
_this5.socket = socket;
|
|
235
|
-
_this5.logger.info("".concat(_this5.namespace, " connection url: ").concat(webSocketUrl));
|
|
236
|
-
return socket.open(webSocketUrl, options);
|
|
237
|
-
}).then(function () {
|
|
238
|
-
_this5.logger.info("".concat(_this5.namespace, ": connected to mercury, success, action: connected, url: ").concat(attemptWSUrl));
|
|
336
|
+
// Default behavior for normal connection
|
|
239
337
|
callback();
|
|
240
|
-
return
|
|
338
|
+
return _this7.webex.internal.feature.getFeature('developer', 'web-high-availability').then(function (haMessagingEnabled) {
|
|
241
339
|
if (haMessagingEnabled) {
|
|
242
|
-
return
|
|
340
|
+
return _this7.webex.internal.device.refresh();
|
|
243
341
|
}
|
|
244
342
|
return _promise.default.resolve();
|
|
245
343
|
});
|
|
246
344
|
}).catch(function (reason) {
|
|
247
|
-
var
|
|
248
|
-
|
|
345
|
+
var _this7$backoffCall, _this7$backoffCall3;
|
|
346
|
+
// For shutdown, simpler error handling - just callback for retry
|
|
347
|
+
if (isShutdownSwitchover) {
|
|
348
|
+
_this7.logger.info("".concat(_this7.namespace, ": [shutdown] switchover attempt failed"), reason);
|
|
349
|
+
return callback(reason);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Normal connection error handling (existing complex logic)
|
|
353
|
+
_this7.lastError = reason; // remember the last error
|
|
249
354
|
|
|
250
355
|
// Suppress connection errors that appear to be network related. This
|
|
251
356
|
// may end up suppressing metrics during outages, but we might not care
|
|
252
357
|
// (especially since many of our outages happen in a way that client
|
|
253
358
|
// metrics can't be trusted).
|
|
254
|
-
if (reason.code !== 1006 &&
|
|
255
|
-
var
|
|
256
|
-
|
|
257
|
-
retries: (
|
|
359
|
+
if (reason.code !== 1006 && _this7.backoffCall && ((_this7$backoffCall = _this7.backoffCall) === null || _this7$backoffCall === void 0 ? void 0 : _this7$backoffCall.getNumRetries()) > 0) {
|
|
360
|
+
var _this7$backoffCall2;
|
|
361
|
+
_this7._emit('connection_failed', reason, {
|
|
362
|
+
retries: (_this7$backoffCall2 = _this7.backoffCall) === null || _this7$backoffCall2 === void 0 ? void 0 : _this7$backoffCall2.getNumRetries()
|
|
258
363
|
});
|
|
259
364
|
}
|
|
260
|
-
|
|
365
|
+
_this7.logger.info("".concat(_this7.namespace, ": connection attempt failed"), reason, ((_this7$backoffCall3 = _this7.backoffCall) === null || _this7$backoffCall3 === void 0 ? void 0 : _this7$backoffCall3.getNumRetries()) === 0 ? reason.stack : '');
|
|
261
366
|
// UnknownResponse is produced by IE for any 4XXX; treated it like a bad
|
|
262
367
|
// web socket url and let WDM handle the token checking
|
|
263
368
|
if (reason instanceof _errors.UnknownResponse) {
|
|
264
|
-
|
|
265
|
-
return
|
|
369
|
+
_this7.logger.info("".concat(_this7.namespace, ": received unknown response code, refreshing device registration"));
|
|
370
|
+
return _this7.webex.internal.device.refresh().then(function () {
|
|
266
371
|
return callback(reason);
|
|
267
372
|
});
|
|
268
373
|
}
|
|
269
374
|
// NotAuthorized implies expired token
|
|
270
375
|
if (reason instanceof _errors.NotAuthorized) {
|
|
271
|
-
|
|
272
|
-
return
|
|
376
|
+
_this7.logger.info("".concat(_this7.namespace, ": received authorization error, reauthorizing"));
|
|
377
|
+
return _this7.webex.credentials.refresh({
|
|
273
378
|
force: true
|
|
274
379
|
}).then(function () {
|
|
275
380
|
return callback(reason);
|
|
@@ -284,15 +389,15 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
284
389
|
// BadRequest implies current credentials are for a Service Account
|
|
285
390
|
// Forbidden implies current user is not entitle for Webex
|
|
286
391
|
if (reason instanceof _errors.BadRequest || reason instanceof _errors.Forbidden) {
|
|
287
|
-
|
|
288
|
-
|
|
392
|
+
_this7.logger.warn("".concat(_this7.namespace, ": received unrecoverable response from mercury"));
|
|
393
|
+
_this7.backoffCall.abort();
|
|
289
394
|
return callback(reason);
|
|
290
395
|
}
|
|
291
396
|
if (reason instanceof _errors.ConnectionError) {
|
|
292
|
-
return
|
|
397
|
+
return _this7.webex.internal.feature.getFeature('developer', 'web-high-availability').then(function (haMessagingEnabled) {
|
|
293
398
|
if (haMessagingEnabled) {
|
|
294
|
-
|
|
295
|
-
return
|
|
399
|
+
_this7.logger.info("".concat(_this7.namespace, ": received a generic connection error, will try to connect to another datacenter. failed, action: 'failed', url: ").concat(newWSUrl, " error: ").concat(reason.message));
|
|
400
|
+
return _this7.webex.internal.services.markFailedUrl(newWSUrl);
|
|
296
401
|
}
|
|
297
402
|
return null;
|
|
298
403
|
}).then(function () {
|
|
@@ -301,63 +406,116 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
301
406
|
}
|
|
302
407
|
return callback(reason);
|
|
303
408
|
}).catch(function (reason) {
|
|
304
|
-
|
|
409
|
+
_this7.logger.error("".concat(_this7.namespace, ": failed to handle connection failure"), reason);
|
|
305
410
|
callback(reason);
|
|
306
411
|
});
|
|
307
412
|
},
|
|
413
|
+
_prepareAndOpenSocket: function _prepareAndOpenSocket(socket, socketUrl) {
|
|
414
|
+
var _this8 = this;
|
|
415
|
+
var isShutdownSwitchover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
416
|
+
var logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
417
|
+
return _promise.default.all([this._prepareUrl(socketUrl), this.webex.credentials.getUserToken()]).then(function (_ref) {
|
|
418
|
+
var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
|
|
419
|
+
webSocketUrl = _ref2[0],
|
|
420
|
+
token = _ref2[1];
|
|
421
|
+
var options = {
|
|
422
|
+
forceCloseDelay: _this8.config.forceCloseDelay,
|
|
423
|
+
pingInterval: _this8.config.pingInterval,
|
|
424
|
+
pongTimeout: _this8.config.pongTimeout,
|
|
425
|
+
token: token.toString(),
|
|
426
|
+
trackingId: "".concat(_this8.webex.sessionId, "_").concat((0, _now.default)()),
|
|
427
|
+
logger: _this8.logger
|
|
428
|
+
};
|
|
429
|
+
if (_this8.webex.config.defaultMercuryOptions) {
|
|
430
|
+
var customOptionsMsg = isShutdownSwitchover ? 'setting custom options for switchover' : 'setting custom options';
|
|
431
|
+
_this8.logger.info("".concat(_this8.namespace, ": ").concat(customOptionsMsg));
|
|
432
|
+
options = _objectSpread(_objectSpread({}, options), _this8.webex.config.defaultMercuryOptions);
|
|
433
|
+
}
|
|
434
|
+
_this8.logger.info("".concat(_this8.namespace, ": ").concat(logPrefix, " url: ").concat(webSocketUrl));
|
|
435
|
+
return socket.open(webSocketUrl, options).then(function () {
|
|
436
|
+
return webSocketUrl;
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
},
|
|
308
440
|
_connectWithBackoff: function _connectWithBackoff(webSocketUrl) {
|
|
309
|
-
var
|
|
441
|
+
var _this9 = this;
|
|
442
|
+
var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
443
|
+
var _context$isShutdownSw = context.isShutdownSwitchover,
|
|
444
|
+
isShutdownSwitchover = _context$isShutdownSw === void 0 ? false : _context$isShutdownSw,
|
|
445
|
+
_context$attemptOptio = context.attemptOptions,
|
|
446
|
+
attemptOptions = _context$attemptOptio === void 0 ? {} : _context$attemptOptio;
|
|
310
447
|
return new _promise.default(function (resolve, reject) {
|
|
311
448
|
// eslint gets confused about whether or not call is actually used
|
|
312
449
|
// eslint-disable-next-line prefer-const
|
|
313
450
|
var call;
|
|
314
451
|
var onComplete = function onComplete(err) {
|
|
315
|
-
|
|
316
|
-
|
|
452
|
+
// Clear state flags based on connection type
|
|
453
|
+
if (isShutdownSwitchover) {
|
|
454
|
+
_this9._shutdownSwitchoverInProgress = false;
|
|
455
|
+
_this9._shutdownSwitchoverBackoffCall = undefined;
|
|
456
|
+
} else {
|
|
457
|
+
_this9.connecting = false;
|
|
458
|
+
_this9.backoffCall = undefined;
|
|
459
|
+
}
|
|
317
460
|
if (err) {
|
|
318
|
-
|
|
461
|
+
var msg = isShutdownSwitchover ? "[shutdown] switchover failed after ".concat(call.getNumRetries(), " retries") : "failed to connect after ".concat(call.getNumRetries(), " retries");
|
|
462
|
+
_this9.logger.info("".concat(_this9.namespace, ": ").concat(msg, "; log statement about next retry was inaccurate; ").concat(err));
|
|
319
463
|
return reject(err);
|
|
320
464
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
465
|
+
|
|
466
|
+
// Default success handling for normal connections
|
|
467
|
+
if (!isShutdownSwitchover) {
|
|
468
|
+
_this9.connected = true;
|
|
469
|
+
_this9.hasEverConnected = true;
|
|
470
|
+
_this9._emit('online');
|
|
471
|
+
_this9.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(true);
|
|
472
|
+
}
|
|
325
473
|
return resolve();
|
|
326
474
|
};
|
|
327
475
|
|
|
328
476
|
// eslint-disable-next-line prefer-reflect
|
|
329
477
|
call = _backoff.default.call(function (callback) {
|
|
330
|
-
|
|
331
|
-
|
|
478
|
+
var attemptNum = call.getNumRetries();
|
|
479
|
+
var logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
480
|
+
_this9.logger.info("".concat(_this9.namespace, ": executing ").concat(logPrefix, " attempt ").concat(attemptNum));
|
|
481
|
+
_this9._attemptConnection(webSocketUrl, callback, attemptOptions);
|
|
332
482
|
}, onComplete);
|
|
333
483
|
call.setStrategy(new _backoff.default.ExponentialStrategy({
|
|
334
|
-
initialDelay:
|
|
335
|
-
maxDelay:
|
|
484
|
+
initialDelay: _this9.config.backoffTimeReset,
|
|
485
|
+
maxDelay: _this9.config.backoffTimeMax
|
|
336
486
|
}));
|
|
337
|
-
if (
|
|
338
|
-
call.failAfter(
|
|
339
|
-
} else if (
|
|
340
|
-
call.failAfter(
|
|
487
|
+
if (_this9.config.initialConnectionMaxRetries && !_this9.hasEverConnected && !isShutdownSwitchover) {
|
|
488
|
+
call.failAfter(_this9.config.initialConnectionMaxRetries);
|
|
489
|
+
} else if (_this9.config.maxRetries) {
|
|
490
|
+
call.failAfter(_this9.config.maxRetries);
|
|
341
491
|
}
|
|
342
492
|
call.on('abort', function () {
|
|
343
|
-
|
|
344
|
-
|
|
493
|
+
var msg = isShutdownSwitchover ? 'Shutdown Switchover' : 'Connection';
|
|
494
|
+
_this9.logger.info("".concat(_this9.namespace, ": ").concat(msg, " aborted"));
|
|
495
|
+
reject(new Error("Mercury ".concat(msg, " Aborted")));
|
|
345
496
|
});
|
|
346
497
|
call.on('callback', function (err) {
|
|
347
498
|
if (err) {
|
|
348
499
|
var number = call.getNumRetries();
|
|
349
|
-
var delay = Math.min(call.strategy_.nextBackoffDelay_,
|
|
350
|
-
|
|
500
|
+
var delay = Math.min(call.strategy_.nextBackoffDelay_, _this9.config.backoffTimeMax);
|
|
501
|
+
var logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : '';
|
|
502
|
+
_this9.logger.info("".concat(_this9.namespace, ": ").concat(logPrefix, " failed to connect; attempting retry ").concat(number + 1, " in ").concat(delay, " ms"));
|
|
351
503
|
/* istanbul ignore if */
|
|
352
504
|
if (process.env.NODE_ENV === 'development') {
|
|
353
|
-
|
|
505
|
+
_this9.logger.debug("".concat(_this9.namespace, ": "), err, err.stack);
|
|
354
506
|
}
|
|
355
507
|
return;
|
|
356
508
|
}
|
|
357
|
-
|
|
509
|
+
_this9.logger.info("".concat(_this9.namespace, ": connected"));
|
|
358
510
|
});
|
|
511
|
+
|
|
512
|
+
// Store backoff call reference BEFORE starting (so it's available in _attemptConnection)
|
|
513
|
+
if (isShutdownSwitchover) {
|
|
514
|
+
_this9._shutdownSwitchoverBackoffCall = call;
|
|
515
|
+
} else {
|
|
516
|
+
_this9.backoffCall = call;
|
|
517
|
+
}
|
|
359
518
|
call.start();
|
|
360
|
-
_this6.backoffCall = call;
|
|
361
519
|
});
|
|
362
520
|
},
|
|
363
521
|
_emit: function _emit() {
|
|
@@ -388,36 +546,74 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
388
546
|
}
|
|
389
547
|
return handlers;
|
|
390
548
|
},
|
|
391
|
-
_onclose: function _onclose(event) {
|
|
549
|
+
_onclose: function _onclose(event, sourceSocket) {
|
|
392
550
|
// I don't see any way to avoid the complexity or statement count in here.
|
|
393
551
|
/* eslint complexity: [0] */
|
|
394
552
|
|
|
395
553
|
try {
|
|
554
|
+
var isActiveSocket = sourceSocket === this.socket;
|
|
396
555
|
var reason = event.reason && event.reason.toLowerCase();
|
|
397
|
-
var socketUrl
|
|
398
|
-
this.socket
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
556
|
+
var socketUrl;
|
|
557
|
+
if (isActiveSocket && this.socket) {
|
|
558
|
+
// Active socket closed - get URL from current socket reference
|
|
559
|
+
socketUrl = this.socket.url;
|
|
560
|
+
} else if (sourceSocket) {
|
|
561
|
+
// Old socket closed - get URL from the closed socket
|
|
562
|
+
socketUrl = sourceSocket.url;
|
|
563
|
+
}
|
|
564
|
+
if (isActiveSocket) {
|
|
565
|
+
// Only tear down state if the currently active socket closed
|
|
566
|
+
if (this.socket) {
|
|
567
|
+
this.socket.removeAllListeners();
|
|
568
|
+
}
|
|
569
|
+
this.unset('socket');
|
|
570
|
+
this.connected = false;
|
|
571
|
+
this._emit('offline', event);
|
|
572
|
+
this.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(false);
|
|
573
|
+
} else {
|
|
574
|
+
// Old socket closed; do not flip connection state
|
|
575
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] non-active socket closed, code=").concat(event.code));
|
|
576
|
+
// Clean up listeners from old socket now that it's closed
|
|
577
|
+
if (sourceSocket) {
|
|
578
|
+
sourceSocket.removeAllListeners();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
403
581
|
switch (event.code) {
|
|
404
582
|
case 1003:
|
|
405
583
|
// metric: disconnect
|
|
406
584
|
this.logger.info("".concat(this.namespace, ": Mercury service rejected last message; will not reconnect: ").concat(event.reason));
|
|
407
|
-
this._emit('offline.permanent', event);
|
|
585
|
+
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
408
586
|
break;
|
|
409
587
|
case 4000:
|
|
410
588
|
// metric: disconnect
|
|
411
589
|
this.logger.info("".concat(this.namespace, ": socket replaced; will not reconnect"));
|
|
412
|
-
this._emit('offline.replaced', event);
|
|
590
|
+
if (isActiveSocket) this._emit('offline.replaced', event);
|
|
591
|
+
// If not active, nothing to do
|
|
592
|
+
break;
|
|
593
|
+
case 4001:
|
|
594
|
+
// replaced during shutdown
|
|
595
|
+
if (isActiveSocket) {
|
|
596
|
+
// Server closed active socket with 4001, meaning it expected this connection
|
|
597
|
+
// to be replaced, but the switchover in _handleImminentShutdown failed.
|
|
598
|
+
// This is a permanent failure - do not reconnect.
|
|
599
|
+
this.logger.warn("".concat(this.namespace, ": active socket closed with 4001; shutdown switchover failed"));
|
|
600
|
+
this._emit('offline.permanent', event);
|
|
601
|
+
} else {
|
|
602
|
+
// Expected: old socket closed after successful switchover
|
|
603
|
+
this.logger.info("".concat(this.namespace, ": old socket closed with 4001 (replaced during shutdown); no reconnect needed"));
|
|
604
|
+
this._emit('offline.replaced', event);
|
|
605
|
+
}
|
|
413
606
|
break;
|
|
414
607
|
case 1001:
|
|
415
608
|
case 1005:
|
|
416
609
|
case 1006:
|
|
417
610
|
case 1011:
|
|
418
611
|
this.logger.info("".concat(this.namespace, ": socket disconnected; reconnecting"));
|
|
419
|
-
|
|
420
|
-
|
|
612
|
+
if (isActiveSocket) {
|
|
613
|
+
this._emit('offline.transient', event);
|
|
614
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] reconnecting active socket to recover"));
|
|
615
|
+
this._reconnect(socketUrl);
|
|
616
|
+
}
|
|
421
617
|
// metric: disconnect
|
|
422
618
|
// if (code == 1011 && reason !== ping error) metric: unexpected disconnect
|
|
423
619
|
break;
|
|
@@ -426,31 +622,42 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
426
622
|
// 3050 indicates logout form of closure, default to old behavior, use config reason defined by consumer to proceed with the permanent block
|
|
427
623
|
if (normalReconnectReasons.includes(reason)) {
|
|
428
624
|
this.logger.info("".concat(this.namespace, ": socket disconnected; reconnecting"));
|
|
429
|
-
|
|
430
|
-
|
|
625
|
+
if (isActiveSocket) {
|
|
626
|
+
this._emit('offline.transient', event);
|
|
627
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] reconnecting due to normal close"));
|
|
628
|
+
this._reconnect(socketUrl);
|
|
629
|
+
}
|
|
431
630
|
// metric: disconnect
|
|
432
631
|
// if (reason === done forced) metric: force closure
|
|
433
632
|
} else {
|
|
434
633
|
this.logger.info("".concat(this.namespace, ": socket disconnected; will not reconnect: ").concat(event.reason));
|
|
435
|
-
this._emit('offline.permanent', event);
|
|
634
|
+
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
436
635
|
}
|
|
437
636
|
break;
|
|
438
637
|
default:
|
|
439
638
|
this.logger.info("".concat(this.namespace, ": socket disconnected unexpectedly; will not reconnect"));
|
|
440
639
|
// unexpected disconnect
|
|
441
|
-
this._emit('offline.permanent', event);
|
|
640
|
+
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
442
641
|
}
|
|
443
642
|
} catch (error) {
|
|
444
643
|
this.logger.error("".concat(this.namespace, ": error occurred in close handler"), error);
|
|
445
644
|
}
|
|
446
645
|
},
|
|
447
646
|
_onmessage: function _onmessage(event) {
|
|
448
|
-
var
|
|
647
|
+
var _this10 = this;
|
|
449
648
|
this._setTimeOffset(event);
|
|
450
649
|
var envelope = event.data;
|
|
451
650
|
if (process.env.ENABLE_MERCURY_LOGGING) {
|
|
452
651
|
this.logger.debug("".concat(this.namespace, ": message envelope: "), envelope);
|
|
453
652
|
}
|
|
653
|
+
|
|
654
|
+
// Handle shutdown message shape: { type: 'shutdown' }
|
|
655
|
+
if (envelope && envelope.type === 'shutdown') {
|
|
656
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] imminent shutdown message received"));
|
|
657
|
+
this._emit('event:mercury_shutdown_imminent', envelope);
|
|
658
|
+
this._handleImminentShutdown();
|
|
659
|
+
return _promise.default.resolve();
|
|
660
|
+
}
|
|
454
661
|
var data = envelope.data;
|
|
455
662
|
this._applyOverrides(data);
|
|
456
663
|
return this._getEventHandlers(data.eventType).reduce(function (promise, handler) {
|
|
@@ -458,24 +665,24 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
458
665
|
var namespace = handler.namespace,
|
|
459
666
|
name = handler.name;
|
|
460
667
|
return new _promise.default(function (resolve) {
|
|
461
|
-
return resolve((
|
|
668
|
+
return resolve((_this10.webex[namespace] || _this10.webex.internal[namespace])[name](data));
|
|
462
669
|
}).catch(function (reason) {
|
|
463
|
-
return
|
|
670
|
+
return _this10.logger.error("".concat(_this10.namespace, ": error occurred in autowired event handler for ").concat(data.eventType), reason);
|
|
464
671
|
});
|
|
465
672
|
});
|
|
466
673
|
}, _promise.default.resolve()).then(function () {
|
|
467
|
-
|
|
674
|
+
_this10._emit('event', event.data);
|
|
468
675
|
var _data$eventType$split = data.eventType.split('.'),
|
|
469
676
|
_data$eventType$split2 = (0, _slicedToArray2.default)(_data$eventType$split, 1),
|
|
470
677
|
namespace = _data$eventType$split2[0];
|
|
471
678
|
if (namespace === data.eventType) {
|
|
472
|
-
|
|
679
|
+
_this10._emit("event:".concat(namespace), envelope);
|
|
473
680
|
} else {
|
|
474
|
-
|
|
475
|
-
|
|
681
|
+
_this10._emit("event:".concat(namespace), envelope);
|
|
682
|
+
_this10._emit("event:".concat(data.eventType), envelope);
|
|
476
683
|
}
|
|
477
684
|
}).catch(function (reason) {
|
|
478
|
-
|
|
685
|
+
_this10.logger.error("".concat(_this10.namespace, ": error occurred processing socket message"), reason);
|
|
479
686
|
});
|
|
480
687
|
},
|
|
481
688
|
_setTimeOffset: function _setTimeOffset(event) {
|
|
@@ -488,7 +695,7 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
488
695
|
this.logger.info("".concat(this.namespace, ": reconnecting"));
|
|
489
696
|
return this.connect(webSocketUrl);
|
|
490
697
|
},
|
|
491
|
-
version: "3.
|
|
698
|
+
version: "3.10.0"
|
|
492
699
|
}, ((0, _applyDecoratedDescriptor2.default)(_obj, "connect", [_common.oneFlight], (0, _getOwnPropertyDescriptor.default)(_obj, "connect"), _obj), (0, _applyDecoratedDescriptor2.default)(_obj, "disconnect", [_common.oneFlight], (0, _getOwnPropertyDescriptor.default)(_obj, "disconnect"), _obj), (0, _applyDecoratedDescriptor2.default)(_obj, "listen", [_dec], (0, _getOwnPropertyDescriptor.default)(_obj, "listen"), _obj), (0, _applyDecoratedDescriptor2.default)(_obj, "stopListening", [_dec2], (0, _getOwnPropertyDescriptor.default)(_obj, "stopListening"), _obj)), _obj)));
|
|
493
700
|
var _default = exports.default = Mercury;
|
|
494
701
|
//# sourceMappingURL=mercury.js.map
|