@webex/internal-plugin-mercury 3.11.0 → 3.12.0-next.2
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 +54 -0
- package/dist/mercury.js +388 -198
- package/dist/mercury.js.map +1 -1
- package/dist/socket/constants.js +16 -0
- package/dist/socket/constants.js.map +1 -0
- package/dist/socket/socket-base.js +36 -3
- package/dist/socket/socket-base.js.map +1 -1
- package/package.json +17 -17
- package/src/mercury.js +389 -171
- package/src/socket/constants.js +6 -0
- package/src/socket/socket-base.js +40 -3
- package/test/unit/spec/mercury-events.js +20 -2
- package/test/unit/spec/mercury.js +201 -139
- package/test/unit/spec/socket.js +61 -0
package/dist/mercury.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _Array$from = require("@babel/runtime-corejs2/core-js/array/from");
|
|
4
|
+
var _Symbol = require("@babel/runtime-corejs2/core-js/symbol");
|
|
5
|
+
var _Symbol$iterator = require("@babel/runtime-corejs2/core-js/symbol/iterator");
|
|
6
|
+
var _Array$isArray = require("@babel/runtime-corejs2/core-js/array/is-array");
|
|
3
7
|
var _Object$keys2 = require("@babel/runtime-corejs2/core-js/object/keys");
|
|
4
8
|
var _Object$getOwnPropertySymbols = require("@babel/runtime-corejs2/core-js/object/get-own-property-symbols");
|
|
5
9
|
var _Object$getOwnPropertyDescriptor2 = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");
|
|
@@ -11,12 +15,15 @@ _Object$defineProperty(exports, "__esModule", {
|
|
|
11
15
|
value: true
|
|
12
16
|
});
|
|
13
17
|
exports.default = void 0;
|
|
18
|
+
var _map = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/map"));
|
|
14
19
|
var _now = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/date/now"));
|
|
15
20
|
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
|
|
21
|
+
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
|
|
16
22
|
var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
|
|
17
23
|
var _assign = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/assign"));
|
|
18
24
|
var _deleteProperty = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/reflect/delete-property"));
|
|
19
25
|
var _getOwnPropertyDescriptor = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor"));
|
|
26
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
|
|
20
27
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
|
|
21
28
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
|
|
22
29
|
var _applyDecoratedDescriptor2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/applyDecoratedDescriptor"));
|
|
@@ -34,10 +41,14 @@ var _dec, _dec2, _obj;
|
|
|
34
41
|
*/
|
|
35
42
|
function ownKeys(e, r) { var t = _Object$keys2(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return _Object$getOwnPropertyDescriptor2(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
36
43
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor2(t, r)); }); } return e; }
|
|
44
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof _Symbol && r[_Symbol$iterator] || r["@@iterator"]; if (!t) { if (_Array$isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
45
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? _Array$from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
46
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
37
47
|
var normalReconnectReasons = ['idle', 'done (forced)', 'pong not received', 'pong mismatch'];
|
|
38
48
|
var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mercury#listen(): Use Mercury#connect() instead'), _dec2 = (0, _common.deprecated)('Mercury#stopListening(): Use Mercury#disconnect() instead'), _obj = {
|
|
39
49
|
namespace: 'Mercury',
|
|
40
50
|
lastError: undefined,
|
|
51
|
+
defaultSessionId: 'mercury-default-session',
|
|
41
52
|
session: {
|
|
42
53
|
connected: {
|
|
43
54
|
default: false,
|
|
@@ -51,7 +62,24 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
51
62
|
default: false,
|
|
52
63
|
type: 'boolean'
|
|
53
64
|
},
|
|
54
|
-
|
|
65
|
+
sockets: {
|
|
66
|
+
default: function _default() {
|
|
67
|
+
return new _map.default();
|
|
68
|
+
},
|
|
69
|
+
type: 'object'
|
|
70
|
+
},
|
|
71
|
+
backoffCalls: {
|
|
72
|
+
default: function _default() {
|
|
73
|
+
return new _map.default();
|
|
74
|
+
},
|
|
75
|
+
type: 'object'
|
|
76
|
+
},
|
|
77
|
+
_shutdownSwitchoverBackoffCalls: {
|
|
78
|
+
default: function _default() {
|
|
79
|
+
return new _map.default();
|
|
80
|
+
},
|
|
81
|
+
type: 'object'
|
|
82
|
+
},
|
|
55
83
|
localClusterServiceUrls: 'object',
|
|
56
84
|
mercuryTimeOffset: {
|
|
57
85
|
default: undefined,
|
|
@@ -105,60 +133,70 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
105
133
|
/**
|
|
106
134
|
* Attach event listeners to a socket.
|
|
107
135
|
* @param {Socket} socket - The socket to attach listeners to
|
|
136
|
+
* @param {sessionId} sessionId - The socket related session ID
|
|
108
137
|
* @returns {void}
|
|
109
138
|
*/
|
|
110
|
-
_attachSocketEventListeners: function _attachSocketEventListeners(socket) {
|
|
139
|
+
_attachSocketEventListeners: function _attachSocketEventListeners(socket, sessionId) {
|
|
111
140
|
var _this2 = this;
|
|
112
141
|
socket.on('close', function (event) {
|
|
113
|
-
return _this2._onclose(event, socket);
|
|
142
|
+
return _this2._onclose(sessionId, event, socket);
|
|
114
143
|
});
|
|
115
144
|
socket.on('message', function () {
|
|
116
|
-
|
|
145
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
146
|
+
args[_key] = arguments[_key];
|
|
147
|
+
}
|
|
148
|
+
return _this2._onmessage.apply(_this2, [sessionId].concat(args));
|
|
117
149
|
});
|
|
118
150
|
socket.on('pong', function () {
|
|
119
|
-
|
|
151
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
152
|
+
args[_key2] = arguments[_key2];
|
|
153
|
+
}
|
|
154
|
+
return _this2._setTimeOffset.apply(_this2, [sessionId].concat(args));
|
|
120
155
|
});
|
|
121
156
|
socket.on('sequence-mismatch', function () {
|
|
122
|
-
for (var
|
|
123
|
-
args[
|
|
157
|
+
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
|
|
158
|
+
args[_key3] = arguments[_key3];
|
|
124
159
|
}
|
|
125
|
-
return _this2._emit.apply(_this2, ['sequence-mismatch'].concat(args));
|
|
160
|
+
return _this2._emit.apply(_this2, [sessionId, 'sequence-mismatch'].concat(args));
|
|
126
161
|
});
|
|
127
162
|
socket.on('ping-pong-latency', function () {
|
|
128
|
-
for (var
|
|
129
|
-
args[
|
|
163
|
+
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
|
164
|
+
args[_key4] = arguments[_key4];
|
|
130
165
|
}
|
|
131
|
-
return _this2._emit.apply(_this2, ['ping-pong-latency'].concat(args));
|
|
166
|
+
return _this2._emit.apply(_this2, [sessionId, 'ping-pong-latency'].concat(args));
|
|
132
167
|
});
|
|
133
168
|
},
|
|
134
169
|
/**
|
|
135
170
|
* Handle imminent shutdown by establishing a new connection while keeping
|
|
136
171
|
* the current one alive (make-before-break).
|
|
137
172
|
* Idempotent: will no-op if already in progress.
|
|
173
|
+
* @param {string} sessionId - The session ID for which the shutdown is imminent
|
|
138
174
|
* @returns {void}
|
|
139
175
|
*/
|
|
140
|
-
_handleImminentShutdown: function _handleImminentShutdown() {
|
|
176
|
+
_handleImminentShutdown: function _handleImminentShutdown(sessionId) {
|
|
141
177
|
var _this3 = this;
|
|
178
|
+
var oldSocket = this.sockets.get(sessionId);
|
|
142
179
|
try {
|
|
143
|
-
if
|
|
144
|
-
|
|
180
|
+
// Idempotent: if we already have a switchover backoff call for this session,
|
|
181
|
+
// a switchover is in progress – do nothing.
|
|
182
|
+
if (this._shutdownSwitchoverBackoffCalls.get(sessionId)) {
|
|
183
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] switchover already in progress for ").concat(sessionId));
|
|
145
184
|
return;
|
|
146
185
|
}
|
|
147
|
-
this._shutdownSwitchoverInProgress = true;
|
|
148
186
|
this._shutdownSwitchoverId = "".concat((0, _now.default)());
|
|
149
|
-
this.logger.info("".concat(this.namespace, ": [shutdown] switchover start, id=").concat(this._shutdownSwitchoverId));
|
|
150
|
-
this._connectWithBackoff(undefined, {
|
|
187
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] switchover start, id=").concat(this._shutdownSwitchoverId, " for ").concat(sessionId));
|
|
188
|
+
this._connectWithBackoff(undefined, sessionId, {
|
|
151
189
|
isShutdownSwitchover: true,
|
|
152
190
|
attemptOptions: {
|
|
153
191
|
isShutdownSwitchover: true,
|
|
154
192
|
onSuccess: function onSuccess(newSocket, webSocketUrl) {
|
|
155
|
-
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] switchover connected, url: ").concat(webSocketUrl));
|
|
156
|
-
|
|
193
|
+
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] switchover connected, url: ").concat(webSocketUrl, " for ").concat(sessionId));
|
|
194
|
+
|
|
157
195
|
// Atomically switch active socket reference
|
|
158
|
-
_this3.socket =
|
|
159
|
-
_this3.connected =
|
|
196
|
+
_this3.socket = _this3.sockets.get(_this3.defaultSessionId);
|
|
197
|
+
_this3.connected = _this3.hasConnectedSockets(); // remain connected throughout
|
|
160
198
|
|
|
161
|
-
_this3._emit('event:mercury_shutdown_switchover_complete', {
|
|
199
|
+
_this3._emit(sessionId, 'event:mercury_shutdown_switchover_complete', {
|
|
162
200
|
url: webSocketUrl
|
|
163
201
|
});
|
|
164
202
|
if (oldSocket) {
|
|
@@ -167,18 +205,18 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
167
205
|
}
|
|
168
206
|
}
|
|
169
207
|
}).then(function () {
|
|
170
|
-
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] switchover completed successfully"));
|
|
208
|
+
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] switchover completed successfully for ").concat(sessionId));
|
|
171
209
|
}).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', {
|
|
210
|
+
_this3.logger.info("".concat(_this3.namespace, ": [shutdown] switchover exhausted retries; will fall back to normal reconnection for ").concat(sessionId, ": "), err);
|
|
211
|
+
_this3._emit(sessionId, 'event:mercury_shutdown_switchover_failed', {
|
|
174
212
|
reason: err
|
|
175
213
|
});
|
|
176
214
|
// Old socket will eventually close with 4001, triggering normal reconnection
|
|
177
215
|
});
|
|
178
216
|
} catch (e) {
|
|
179
|
-
this.logger.error("".concat(this.namespace, ": [shutdown] error during switchover"), e);
|
|
180
|
-
this.
|
|
181
|
-
this._emit('event:mercury_shutdown_switchover_failed', {
|
|
217
|
+
this.logger.error("".concat(this.namespace, ": [shutdown] error during switchover for ").concat(sessionId), e);
|
|
218
|
+
this._shutdownSwitchoverBackoffCalls.delete(sessionId);
|
|
219
|
+
this._emit(sessionId, 'event:mercury_shutdown_switchover_failed', {
|
|
182
220
|
reason: e
|
|
183
221
|
});
|
|
184
222
|
}
|
|
@@ -190,45 +228,153 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
190
228
|
getLastError: function getLastError() {
|
|
191
229
|
return this.lastError;
|
|
192
230
|
},
|
|
231
|
+
/**
|
|
232
|
+
* Get all active socket connections
|
|
233
|
+
* @returns {Map} Map of sessionId to socket instances
|
|
234
|
+
*/
|
|
235
|
+
getSockets: function getSockets() {
|
|
236
|
+
return this.sockets;
|
|
237
|
+
},
|
|
238
|
+
/**
|
|
239
|
+
* Get a specific socket by connection ID
|
|
240
|
+
* @param {string} sessionId - The connection identifier
|
|
241
|
+
* @returns {Socket|undefined} The socket instance or undefined if not found
|
|
242
|
+
*/
|
|
243
|
+
getSocket: function getSocket() {
|
|
244
|
+
var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.defaultSessionId;
|
|
245
|
+
return this.sockets.get(sessionId);
|
|
246
|
+
},
|
|
247
|
+
/**
|
|
248
|
+
* Check if a socket is connected
|
|
249
|
+
* @param {string} [sessionId=this.defaultSessionId] - The session identifier
|
|
250
|
+
* @returns {boolean|undefined} True if the socket is connected
|
|
251
|
+
*/
|
|
252
|
+
hasConnectedSockets: function hasConnectedSockets() {
|
|
253
|
+
var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.defaultSessionId;
|
|
254
|
+
var socket = this.sockets.get(sessionId || this.defaultSessionId);
|
|
255
|
+
return socket === null || socket === void 0 ? void 0 : socket.connected;
|
|
256
|
+
},
|
|
257
|
+
/**
|
|
258
|
+
* Check if any sockets are connecting
|
|
259
|
+
* @param {string} [sessionId=this.defaultSessionId] - The session identifier
|
|
260
|
+
* @returns {boolean|undefined} True if the socket is connecting
|
|
261
|
+
*/
|
|
262
|
+
hasConnectingSockets: function hasConnectingSockets() {
|
|
263
|
+
var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.defaultSessionId;
|
|
264
|
+
var socket = this.sockets.get(sessionId || this.defaultSessionId);
|
|
265
|
+
return socket === null || socket === void 0 ? void 0 : socket.connecting;
|
|
266
|
+
},
|
|
267
|
+
/**
|
|
268
|
+
* Connect to Mercury for a specific session.
|
|
269
|
+
* @param {string} [webSocketUrl] - Optional websocket URL override. Falls back to the device websocket URL.
|
|
270
|
+
* @param {string} [sessionId=this.defaultSessionId] - The session identifier for this connection.
|
|
271
|
+
* @returns {Promise<void>} Resolves when connection flow completes for the session.
|
|
272
|
+
*/
|
|
193
273
|
connect: function connect(webSocketUrl) {
|
|
194
274
|
var _this4 = this;
|
|
195
|
-
|
|
196
|
-
|
|
275
|
+
var sessionId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.defaultSessionId;
|
|
276
|
+
if (!this._connectPromises) this._connectPromises = new _map.default();
|
|
277
|
+
|
|
278
|
+
// First check if there's already a connection promise for this session
|
|
279
|
+
if (this._connectPromises.has(sessionId)) {
|
|
280
|
+
this.logger.info("".concat(this.namespace, ": connection ").concat(sessionId, " already in progress, returning existing promise"));
|
|
281
|
+
return this._connectPromises.get(sessionId);
|
|
282
|
+
}
|
|
283
|
+
var sessionSocket = this.sockets.get(sessionId);
|
|
284
|
+
if (sessionSocket !== null && sessionSocket !== void 0 && sessionSocket.connected || sessionSocket !== null && sessionSocket !== void 0 && sessionSocket.connecting) {
|
|
285
|
+
this.logger.info("".concat(this.namespace, ": connection ").concat(sessionId, " already connected, will not connect again"));
|
|
197
286
|
return _promise.default.resolve();
|
|
198
287
|
}
|
|
199
288
|
this.connecting = true;
|
|
200
|
-
this.logger.info("".concat(this.namespace, ": starting connection attempt"));
|
|
289
|
+
this.logger.info("".concat(this.namespace, ": starting connection attempt for ").concat(sessionId));
|
|
201
290
|
this.logger.info("".concat(this.namespace, ": debug_mercury_logging stack: "), new Error('debug_mercury_logging').stack);
|
|
202
|
-
|
|
203
|
-
_this4.logger.info("".concat(_this4.namespace, ": connecting"));
|
|
204
|
-
return _this4._connectWithBackoff(webSocketUrl);
|
|
291
|
+
var connectPromise = _promise.default.resolve(this.webex.internal.device.registered || this.webex.internal.device.register()).then(function () {
|
|
292
|
+
_this4.logger.info("".concat(_this4.namespace, ": connecting ").concat(sessionId));
|
|
293
|
+
return _this4._connectWithBackoff(webSocketUrl, sessionId);
|
|
294
|
+
}).finally(function () {
|
|
295
|
+
_this4._connectPromises.delete(sessionId);
|
|
205
296
|
});
|
|
297
|
+
this._connectPromises.set(sessionId, connectPromise);
|
|
298
|
+
return connectPromise;
|
|
206
299
|
},
|
|
207
300
|
logout: function logout() {
|
|
208
301
|
this.logger.info("".concat(this.namespace, ": logout() called"));
|
|
209
302
|
this.logger.info("".concat(this.namespace, ": debug_mercury_logging stack: "), new Error('debug_mercury_logging').stack);
|
|
210
|
-
return this.
|
|
303
|
+
return this.disconnectAll(this.config.beforeLogoutOptionsCloseReason && !normalReconnectReasons.includes(this.config.beforeLogoutOptionsCloseReason) ? {
|
|
211
304
|
code: 3050,
|
|
212
305
|
reason: this.config.beforeLogoutOptionsCloseReason
|
|
213
306
|
} : undefined);
|
|
214
307
|
},
|
|
308
|
+
/**
|
|
309
|
+
* Disconnect a Mercury socket for a specific session.
|
|
310
|
+
* @param {object} [options] - Optional websocket close options (for example: `{code, reason}`).
|
|
311
|
+
* @param {string} [sessionId=this.defaultSessionId] - The session identifier to disconnect.
|
|
312
|
+
* @returns {Promise<void>} Resolves after disconnect cleanup and close handling are initiated/completed.
|
|
313
|
+
*/
|
|
215
314
|
disconnect: function disconnect(options) {
|
|
216
315
|
var _this5 = this;
|
|
316
|
+
var sessionId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.defaultSessionId;
|
|
317
|
+
this.logger.info("".concat(this.namespace, "#disconnect: connecting state: ").concat(this.connecting, ", connected state: ").concat(this.connected, ", socket exists: ").concat(!!this.socket, ", options: ").concat((0, _stringify.default)(options)));
|
|
217
318
|
return new _promise.default(function (resolve) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
_this5.
|
|
319
|
+
var backoffCall = _this5.backoffCalls.get(sessionId);
|
|
320
|
+
if (backoffCall) {
|
|
321
|
+
_this5.logger.info("".concat(_this5.namespace, ": aborting connection ").concat(sessionId));
|
|
322
|
+
backoffCall.abort();
|
|
323
|
+
_this5.backoffCalls.delete(sessionId);
|
|
324
|
+
}
|
|
325
|
+
var shutdownSwitchoverBackoffCall = _this5._shutdownSwitchoverBackoffCalls.get(sessionId);
|
|
326
|
+
if (shutdownSwitchoverBackoffCall) {
|
|
327
|
+
_this5.logger.info("".concat(_this5.namespace, ": aborting shutdown switchover connection ").concat(sessionId));
|
|
328
|
+
shutdownSwitchoverBackoffCall.abort();
|
|
329
|
+
_this5._shutdownSwitchoverBackoffCalls.delete(sessionId);
|
|
221
330
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
_this5.
|
|
331
|
+
// Clean up any pending connection promises
|
|
332
|
+
if (_this5._connectPromises) {
|
|
333
|
+
_this5._connectPromises.delete(sessionId);
|
|
225
334
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
335
|
+
var sessionSocket = _this5.sockets.get(sessionId);
|
|
336
|
+
var suffix = sessionId === _this5.defaultSessionId ? '' : ":".concat(sessionId);
|
|
337
|
+
if (sessionSocket) {
|
|
338
|
+
sessionSocket.removeAllListeners('message');
|
|
339
|
+
sessionSocket.connecting = false;
|
|
340
|
+
sessionSocket.connected = false;
|
|
341
|
+
_this5.once(sessionId === _this5.defaultSessionId ? 'offline' : "offline".concat(suffix), resolve);
|
|
342
|
+
resolve(sessionSocket.close(options || undefined));
|
|
230
343
|
}
|
|
231
344
|
resolve();
|
|
345
|
+
|
|
346
|
+
// Update overall connected status
|
|
347
|
+
_this5.connected = _this5.hasConnectedSockets();
|
|
348
|
+
});
|
|
349
|
+
},
|
|
350
|
+
/**
|
|
351
|
+
* Disconnect all socket connections
|
|
352
|
+
* @param {object} options - Close options
|
|
353
|
+
* @returns {Promise} Promise that resolves when all connections are closed
|
|
354
|
+
*/
|
|
355
|
+
disconnectAll: function disconnectAll(options) {
|
|
356
|
+
var _this6 = this;
|
|
357
|
+
var disconnectPromises = [];
|
|
358
|
+
var _iterator = _createForOfIteratorHelper(this.sockets.keys()),
|
|
359
|
+
_step;
|
|
360
|
+
try {
|
|
361
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
362
|
+
var sessionId = _step.value;
|
|
363
|
+
disconnectPromises.push(this.disconnect(options, sessionId));
|
|
364
|
+
}
|
|
365
|
+
} catch (err) {
|
|
366
|
+
_iterator.e(err);
|
|
367
|
+
} finally {
|
|
368
|
+
_iterator.f();
|
|
369
|
+
}
|
|
370
|
+
return _promise.default.all(disconnectPromises).then(function () {
|
|
371
|
+
_this6.connected = false;
|
|
372
|
+
_this6.sockets.clear();
|
|
373
|
+
_this6.backoffCalls.clear();
|
|
374
|
+
// Clear connection promises to prevent stale promises
|
|
375
|
+
if (_this6._connectPromises) {
|
|
376
|
+
_this6._connectPromises.clear();
|
|
377
|
+
}
|
|
232
378
|
});
|
|
233
379
|
},
|
|
234
380
|
listen: function listen() {
|
|
@@ -252,7 +398,7 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
252
398
|
});
|
|
253
399
|
},
|
|
254
400
|
_prepareUrl: function _prepareUrl(webSocketUrl) {
|
|
255
|
-
var
|
|
401
|
+
var _this7 = this;
|
|
256
402
|
if (!webSocketUrl) {
|
|
257
403
|
webSocketUrl = this.webex.internal.device.webSocketUrl;
|
|
258
404
|
}
|
|
@@ -260,16 +406,16 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
260
406
|
if (haMessagingEnabled) {
|
|
261
407
|
var highPrioritySocketUrl;
|
|
262
408
|
try {
|
|
263
|
-
highPrioritySocketUrl =
|
|
409
|
+
highPrioritySocketUrl = _this7.webex.internal.services.convertUrlToPriorityHostUrl(webSocketUrl);
|
|
264
410
|
} catch (e) {
|
|
265
|
-
|
|
411
|
+
_this7.logger.warn("".concat(_this7.namespace, ": error converting to high priority url"), e);
|
|
266
412
|
}
|
|
267
413
|
if (!highPrioritySocketUrl) {
|
|
268
414
|
var _url$parse;
|
|
269
415
|
var hostFromUrl = (_url$parse = _url.default.parse(webSocketUrl, true)) === null || _url$parse === void 0 ? void 0 : _url$parse.host;
|
|
270
|
-
var isValidHost =
|
|
416
|
+
var isValidHost = _this7.webex.internal.services.isValidHost(hostFromUrl);
|
|
271
417
|
if (!isValidHost) {
|
|
272
|
-
|
|
418
|
+
_this7.logger.error("".concat(_this7.namespace, ": host ").concat(hostFromUrl, " is not a valid host from host catalog"));
|
|
273
419
|
return '';
|
|
274
420
|
}
|
|
275
421
|
}
|
|
@@ -279,7 +425,7 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
279
425
|
}).then(function (wsUrl) {
|
|
280
426
|
webSocketUrl = wsUrl;
|
|
281
427
|
}).then(function () {
|
|
282
|
-
return
|
|
428
|
+
return _this7.webex.internal.feature.getFeature('developer', 'web-shared-mercury');
|
|
283
429
|
}).then(function (webSharedMercury) {
|
|
284
430
|
if (!webSocketUrl) {
|
|
285
431
|
return '';
|
|
@@ -297,27 +443,31 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
297
443
|
});
|
|
298
444
|
(0, _deleteProperty.default)(webSocketUrl.query, 'bufferStates');
|
|
299
445
|
}
|
|
300
|
-
if ((0, _lodash.get)(
|
|
446
|
+
if ((0, _lodash.get)(_this7, 'webex.config.device.ephemeral', false)) {
|
|
301
447
|
webSocketUrl.query.multipleConnections = true;
|
|
302
448
|
}
|
|
303
449
|
webSocketUrl.query.clientTimestamp = (0, _now.default)();
|
|
450
|
+
delete webSocketUrl.search;
|
|
304
451
|
return _url.default.format(webSocketUrl);
|
|
305
452
|
});
|
|
306
453
|
},
|
|
307
|
-
_attemptConnection: function _attemptConnection(socketUrl, callback) {
|
|
308
|
-
var
|
|
309
|
-
var options = arguments.length >
|
|
454
|
+
_attemptConnection: function _attemptConnection(socketUrl, sessionId, callback) {
|
|
455
|
+
var _this8 = this;
|
|
456
|
+
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
310
457
|
var _options$isShutdownSw = options.isShutdownSwitchover,
|
|
311
458
|
isShutdownSwitchover = _options$isShutdownSw === void 0 ? false : _options$isShutdownSw,
|
|
312
459
|
_options$onSuccess = options.onSuccess,
|
|
313
460
|
onSuccess = _options$onSuccess === void 0 ? null : _options$onSuccess;
|
|
314
461
|
var socket = new _socket.default();
|
|
462
|
+
socket.connecting = true;
|
|
315
463
|
var newWSUrl;
|
|
316
|
-
this._attachSocketEventListeners(socket);
|
|
464
|
+
this._attachSocketEventListeners(socket, sessionId);
|
|
465
|
+
var backoffCall = isShutdownSwitchover ? this._shutdownSwitchoverBackoffCalls.get(sessionId) : this.backoffCalls.get(sessionId);
|
|
317
466
|
|
|
318
467
|
// Check appropriate backoff call based on connection type
|
|
319
|
-
if (
|
|
320
|
-
var
|
|
468
|
+
if (!backoffCall) {
|
|
469
|
+
var mode = isShutdownSwitchover ? 'switchover backoff call' : 'backoffCall';
|
|
470
|
+
var msg = "".concat(this.namespace, ": prevent socket open when ").concat(mode, " no longer defined for ").concat(sessionId);
|
|
321
471
|
var err = new Error(msg);
|
|
322
472
|
this.logger.info(msg);
|
|
323
473
|
|
|
@@ -325,24 +475,15 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
325
475
|
callback(err);
|
|
326
476
|
return _promise.default.reject(err);
|
|
327
477
|
}
|
|
328
|
-
if (!isShutdownSwitchover && !this.backoffCall) {
|
|
329
|
-
var _msg = "".concat(this.namespace, ": prevent socket open when backoffCall no longer defined");
|
|
330
|
-
var _err = new Error(_msg);
|
|
331
|
-
this.logger.info(_msg);
|
|
332
|
-
|
|
333
|
-
// Call the callback with the error before rejecting
|
|
334
|
-
callback(_err);
|
|
335
|
-
return _promise.default.reject(_err);
|
|
336
|
-
}
|
|
337
478
|
|
|
338
479
|
// For shutdown switchover, don't set socket yet (make-before-break)
|
|
339
480
|
// For normal connection, set socket before opening to allow disconnect() to close it
|
|
340
481
|
if (!isShutdownSwitchover) {
|
|
341
|
-
this.
|
|
482
|
+
this.sockets.set(sessionId, socket);
|
|
342
483
|
}
|
|
343
|
-
return this._prepareAndOpenSocket(socket, socketUrl, isShutdownSwitchover).then(function (webSocketUrl) {
|
|
484
|
+
return this._prepareAndOpenSocket(socket, socketUrl, sessionId, isShutdownSwitchover).then(function (webSocketUrl) {
|
|
344
485
|
newWSUrl = webSocketUrl;
|
|
345
|
-
|
|
486
|
+
_this8.logger.info("".concat(_this8.namespace, ": ").concat(isShutdownSwitchover ? '[shutdown] switchover' : '', " connected to mercury, success, action: connected for ").concat(sessionId, ", url: ").concat(newWSUrl));
|
|
346
487
|
|
|
347
488
|
// Custom success handler for shutdown switchover
|
|
348
489
|
if (onSuccess) {
|
|
@@ -353,46 +494,46 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
353
494
|
|
|
354
495
|
// Default behavior for normal connection
|
|
355
496
|
callback();
|
|
356
|
-
return
|
|
497
|
+
return _this8.webex.internal.feature.getFeature('developer', 'web-high-availability').then(function (haMessagingEnabled) {
|
|
357
498
|
if (haMessagingEnabled) {
|
|
358
|
-
return
|
|
499
|
+
return _this8.webex.internal.device.refresh();
|
|
359
500
|
}
|
|
360
501
|
return _promise.default.resolve();
|
|
361
502
|
});
|
|
362
503
|
}).catch(function (reason) {
|
|
363
|
-
var _this7$backoffCall, _this7$backoffCall3;
|
|
364
504
|
// For shutdown, simpler error handling - just callback for retry
|
|
365
505
|
if (isShutdownSwitchover) {
|
|
366
|
-
|
|
506
|
+
_this8.logger.info("".concat(_this8.namespace, ": [shutdown] switchover attempt failed for ").concat(sessionId), reason);
|
|
367
507
|
return callback(reason);
|
|
368
508
|
}
|
|
369
509
|
|
|
370
510
|
// Normal connection error handling (existing complex logic)
|
|
371
|
-
|
|
511
|
+
_this8.lastError = reason; // remember the last error
|
|
372
512
|
|
|
513
|
+
var backoffCallNormal = _this8.backoffCalls.get(sessionId);
|
|
373
514
|
// Suppress connection errors that appear to be network related. This
|
|
374
515
|
// may end up suppressing metrics during outages, but we might not care
|
|
375
516
|
// (especially since many of our outages happen in a way that client
|
|
376
517
|
// metrics can't be trusted).
|
|
377
|
-
if (reason.code !== 1006 &&
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
retries:
|
|
518
|
+
if (reason.code !== 1006 && backoffCallNormal && (backoffCallNormal === null || backoffCallNormal === void 0 ? void 0 : backoffCallNormal.getNumRetries()) > 0) {
|
|
519
|
+
_this8._emit(sessionId, 'connection_failed', reason, {
|
|
520
|
+
sessionId: sessionId,
|
|
521
|
+
retries: backoffCallNormal === null || backoffCallNormal === void 0 ? void 0 : backoffCallNormal.getNumRetries()
|
|
381
522
|
});
|
|
382
523
|
}
|
|
383
|
-
|
|
524
|
+
_this8.logger.info("".concat(_this8.namespace, ": connection attempt failed for ").concat(sessionId), reason, (backoffCallNormal === null || backoffCallNormal === void 0 ? void 0 : backoffCallNormal.getNumRetries()) === 0 ? reason.stack : '');
|
|
384
525
|
// UnknownResponse is produced by IE for any 4XXX; treated it like a bad
|
|
385
526
|
// web socket url and let WDM handle the token checking
|
|
386
527
|
if (reason instanceof _errors.UnknownResponse) {
|
|
387
|
-
|
|
388
|
-
return
|
|
528
|
+
_this8.logger.info("".concat(_this8.namespace, ": received unknown response code for ").concat(sessionId, ", refreshing device registration"));
|
|
529
|
+
return _this8.webex.internal.device.refresh().then(function () {
|
|
389
530
|
return callback(reason);
|
|
390
531
|
});
|
|
391
532
|
}
|
|
392
533
|
// NotAuthorized implies expired token
|
|
393
534
|
if (reason instanceof _errors.NotAuthorized) {
|
|
394
|
-
|
|
395
|
-
return
|
|
535
|
+
_this8.logger.info("".concat(_this8.namespace, ": received authorization error for ").concat(sessionId, ", reauthorizing"));
|
|
536
|
+
return _this8.webex.credentials.refresh({
|
|
396
537
|
force: true
|
|
397
538
|
}).then(function () {
|
|
398
539
|
return callback(reason);
|
|
@@ -407,15 +548,15 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
407
548
|
// BadRequest implies current credentials are for a Service Account
|
|
408
549
|
// Forbidden implies current user is not entitle for Webex
|
|
409
550
|
if (reason instanceof _errors.BadRequest || reason instanceof _errors.Forbidden) {
|
|
410
|
-
|
|
411
|
-
|
|
551
|
+
_this8.logger.warn("".concat(_this8.namespace, ": received unrecoverable response from mercury for ").concat(sessionId));
|
|
552
|
+
backoffCallNormal === null || backoffCallNormal === void 0 ? void 0 : backoffCallNormal.abort();
|
|
412
553
|
return callback(reason);
|
|
413
554
|
}
|
|
414
555
|
if (reason instanceof _errors.ConnectionError) {
|
|
415
|
-
return
|
|
556
|
+
return _this8.webex.internal.feature.getFeature('developer', 'web-high-availability').then(function (haMessagingEnabled) {
|
|
416
557
|
if (haMessagingEnabled) {
|
|
417
|
-
|
|
418
|
-
return
|
|
558
|
+
_this8.logger.info("".concat(_this8.namespace, ": received a generic connection error for ").concat(sessionId, ", will try to connect to another datacenter. failed, action: 'failed', url: ").concat(newWSUrl, " error: ").concat(reason.message));
|
|
559
|
+
return _this8.webex.internal.services.markFailedUrl(newWSUrl);
|
|
419
560
|
}
|
|
420
561
|
return null;
|
|
421
562
|
}).then(function () {
|
|
@@ -424,126 +565,169 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
424
565
|
}
|
|
425
566
|
return callback(reason);
|
|
426
567
|
}).catch(function (reason) {
|
|
427
|
-
|
|
568
|
+
_this8.logger.error("".concat(_this8.namespace, ": failed to handle connection failure for ").concat(sessionId), reason);
|
|
428
569
|
callback(reason);
|
|
429
570
|
});
|
|
430
571
|
},
|
|
431
|
-
_prepareAndOpenSocket: function _prepareAndOpenSocket(socket, socketUrl) {
|
|
432
|
-
var
|
|
433
|
-
var isShutdownSwitchover = arguments.length >
|
|
572
|
+
_prepareAndOpenSocket: function _prepareAndOpenSocket(socket, socketUrl, sessionId) {
|
|
573
|
+
var _this9 = this;
|
|
574
|
+
var isShutdownSwitchover = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
434
575
|
var logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
435
576
|
return _promise.default.all([this._prepareUrl(socketUrl), this.webex.credentials.getUserToken()]).then(function (_ref) {
|
|
436
577
|
var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
|
|
437
578
|
webSocketUrl = _ref2[0],
|
|
438
579
|
token = _ref2[1];
|
|
439
580
|
var options = {
|
|
440
|
-
forceCloseDelay:
|
|
441
|
-
pingInterval:
|
|
442
|
-
pongTimeout:
|
|
581
|
+
forceCloseDelay: _this9.config.forceCloseDelay,
|
|
582
|
+
pingInterval: _this9.config.pingInterval,
|
|
583
|
+
pongTimeout: _this9.config.pongTimeout,
|
|
443
584
|
token: token.toString(),
|
|
444
|
-
trackingId: "".concat(
|
|
445
|
-
logger:
|
|
585
|
+
trackingId: "".concat(_this9.webex.sessionId, "_").concat((0, _now.default)()),
|
|
586
|
+
logger: _this9.logger
|
|
446
587
|
};
|
|
447
|
-
if (
|
|
588
|
+
if (_this9.webex.config.defaultMercuryOptions) {
|
|
448
589
|
var customOptionsMsg = isShutdownSwitchover ? 'setting custom options for switchover' : 'setting custom options';
|
|
449
|
-
|
|
450
|
-
options = _objectSpread(_objectSpread({}, options),
|
|
590
|
+
_this9.logger.info("".concat(_this9.namespace, ": ").concat(customOptionsMsg));
|
|
591
|
+
options = _objectSpread(_objectSpread({}, options), _this9.webex.config.defaultMercuryOptions);
|
|
451
592
|
}
|
|
452
|
-
|
|
593
|
+
|
|
594
|
+
// Set the socket before opening it. This allows a disconnect() to close
|
|
595
|
+
// the socket if it is in the process of being opened.
|
|
596
|
+
_this9.sockets.set(sessionId, socket);
|
|
597
|
+
_this9.socket = _this9.sockets.get(_this9.defaultSessionId);
|
|
598
|
+
_this9.logger.info("".concat(_this9.namespace, " ").concat(logPrefix, " url for ").concat(sessionId, ": ").concat(webSocketUrl));
|
|
453
599
|
return socket.open(webSocketUrl, options).then(function () {
|
|
454
600
|
return webSocketUrl;
|
|
455
601
|
});
|
|
456
602
|
});
|
|
457
603
|
},
|
|
458
|
-
_connectWithBackoff: function _connectWithBackoff(webSocketUrl) {
|
|
459
|
-
var
|
|
460
|
-
var context = arguments.length >
|
|
604
|
+
_connectWithBackoff: function _connectWithBackoff(webSocketUrl, sessionId) {
|
|
605
|
+
var _this0 = this;
|
|
606
|
+
var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
461
607
|
var _context$isShutdownSw = context.isShutdownSwitchover,
|
|
462
608
|
isShutdownSwitchover = _context$isShutdownSw === void 0 ? false : _context$isShutdownSw,
|
|
463
609
|
_context$attemptOptio = context.attemptOptions,
|
|
464
610
|
attemptOptions = _context$attemptOptio === void 0 ? {} : _context$attemptOptio;
|
|
465
611
|
return new _promise.default(function (resolve, reject) {
|
|
466
|
-
// eslint gets confused about whether
|
|
612
|
+
// eslint gets confused about whether call is actually used
|
|
467
613
|
// eslint-disable-next-line prefer-const
|
|
468
614
|
var call;
|
|
469
615
|
var onComplete = function onComplete(err) {
|
|
470
|
-
|
|
616
|
+
var sid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : sessionId;
|
|
471
617
|
if (isShutdownSwitchover) {
|
|
472
|
-
|
|
473
|
-
_this9._shutdownSwitchoverBackoffCall = undefined;
|
|
618
|
+
_this0._shutdownSwitchoverBackoffCalls.delete(sid);
|
|
474
619
|
} else {
|
|
475
|
-
|
|
476
|
-
_this9.backoffCall = undefined;
|
|
620
|
+
_this0.backoffCalls.delete(sid);
|
|
477
621
|
}
|
|
622
|
+
var sessionSocket = _this0.sockets.get(sid);
|
|
478
623
|
if (err) {
|
|
479
624
|
var msg = isShutdownSwitchover ? "[shutdown] switchover failed after ".concat(call.getNumRetries(), " retries") : "failed to connect after ".concat(call.getNumRetries(), " retries");
|
|
480
|
-
|
|
625
|
+
_this0.logger.info("".concat(_this0.namespace, ": ").concat(msg, "; log statement about next retry was inaccurate; ").concat(err));
|
|
626
|
+
if (sessionSocket) {
|
|
627
|
+
sessionSocket.connecting = false;
|
|
628
|
+
sessionSocket.connected = false;
|
|
629
|
+
}
|
|
481
630
|
return reject(err);
|
|
482
631
|
}
|
|
483
632
|
|
|
633
|
+
// Update overall connected status
|
|
634
|
+
if (sessionSocket) {
|
|
635
|
+
sessionSocket.connecting = false;
|
|
636
|
+
sessionSocket.connected = true;
|
|
637
|
+
}
|
|
484
638
|
// Default success handling for normal connections
|
|
485
639
|
if (!isShutdownSwitchover) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
640
|
+
_this0.connecting = _this0.hasConnectingSockets();
|
|
641
|
+
_this0.connected = _this0.hasConnectedSockets();
|
|
642
|
+
_this0.hasEverConnected = true;
|
|
643
|
+
_this0._emit(sid, 'online');
|
|
644
|
+
if (_this0.connected) {
|
|
645
|
+
_this0.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(true);
|
|
646
|
+
}
|
|
490
647
|
}
|
|
491
648
|
return resolve();
|
|
492
649
|
};
|
|
493
|
-
|
|
494
650
|
// eslint-disable-next-line prefer-reflect
|
|
495
651
|
call = _backoff.default.call(function (callback) {
|
|
496
652
|
var attemptNum = call.getNumRetries();
|
|
497
653
|
var logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
},
|
|
654
|
+
_this0.logger.info("".concat(_this0.namespace, ": executing ").concat(logPrefix, " attempt ").concat(attemptNum, " for ").concat(sessionId));
|
|
655
|
+
_this0._attemptConnection(webSocketUrl, sessionId, callback, attemptOptions);
|
|
656
|
+
}, function (err) {
|
|
657
|
+
return onComplete(err, sessionId);
|
|
658
|
+
});
|
|
501
659
|
call.setStrategy(new _backoff.default.ExponentialStrategy({
|
|
502
|
-
initialDelay:
|
|
503
|
-
maxDelay:
|
|
660
|
+
initialDelay: _this0.config.backoffTimeReset,
|
|
661
|
+
maxDelay: _this0.config.backoffTimeMax
|
|
504
662
|
}));
|
|
505
|
-
if (
|
|
506
|
-
call.failAfter(
|
|
507
|
-
} else if (
|
|
508
|
-
call.failAfter(
|
|
663
|
+
if (_this0.config.initialConnectionMaxRetries && !_this0.hasEverConnected && !isShutdownSwitchover) {
|
|
664
|
+
call.failAfter(_this0.config.initialConnectionMaxRetries);
|
|
665
|
+
} else if (_this0.config.maxRetries) {
|
|
666
|
+
call.failAfter(_this0.config.maxRetries);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Store the call BEFORE setting up event handlers to prevent race conditions
|
|
670
|
+
// Store backoff call reference BEFORE starting (so it's available in _attemptConnection)
|
|
671
|
+
if (isShutdownSwitchover) {
|
|
672
|
+
_this0._shutdownSwitchoverBackoffCalls.set(sessionId, call);
|
|
673
|
+
} else {
|
|
674
|
+
_this0.backoffCalls.set(sessionId, call);
|
|
509
675
|
}
|
|
510
676
|
call.on('abort', function () {
|
|
511
677
|
var msg = isShutdownSwitchover ? 'Shutdown Switchover' : 'Connection';
|
|
512
|
-
|
|
513
|
-
reject(new Error("Mercury ".concat(msg, " Aborted")));
|
|
678
|
+
_this0.logger.info("".concat(_this0.namespace, ": ").concat(msg, " aborted for ").concat(sessionId));
|
|
679
|
+
reject(new Error("Mercury ".concat(msg, " Aborted for ").concat(sessionId)));
|
|
514
680
|
});
|
|
515
681
|
call.on('callback', function (err) {
|
|
516
682
|
if (err) {
|
|
517
683
|
var number = call.getNumRetries();
|
|
518
|
-
var delay = Math.min(call.strategy_.nextBackoffDelay_,
|
|
684
|
+
var delay = Math.min(call.strategy_.nextBackoffDelay_, _this0.config.backoffTimeMax);
|
|
519
685
|
var logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : '';
|
|
520
|
-
|
|
686
|
+
_this0.logger.info("".concat(_this0.namespace, ": ").concat(logPrefix, " failed to connect; attempting retry ").concat(number + 1, " in ").concat(delay, " ms for ").concat(sessionId));
|
|
521
687
|
/* istanbul ignore if */
|
|
522
688
|
if (process.env.NODE_ENV === 'development') {
|
|
523
|
-
|
|
689
|
+
_this0.logger.debug("".concat(_this0.namespace, ": "), err, err.stack);
|
|
524
690
|
}
|
|
525
691
|
return;
|
|
526
692
|
}
|
|
527
|
-
|
|
693
|
+
_this0.logger.info("".concat(_this0.namespace, ": connected ").concat(sessionId));
|
|
528
694
|
});
|
|
529
|
-
|
|
530
|
-
// Store backoff call reference BEFORE starting (so it's available in _attemptConnection)
|
|
531
|
-
if (isShutdownSwitchover) {
|
|
532
|
-
_this9._shutdownSwitchoverBackoffCall = call;
|
|
533
|
-
} else {
|
|
534
|
-
_this9.backoffCall = call;
|
|
535
|
-
}
|
|
536
695
|
call.start();
|
|
537
696
|
});
|
|
538
697
|
},
|
|
539
698
|
_emit: function _emit() {
|
|
540
|
-
for (var
|
|
541
|
-
args[
|
|
699
|
+
for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
|
|
700
|
+
args[_key5] = arguments[_key5];
|
|
542
701
|
}
|
|
543
702
|
try {
|
|
544
|
-
|
|
703
|
+
if (!args || args.length === 0) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// New signature: _emit(sessionId, eventName, ...rest)
|
|
708
|
+
// Backwards compatibility: if the first arg isn't a known sessionId (or defaultSessionId),
|
|
709
|
+
// treat the call as the old signature and forward directly to trigger(...)
|
|
710
|
+
var first = args[0],
|
|
711
|
+
second = args[1],
|
|
712
|
+
rest = _arrayLikeToArray(args).slice(2);
|
|
713
|
+
if (typeof first === 'string' && typeof second === 'string') {
|
|
714
|
+
var sessionId = first;
|
|
715
|
+
var eventName = second;
|
|
716
|
+
var suffix = sessionId === this.defaultSessionId ? '' : ":".concat(sessionId);
|
|
717
|
+
this.trigger.apply(this, ["".concat(eventName).concat(suffix)].concat((0, _toConsumableArray2.default)(rest)));
|
|
718
|
+
} else {
|
|
719
|
+
// Old usage: _emit(eventName, ...args)
|
|
720
|
+
this.trigger.apply(this, args);
|
|
721
|
+
}
|
|
545
722
|
} catch (error) {
|
|
546
|
-
|
|
723
|
+
// Safely handle errors without causing additional issues during cleanup
|
|
724
|
+
try {
|
|
725
|
+
this.logger.error("".concat(this.namespace, ": error occurred in event handler:"), error, ' with args: ', args);
|
|
726
|
+
} catch (logError) {
|
|
727
|
+
// If even logging fails, just ignore to prevent cascading errors during cleanup
|
|
728
|
+
// eslint-disable-next-line no-console
|
|
729
|
+
console.error('Mercury _emit error handling failed:', logError);
|
|
730
|
+
}
|
|
547
731
|
}
|
|
548
732
|
},
|
|
549
733
|
_getEventHandlers: function _getEventHandlers(eventType) {
|
|
@@ -564,33 +748,36 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
564
748
|
}
|
|
565
749
|
return handlers;
|
|
566
750
|
},
|
|
567
|
-
_onclose: function _onclose(event, sourceSocket) {
|
|
751
|
+
_onclose: function _onclose(sessionId, event, sourceSocket) {
|
|
568
752
|
// I don't see any way to avoid the complexity or statement count in here.
|
|
569
753
|
/* eslint complexity: [0] */
|
|
570
754
|
|
|
571
755
|
try {
|
|
572
|
-
var isActiveSocket = sourceSocket === this.socket;
|
|
573
756
|
var reason = event.reason && event.reason.toLowerCase();
|
|
757
|
+
var sessionSocket = this.sockets.get(sessionId);
|
|
574
758
|
var socketUrl;
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
} else if (sourceSocket) {
|
|
579
|
-
// Old socket closed - get URL from the closed socket
|
|
759
|
+
event.sessionId = sessionId;
|
|
760
|
+
var isActiveSocket = sourceSocket === sessionSocket;
|
|
761
|
+
if (sourceSocket) {
|
|
580
762
|
socketUrl = sourceSocket.url;
|
|
581
763
|
}
|
|
764
|
+
this.sockets.delete(sessionId);
|
|
582
765
|
if (isActiveSocket) {
|
|
583
766
|
// Only tear down state if the currently active socket closed
|
|
584
|
-
if (
|
|
585
|
-
|
|
767
|
+
if (sessionSocket) {
|
|
768
|
+
sessionSocket.removeAllListeners();
|
|
769
|
+
if (sessionId === this.defaultSessionId) this.unset('socket');
|
|
770
|
+
this._emit(sessionId, 'offline', event);
|
|
771
|
+
}
|
|
772
|
+
// Update overall connected status
|
|
773
|
+
this.connecting = this.hasConnectingSockets();
|
|
774
|
+
this.connected = this.hasConnectedSockets();
|
|
775
|
+
if (!this.connected) {
|
|
776
|
+
this.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(false);
|
|
586
777
|
}
|
|
587
|
-
this.unset('socket');
|
|
588
|
-
this.connected = false;
|
|
589
|
-
this._emit('offline', event);
|
|
590
|
-
this.webex.internal.newMetrics.callDiagnosticMetrics.setMercuryConnectedStatus(false);
|
|
591
778
|
} else {
|
|
592
779
|
// Old socket closed; do not flip connection state
|
|
593
|
-
this.logger.info("".concat(this.namespace, ": [shutdown] non-active socket closed, code=").concat(event.code));
|
|
780
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] non-active socket closed, code=").concat(event.code, " for ").concat(sessionId));
|
|
594
781
|
// Clean up listeners from old socket now that it's closed
|
|
595
782
|
if (sourceSocket) {
|
|
596
783
|
sourceSocket.removeAllListeners();
|
|
@@ -599,13 +786,13 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
599
786
|
switch (event.code) {
|
|
600
787
|
case 1003:
|
|
601
788
|
// metric: disconnect
|
|
602
|
-
this.logger.info("".concat(this.namespace, ": Mercury service rejected last message; will not reconnect: ").concat(event.reason));
|
|
603
|
-
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
789
|
+
this.logger.info("".concat(this.namespace, ": Mercury service rejected last message for ").concat(sessionId, "; will not reconnect: ").concat(event.reason));
|
|
790
|
+
if (isActiveSocket) this._emit(sessionId, 'offline.permanent', event);
|
|
604
791
|
break;
|
|
605
792
|
case 4000:
|
|
606
793
|
// metric: disconnect
|
|
607
|
-
this.logger.info("".concat(this.namespace, ": socket replaced; will not reconnect"));
|
|
608
|
-
if (isActiveSocket) this._emit('offline.replaced', event);
|
|
794
|
+
this.logger.info("".concat(this.namespace, ": socket ").concat(sessionId, " replaced; will not reconnect"));
|
|
795
|
+
if (isActiveSocket) this._emit(sessionId, 'offline.replaced', event);
|
|
609
796
|
// If not active, nothing to do
|
|
610
797
|
break;
|
|
611
798
|
case 4001:
|
|
@@ -614,23 +801,23 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
614
801
|
// Server closed active socket with 4001, meaning it expected this connection
|
|
615
802
|
// to be replaced, but the switchover in _handleImminentShutdown failed.
|
|
616
803
|
// This is a permanent failure - do not reconnect.
|
|
617
|
-
this.logger.warn("".concat(this.namespace, ": active socket closed with 4001; shutdown switchover failed"));
|
|
618
|
-
this._emit('offline.permanent', event);
|
|
804
|
+
this.logger.warn("".concat(this.namespace, ": active socket closed with 4001; shutdown switchover failed for ").concat(sessionId));
|
|
805
|
+
this._emit(sessionId, 'offline.permanent', event);
|
|
619
806
|
} else {
|
|
620
807
|
// Expected: old socket closed after successful switchover
|
|
621
|
-
this.logger.info("".concat(this.namespace, ": old socket closed with 4001 (replaced during shutdown); no reconnect needed"));
|
|
622
|
-
this._emit('offline.replaced', event);
|
|
808
|
+
this.logger.info("".concat(this.namespace, ": old socket closed with 4001 (replaced during shutdown); no reconnect needed for ").concat(sessionId));
|
|
809
|
+
this._emit(sessionId, 'offline.replaced', event);
|
|
623
810
|
}
|
|
624
811
|
break;
|
|
625
812
|
case 1001:
|
|
626
813
|
case 1005:
|
|
627
814
|
case 1006:
|
|
628
815
|
case 1011:
|
|
629
|
-
this.logger.info("".concat(this.namespace, ": socket disconnected; reconnecting"));
|
|
816
|
+
this.logger.info("".concat(this.namespace, ": socket ").concat(sessionId, " disconnected; reconnecting"));
|
|
630
817
|
if (isActiveSocket) {
|
|
631
|
-
this._emit('offline.transient', event);
|
|
632
|
-
this.logger.info("".concat(this.namespace, ": [shutdown] reconnecting active socket to recover"));
|
|
633
|
-
this._reconnect(socketUrl);
|
|
818
|
+
this._emit(sessionId, 'offline.transient', event);
|
|
819
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] reconnecting active socket to recover for ").concat(sessionId));
|
|
820
|
+
this._reconnect(socketUrl, sessionId);
|
|
634
821
|
}
|
|
635
822
|
// metric: disconnect
|
|
636
823
|
// if (code == 1011 && reason !== ping error) metric: unexpected disconnect
|
|
@@ -639,43 +826,45 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
639
826
|
case 3050:
|
|
640
827
|
// 3050 indicates logout form of closure, default to old behavior, use config reason defined by consumer to proceed with the permanent block
|
|
641
828
|
if (normalReconnectReasons.includes(reason)) {
|
|
642
|
-
this.logger.info("".concat(this.namespace, ": socket disconnected; reconnecting"));
|
|
829
|
+
this.logger.info("".concat(this.namespace, ": socket ").concat(sessionId, " disconnected; reconnecting"));
|
|
643
830
|
if (isActiveSocket) {
|
|
644
|
-
this._emit('offline.transient', event);
|
|
645
|
-
this.logger.info("".concat(this.namespace, ": [shutdown] reconnecting due to normal close"));
|
|
646
|
-
this._reconnect(socketUrl);
|
|
831
|
+
this._emit(sessionId, 'offline.transient', event);
|
|
832
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] reconnecting due to normal close for ").concat(sessionId));
|
|
833
|
+
this._reconnect(socketUrl, sessionId);
|
|
647
834
|
}
|
|
648
835
|
// metric: disconnect
|
|
649
836
|
// if (reason === done forced) metric: force closure
|
|
650
837
|
} else {
|
|
651
|
-
this.logger.info("".concat(this.namespace, ": socket disconnected; will not reconnect: ").concat(event.reason));
|
|
652
|
-
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
838
|
+
this.logger.info("".concat(this.namespace, ": socket ").concat(sessionId, " disconnected; will not reconnect: ").concat(event.reason));
|
|
839
|
+
if (isActiveSocket) this._emit(sessionId, 'offline.permanent', event);
|
|
653
840
|
}
|
|
654
841
|
break;
|
|
655
842
|
default:
|
|
656
|
-
this.logger.info("".concat(this.namespace, ": socket disconnected unexpectedly; will not reconnect"));
|
|
843
|
+
this.logger.info("".concat(this.namespace, ": socket ").concat(sessionId, " disconnected unexpectedly; will not reconnect"));
|
|
657
844
|
// unexpected disconnect
|
|
658
|
-
if (isActiveSocket) this._emit('offline.permanent', event);
|
|
845
|
+
if (isActiveSocket) this._emit(sessionId, 'offline.permanent', event);
|
|
659
846
|
}
|
|
660
847
|
} catch (error) {
|
|
661
|
-
this.logger.error("".concat(this.namespace, ": error occurred in close handler"), error);
|
|
848
|
+
this.logger.error("".concat(this.namespace, ": error occurred in close handler for ").concat(sessionId), error);
|
|
662
849
|
}
|
|
663
850
|
},
|
|
664
|
-
_onmessage: function _onmessage(event) {
|
|
665
|
-
var
|
|
666
|
-
this._setTimeOffset(event);
|
|
851
|
+
_onmessage: function _onmessage(sessionId, event) {
|
|
852
|
+
var _this1 = this;
|
|
853
|
+
this._setTimeOffset(sessionId, event);
|
|
667
854
|
var envelope = event.data;
|
|
668
855
|
if (process.env.ENABLE_MERCURY_LOGGING) {
|
|
669
|
-
this.logger.debug("".concat(this.namespace, ": message envelope: "), envelope);
|
|
856
|
+
this.logger.debug("".concat(this.namespace, ": message envelope from ").concat(sessionId, ": "), envelope);
|
|
670
857
|
}
|
|
858
|
+
envelope.sessionId = sessionId;
|
|
671
859
|
|
|
672
860
|
// Handle shutdown message shape: { type: 'shutdown' }
|
|
673
861
|
if (envelope && envelope.type === 'shutdown') {
|
|
674
|
-
this.logger.info("".concat(this.namespace, ": [shutdown] imminent shutdown message received"));
|
|
675
|
-
this._emit('event:mercury_shutdown_imminent', envelope);
|
|
676
|
-
this._handleImminentShutdown();
|
|
862
|
+
this.logger.info("".concat(this.namespace, ": [shutdown] imminent shutdown message received for ").concat(sessionId));
|
|
863
|
+
this._emit(sessionId, 'event:mercury_shutdown_imminent', envelope);
|
|
864
|
+
this._handleImminentShutdown(sessionId);
|
|
677
865
|
return _promise.default.resolve();
|
|
678
866
|
}
|
|
867
|
+
envelope.sessionId = sessionId;
|
|
679
868
|
var data = envelope.data;
|
|
680
869
|
this._applyOverrides(data);
|
|
681
870
|
return this._getEventHandlers(data.eventType).reduce(function (promise, handler) {
|
|
@@ -683,37 +872,38 @@ var Mercury = _webexCore.WebexPlugin.extend((_dec = (0, _common.deprecated)('Mer
|
|
|
683
872
|
var namespace = handler.namespace,
|
|
684
873
|
name = handler.name;
|
|
685
874
|
return new _promise.default(function (resolve) {
|
|
686
|
-
return resolve((
|
|
875
|
+
return resolve((_this1.webex[namespace] || _this1.webex.internal[namespace])[name](data));
|
|
687
876
|
}).catch(function (reason) {
|
|
688
|
-
return
|
|
877
|
+
return _this1.logger.error("".concat(_this1.namespace, ": error occurred in autowired event handler for ").concat(data.eventType, " from ").concat(sessionId), reason);
|
|
689
878
|
});
|
|
690
879
|
});
|
|
691
880
|
}, _promise.default.resolve()).then(function () {
|
|
692
|
-
|
|
881
|
+
_this1._emit(sessionId, 'event', envelope);
|
|
693
882
|
var _data$eventType$split = data.eventType.split('.'),
|
|
694
883
|
_data$eventType$split2 = (0, _slicedToArray2.default)(_data$eventType$split, 1),
|
|
695
884
|
namespace = _data$eventType$split2[0];
|
|
696
885
|
if (namespace === data.eventType) {
|
|
697
|
-
|
|
886
|
+
_this1._emit(sessionId, "event:".concat(namespace), envelope);
|
|
698
887
|
} else {
|
|
699
|
-
|
|
700
|
-
|
|
888
|
+
_this1._emit(sessionId, "event:".concat(namespace), envelope);
|
|
889
|
+
_this1._emit(sessionId, "event:".concat(data.eventType), envelope);
|
|
701
890
|
}
|
|
702
891
|
}).catch(function (reason) {
|
|
703
|
-
|
|
892
|
+
_this1.logger.error("".concat(_this1.namespace, ": error occurred processing socket message from ").concat(sessionId), reason);
|
|
704
893
|
});
|
|
705
894
|
},
|
|
706
|
-
_setTimeOffset: function _setTimeOffset(event) {
|
|
895
|
+
_setTimeOffset: function _setTimeOffset(sessionId, event) {
|
|
707
896
|
var wsWriteTimestamp = event.data.wsWriteTimestamp;
|
|
708
897
|
if (typeof wsWriteTimestamp === 'number' && wsWriteTimestamp > 0) {
|
|
709
898
|
this.mercuryTimeOffset = (0, _now.default)() - wsWriteTimestamp;
|
|
710
899
|
}
|
|
711
900
|
},
|
|
712
901
|
_reconnect: function _reconnect(webSocketUrl) {
|
|
713
|
-
|
|
714
|
-
|
|
902
|
+
var sessionId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.defaultSessionId;
|
|
903
|
+
this.logger.info("".concat(this.namespace, ": reconnecting ").concat(sessionId));
|
|
904
|
+
return this.connect(webSocketUrl, sessionId);
|
|
715
905
|
},
|
|
716
|
-
version: "
|
|
717
|
-
}, (0, _applyDecoratedDescriptor2.default)(_obj, "
|
|
718
|
-
var
|
|
906
|
+
version: "3.12.0-next.2"
|
|
907
|
+
}, (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));
|
|
908
|
+
var _default2 = exports.default = Mercury;
|
|
719
909
|
//# sourceMappingURL=mercury.js.map
|