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