@webex/calling 3.12.0-mobius-socket.17 → 3.12.0-mobius-socket.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CallingClient/utils/request.js +6 -3
- package/dist/CallingClient/utils/request.js.map +1 -1
- package/dist/CallingClient/utils/request.test.js +2 -2
- package/dist/CallingClient/utils/request.test.js.map +1 -1
- package/dist/CallingClient/utils/types.js.map +1 -1
- package/dist/SDKConnector/types.js.map +1 -1
- package/dist/mobius-socket/config.js +7 -44
- package/dist/mobius-socket/config.js.map +1 -1
- package/dist/mobius-socket/errors.js +25 -21
- package/dist/mobius-socket/errors.js.map +1 -1
- package/dist/mobius-socket/index.js +0 -44
- package/dist/mobius-socket/index.js.map +1 -1
- package/dist/mobius-socket/mobius-socket-events.test.js +20 -48
- package/dist/mobius-socket/mobius-socket-events.test.js.map +1 -1
- package/dist/mobius-socket/mobius-socket.js +302 -701
- package/dist/mobius-socket/mobius-socket.js.map +1 -1
- package/dist/mobius-socket/mobius-socket.test.js +435 -718
- package/dist/mobius-socket/mobius-socket.test.js.map +1 -1
- package/dist/mobius-socket/socket/socket-base.js +70 -78
- package/dist/mobius-socket/socket/socket-base.js.map +1 -1
- package/dist/mobius-socket/socket.test.js +36 -61
- package/dist/mobius-socket/socket.test.js.map +1 -1
- package/dist/mobius-socket/types.js +7 -0
- package/dist/mobius-socket/types.js.map +1 -0
- package/dist/module/CallingClient/utils/request.js +3 -2
- package/dist/module/mobius-socket/config.js +7 -10
- package/dist/module/mobius-socket/errors.js +17 -0
- package/dist/module/mobius-socket/index.js +0 -3
- package/dist/module/mobius-socket/mobius-socket.js +211 -401
- package/dist/module/mobius-socket/socket/socket-base.js +64 -69
- package/dist/module/mobius-socket/types.js +1 -0
- package/dist/types/CallingClient/utils/request.d.ts.map +1 -1
- package/dist/types/CallingClient/utils/types.d.ts +1 -1
- package/dist/types/CallingClient/utils/types.d.ts.map +1 -1
- package/dist/types/SDKConnector/types.d.ts +15 -0
- package/dist/types/SDKConnector/types.d.ts.map +1 -1
- package/dist/types/mobius-socket/config.d.ts +7 -8
- package/dist/types/mobius-socket/config.d.ts.map +1 -1
- package/dist/types/mobius-socket/errors.d.ts +7 -0
- package/dist/types/mobius-socket/errors.d.ts.map +1 -1
- package/dist/types/mobius-socket/index.d.ts +3 -5
- package/dist/types/mobius-socket/index.d.ts.map +1 -1
- package/dist/types/mobius-socket/mobius-socket.d.ts +26 -37
- package/dist/types/mobius-socket/mobius-socket.d.ts.map +1 -1
- package/dist/types/mobius-socket/socket/socket-base.d.ts +10 -10
- package/dist/types/mobius-socket/socket/socket-base.d.ts.map +1 -1
- package/dist/types/mobius-socket/types.d.ts +21 -0
- package/dist/types/mobius-socket/types.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -2,18 +2,13 @@
|
|
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file
|
|
3
3
|
*/
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
|
-
import { camelCase, set } from 'lodash';
|
|
6
5
|
import backoff from 'backoff';
|
|
7
6
|
import Socket from './socket';
|
|
8
|
-
import { BadRequest, Forbidden, NotAuthorized, UnknownResponse
|
|
7
|
+
import { BadRequest, Forbidden, NotAuthorized, UnknownResponse } from './errors';
|
|
9
8
|
const normalReconnectReasons = ['idle', 'done (forced)'];
|
|
10
|
-
const DEFAULT_MOBIUS_WEBSOCKET_SESSION = 'mobius-websocket-session';
|
|
11
9
|
const MOBIUS_SOCKET_NAMESPACE = 'MobiusSocket';
|
|
12
10
|
const TOKEN_REFRESH_INTERVAL_MS = 1 * 60 * 60 * 1000;
|
|
13
11
|
function normalizeMobiusAuthToken(token) {
|
|
14
|
-
if (typeof token !== 'string') {
|
|
15
|
-
return token;
|
|
16
|
-
}
|
|
17
12
|
return token.replace(/^Bearer\s+/i, '');
|
|
18
13
|
}
|
|
19
14
|
class MobiusSocket extends EventEmitter {
|
|
@@ -25,20 +20,16 @@ class MobiusSocket extends EventEmitter {
|
|
|
25
20
|
this.webex = webex;
|
|
26
21
|
this.config = config;
|
|
27
22
|
this.logger = webex.logger || console;
|
|
28
|
-
this.defaultSessionId = DEFAULT_MOBIUS_WEBSOCKET_SESSION;
|
|
29
23
|
this.connected = false;
|
|
30
24
|
this.connecting = false;
|
|
31
25
|
this.hasEverConnected = false;
|
|
32
26
|
this.socket = undefined;
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this._tokenRefreshTimer = undefined;
|
|
40
|
-
this._tokenRefreshInFlight = undefined;
|
|
41
|
-
this._bindInternalEvents();
|
|
27
|
+
this.backoffCall = undefined;
|
|
28
|
+
this.shutdownSwitchoverBackoffCall = undefined;
|
|
29
|
+
this.seenAsyncEventIds = new Map();
|
|
30
|
+
this.connectPromise = undefined;
|
|
31
|
+
this.tokenRefreshTimer = undefined;
|
|
32
|
+
this.tokenRefreshInFlight = undefined;
|
|
42
33
|
}
|
|
43
34
|
off(eventName, listener) {
|
|
44
35
|
if (listener) {
|
|
@@ -47,88 +38,49 @@ class MobiusSocket extends EventEmitter {
|
|
|
47
38
|
this.removeAllListeners(eventName);
|
|
48
39
|
return this;
|
|
49
40
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this.webex.internal.feature.updateFeature(envelope.data.featureToggle);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
this.on('event:ActiveClusterStatusEvent', (envelope) => {
|
|
57
|
-
if (typeof this.webex.internal.services?.switchActiveClusterIds === 'function' &&
|
|
58
|
-
envelope &&
|
|
59
|
-
envelope.data) {
|
|
60
|
-
this.webex.internal.services.switchActiveClusterIds(envelope.data?.activeClusters);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
this.on('event:u2c.cache-invalidation', (envelope) => {
|
|
64
|
-
if (typeof this.webex.internal.services?.invalidateCache === 'function' &&
|
|
65
|
-
envelope &&
|
|
66
|
-
envelope.data) {
|
|
67
|
-
this.webex.internal.services.invalidateCache(envelope.data?.timestamp);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
_attachSocketEventListeners(socket, sessionId) {
|
|
72
|
-
socket.on('close', (event) => this._onclose(sessionId, event, socket));
|
|
73
|
-
socket.on('message', (...args) => this._onmessage(sessionId, ...args));
|
|
74
|
-
socket.on('pong', (...args) => this._setTimeOffset(sessionId, ...args));
|
|
75
|
-
socket.on('sequence-mismatch', (...args) => this._emit(sessionId, 'sequence-mismatch', ...args));
|
|
76
|
-
socket.on('ping-pong-latency', (...args) => this._emit(sessionId, 'ping-pong-latency', ...args));
|
|
41
|
+
attachSocketEventListeners(socket) {
|
|
42
|
+
socket.on('close', (event) => this.onclose(event, socket));
|
|
43
|
+
socket.on('message', (...args) => this.onmessage(...args));
|
|
77
44
|
}
|
|
78
|
-
|
|
79
|
-
let seenAsyncEventIds = this._seenAsyncEventIdsBySession.get(sessionId);
|
|
80
|
-
if (!seenAsyncEventIds) {
|
|
81
|
-
seenAsyncEventIds = new Map();
|
|
82
|
-
this._seenAsyncEventIdsBySession.set(sessionId, seenAsyncEventIds);
|
|
83
|
-
}
|
|
84
|
-
return seenAsyncEventIds;
|
|
85
|
-
}
|
|
86
|
-
_clearSeenAsyncEventIds(sessionId) {
|
|
87
|
-
if (sessionId) {
|
|
88
|
-
this._seenAsyncEventIdsBySession.delete(sessionId);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
this._seenAsyncEventIdsBySession.clear();
|
|
92
|
-
}
|
|
93
|
-
_trackAsyncEventAndShouldSuppressDuplicate(sessionId, envelope) {
|
|
45
|
+
trackAsyncEventAndShouldSuppressDuplicate(envelope) {
|
|
94
46
|
if (envelope?.type !== 'async_event' || !envelope.eventId) {
|
|
95
47
|
return false;
|
|
96
48
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
seenAsyncEventIds.
|
|
101
|
-
|
|
102
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: duplicate async_event suppressed for ${sessionId}, eventId=${envelope.eventId}`);
|
|
49
|
+
if (this.seenAsyncEventIds.has(envelope.eventId)) {
|
|
50
|
+
const previousValue = this.seenAsyncEventIds.get(envelope.eventId);
|
|
51
|
+
this.seenAsyncEventIds.delete(envelope.eventId);
|
|
52
|
+
this.seenAsyncEventIds.set(envelope.eventId, previousValue);
|
|
53
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: duplicate async_event suppressed, eventId=${envelope.eventId}`);
|
|
103
54
|
return true;
|
|
104
55
|
}
|
|
105
|
-
this.logger.
|
|
106
|
-
seenAsyncEventIds.set(envelope.eventId, true);
|
|
107
|
-
if (seenAsyncEventIds.size > this.config.dedupCacheMaxSize) {
|
|
108
|
-
const oldestEventId = seenAsyncEventIds.keys().next().value;
|
|
109
|
-
seenAsyncEventIds.delete(oldestEventId);
|
|
110
|
-
this.logger.
|
|
56
|
+
this.logger.log(`${MOBIUS_SOCKET_NAMESPACE}: tracking async_event, eventId=${envelope.eventId}`);
|
|
57
|
+
this.seenAsyncEventIds.set(envelope.eventId, true);
|
|
58
|
+
if (this.seenAsyncEventIds.size > this.config.dedupCacheMaxSize) {
|
|
59
|
+
const oldestEventId = this.seenAsyncEventIds.keys().next().value;
|
|
60
|
+
this.seenAsyncEventIds.delete(oldestEventId);
|
|
61
|
+
this.logger.log(`${MOBIUS_SOCKET_NAMESPACE}: evicted oldest async_event from dedup cache, eventId=${oldestEventId}`);
|
|
111
62
|
}
|
|
112
63
|
return false;
|
|
113
64
|
}
|
|
114
|
-
|
|
115
|
-
const oldSocket = this.
|
|
65
|
+
handleImminentShutdown() {
|
|
66
|
+
const oldSocket = this.socket;
|
|
116
67
|
try {
|
|
117
|
-
if (this.
|
|
118
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover already in progress
|
|
68
|
+
if (this.shutdownSwitchoverBackoffCall) {
|
|
69
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover already in progress`);
|
|
119
70
|
return;
|
|
120
71
|
}
|
|
121
|
-
|
|
122
|
-
this.
|
|
123
|
-
this._connectWithBackoff(undefined, sessionId, {
|
|
72
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover start`);
|
|
73
|
+
this.connectWithBackoff(undefined, {
|
|
124
74
|
isShutdownSwitchover: true,
|
|
125
75
|
attemptOptions: {
|
|
126
76
|
isShutdownSwitchover: true,
|
|
127
77
|
onSuccess: (newSocket, webSocketUrl) => {
|
|
128
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover connected, url: ${webSocketUrl}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.
|
|
78
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover connected, url: ${webSocketUrl}`);
|
|
79
|
+
newSocket.connecting = false;
|
|
80
|
+
newSocket.connected = true;
|
|
81
|
+
this.socket = newSocket;
|
|
82
|
+
this.connected = true;
|
|
83
|
+
this.emitEvent('event:mobius_shutdown_switchover_complete', {
|
|
132
84
|
url: webSocketUrl,
|
|
133
85
|
});
|
|
134
86
|
if (oldSocket) {
|
|
@@ -138,185 +90,111 @@ class MobiusSocket extends EventEmitter {
|
|
|
138
90
|
},
|
|
139
91
|
})
|
|
140
92
|
.then(() => {
|
|
141
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover completed successfully
|
|
93
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover completed successfully`);
|
|
142
94
|
})
|
|
143
95
|
.catch((err) => {
|
|
144
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover exhausted retries; will fall back to normal reconnection
|
|
145
|
-
this.
|
|
96
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover exhausted retries; will fall back to normal reconnection: `, err);
|
|
97
|
+
this.emitEvent('event:mobius_shutdown_switchover_failed', { reason: err });
|
|
146
98
|
});
|
|
147
99
|
}
|
|
148
100
|
catch (e) {
|
|
149
|
-
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] error during switchover
|
|
150
|
-
this.
|
|
151
|
-
this.
|
|
101
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] error during switchover`, e);
|
|
102
|
+
this.shutdownSwitchoverBackoffCall = undefined;
|
|
103
|
+
this.emitEvent('event:mobius_shutdown_switchover_failed', { reason: e });
|
|
152
104
|
}
|
|
153
105
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
getSockets() {
|
|
158
|
-
return this.sockets;
|
|
159
|
-
}
|
|
160
|
-
getSocket(sessionId = this.defaultSessionId) {
|
|
161
|
-
return this.sockets.get(sessionId);
|
|
162
|
-
}
|
|
163
|
-
getConnectedWebSocketUrl(sessionId = this.defaultSessionId) {
|
|
164
|
-
const socket = this.getSocket(sessionId);
|
|
165
|
-
if (!socket?.connected) {
|
|
106
|
+
getConnectedWebSocketUrl() {
|
|
107
|
+
if (!this.socket?.connected) {
|
|
166
108
|
return undefined;
|
|
167
109
|
}
|
|
168
|
-
return socket.url;
|
|
169
|
-
}
|
|
170
|
-
send(payload, sessionId = this.defaultSessionId) {
|
|
171
|
-
const socket = this.getSocket(sessionId);
|
|
172
|
-
if (!socket || !socket.connected) {
|
|
173
|
-
return Promise.reject(new Error(`Mobius socket is not connected for session ${sessionId}`));
|
|
174
|
-
}
|
|
175
|
-
return socket.send(payload);
|
|
110
|
+
return this.socket.url;
|
|
176
111
|
}
|
|
177
|
-
sendWssRequest(payload,
|
|
112
|
+
sendWssRequest(payload, options = {}) {
|
|
178
113
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
179
114
|
return Promise.reject(new Error('`payload` is required'));
|
|
180
115
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (typeof sessionIdOrOptions === 'string') {
|
|
184
|
-
sessionId = sessionIdOrOptions;
|
|
185
|
-
}
|
|
186
|
-
else if (sessionIdOrOptions && typeof sessionIdOrOptions === 'object') {
|
|
187
|
-
requestOptions = sessionIdOrOptions;
|
|
116
|
+
if (!this.socket || !this.socket.connected) {
|
|
117
|
+
return Promise.reject(new Error('Mobius socket is not connected'));
|
|
188
118
|
}
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
return Promise.reject(new Error(`Mobius socket is not connected for session ${sessionId}`));
|
|
192
|
-
}
|
|
193
|
-
return socket.sendRequest(payload, {
|
|
194
|
-
timeout: requestOptions.timeout,
|
|
119
|
+
const requestConfigOptions = {
|
|
120
|
+
timeout: options.timeout,
|
|
195
121
|
matchesResponse: (response, request) => response?.type === 'response_event' &&
|
|
196
122
|
response?.subtype === request.type &&
|
|
197
123
|
response?.trackingId === request.trackingId,
|
|
198
124
|
getStatusCode: (response) => response?.statusCode,
|
|
199
125
|
getStatusMessage: (response) => response?.statusMessage,
|
|
200
|
-
createError: (response, statusCode, statusMessage) => this.
|
|
201
|
-
createTimeoutError: (request) => this.
|
|
126
|
+
createError: (response, statusCode, statusMessage) => this.createWssResponseError(response, statusCode, statusMessage),
|
|
127
|
+
createTimeoutError: (request) => this.createWssResponseError({
|
|
202
128
|
type: 'response_event',
|
|
203
129
|
subtype: request.type,
|
|
204
130
|
trackingId: request.trackingId,
|
|
205
131
|
}, 408, 'Mobius websocket response timed out'),
|
|
206
|
-
}
|
|
132
|
+
};
|
|
133
|
+
return this.socket.sendRequest(payload, requestConfigOptions);
|
|
207
134
|
}
|
|
208
135
|
isConnected() {
|
|
209
136
|
return this.connected;
|
|
210
137
|
}
|
|
211
|
-
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
for (const socket of this.sockets.values()) {
|
|
216
|
-
if (socket?.connected) {
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
138
|
+
connect(webSocketUrl) {
|
|
139
|
+
if (this.connectPromise) {
|
|
140
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connection already in progress, returning existing promise`);
|
|
141
|
+
return this.connectPromise;
|
|
219
142
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
hasConnectingSockets(sessionId = this.defaultSessionId) {
|
|
223
|
-
const socket = this.sockets.get(sessionId || this.defaultSessionId);
|
|
224
|
-
return Boolean(socket?.connecting);
|
|
225
|
-
}
|
|
226
|
-
connect(webSocketUrl, sessionId = this.defaultSessionId) {
|
|
227
|
-
if (this._connectPromises.has(sessionId)) {
|
|
228
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connection ${sessionId} already in progress, returning existing promise`);
|
|
229
|
-
return this._connectPromises.get(sessionId);
|
|
230
|
-
}
|
|
231
|
-
const sessionSocket = this.sockets.get(sessionId);
|
|
232
|
-
if (sessionSocket?.connected || sessionSocket?.connecting) {
|
|
233
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connection ${sessionId} already connected, will not connect again`);
|
|
143
|
+
if (this.socket?.connected || this.socket?.connecting) {
|
|
144
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: already connected, will not connect again`);
|
|
234
145
|
return Promise.resolve();
|
|
235
146
|
}
|
|
236
147
|
if (webSocketUrl && this.socketUrl && webSocketUrl !== this.socketUrl) {
|
|
237
148
|
this.hasEverConnected = false;
|
|
238
149
|
}
|
|
239
|
-
const resolvedUrl = webSocketUrl || this.socketUrl;
|
|
240
150
|
if (webSocketUrl) {
|
|
241
151
|
this.socketUrl = webSocketUrl;
|
|
242
152
|
}
|
|
243
153
|
this.connecting = true;
|
|
244
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: starting connection attempt
|
|
154
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: starting connection attempt${Number(this.config.initialConnectionMaxRetries) === 0 && !this.hasEverConnected
|
|
245
155
|
? ' (initial retries disabled)'
|
|
246
156
|
: ''}`);
|
|
247
157
|
const connectPromise = Promise.resolve(this.webex.internal.device.registered || this.webex.internal.device.register())
|
|
248
158
|
.then(() => {
|
|
249
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connecting
|
|
250
|
-
return this.
|
|
159
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connecting`);
|
|
160
|
+
return this.connectWithBackoff(this.socketUrl);
|
|
251
161
|
})
|
|
252
162
|
.finally(() => {
|
|
253
|
-
this.
|
|
163
|
+
this.connectPromise = undefined;
|
|
254
164
|
});
|
|
255
|
-
this.
|
|
165
|
+
this.connectPromise = connectPromise;
|
|
256
166
|
return connectPromise;
|
|
257
167
|
}
|
|
258
|
-
|
|
259
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
this.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
this.
|
|
277
|
-
shutdownSwitchoverBackoffCall.abort();
|
|
278
|
-
this._shutdownSwitchoverBackoffCalls.delete(sessionId);
|
|
279
|
-
}
|
|
280
|
-
this._connectPromises.delete(sessionId);
|
|
281
|
-
const sessionSocket = this.sockets.get(sessionId);
|
|
282
|
-
this._clearSeenAsyncEventIds(sessionId);
|
|
283
|
-
if (!sessionSocket) {
|
|
284
|
-
this.connected = this.hasConnectedSockets();
|
|
285
|
-
if (!this.hasConnectedSockets()) {
|
|
286
|
-
this._stopTokenRefreshTimer();
|
|
287
|
-
}
|
|
168
|
+
disconnect(options) {
|
|
169
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}#disconnect: connecting state: ${this.connecting},
|
|
170
|
+
connected state: ${this.connected}, socket exists: ${!!this.socket},
|
|
171
|
+
options: ${JSON.stringify(options)}`);
|
|
172
|
+
if (this.backoffCall) {
|
|
173
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: aborting connection`);
|
|
174
|
+
this.backoffCall.abort();
|
|
175
|
+
this.backoffCall = undefined;
|
|
176
|
+
}
|
|
177
|
+
if (this.shutdownSwitchoverBackoffCall) {
|
|
178
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: aborting shutdown switchover connection`);
|
|
179
|
+
this.shutdownSwitchoverBackoffCall.abort();
|
|
180
|
+
this.shutdownSwitchoverBackoffCall = undefined;
|
|
181
|
+
}
|
|
182
|
+
this.connectPromise = undefined;
|
|
183
|
+
this.seenAsyncEventIds.clear();
|
|
184
|
+
if (!this.socket) {
|
|
185
|
+
this.connected = false;
|
|
186
|
+
this.stopTokenRefreshTimer();
|
|
288
187
|
return Promise.resolve();
|
|
289
188
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return Promise.resolve(
|
|
294
|
-
this.connected = this.hasConnectedSockets();
|
|
295
|
-
if (!this.hasConnectedSockets()) {
|
|
296
|
-
this._stopTokenRefreshTimer();
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
disconnectAll(options) {
|
|
301
|
-
const disconnectPromises = [];
|
|
302
|
-
for (const sessionId of this.sockets.keys()) {
|
|
303
|
-
disconnectPromises.push(this.disconnect(options, sessionId));
|
|
304
|
-
}
|
|
305
|
-
return Promise.all(disconnectPromises).then(() => {
|
|
189
|
+
this.socket.removeAllListeners('message');
|
|
190
|
+
this.socket.connecting = false;
|
|
191
|
+
this.socket.connected = false;
|
|
192
|
+
return Promise.resolve(this.socket.close(options || undefined)).finally(() => {
|
|
306
193
|
this.connected = false;
|
|
307
|
-
this.
|
|
308
|
-
this.sockets.clear();
|
|
309
|
-
this.backoffCalls.clear();
|
|
310
|
-
this._shutdownSwitchoverBackoffCalls.clear();
|
|
311
|
-
this._clearSeenAsyncEventIds();
|
|
312
|
-
this._stopTokenRefreshTimer();
|
|
313
|
-
this._connectPromises.clear();
|
|
194
|
+
this.stopTokenRefreshTimer();
|
|
314
195
|
});
|
|
315
196
|
}
|
|
316
|
-
|
|
317
|
-
this.localClusterServiceUrls = message.localClusterServiceUrls;
|
|
318
|
-
}
|
|
319
|
-
_createWssResponseError(response, statusCode, statusMessage) {
|
|
197
|
+
createWssResponseError(response, statusCode, statusMessage) {
|
|
320
198
|
const error = new Error(statusMessage || `Mobius websocket request failed with status ${statusCode || 'unknown'}`);
|
|
321
199
|
error.name = 'MobiusSocketResponseError';
|
|
322
200
|
error.statusCode = statusCode;
|
|
@@ -325,45 +203,36 @@ class MobiusSocket extends EventEmitter {
|
|
|
325
203
|
error.trackingId = response?.trackingId;
|
|
326
204
|
return error;
|
|
327
205
|
}
|
|
328
|
-
|
|
329
|
-
if (!event || !event.headers) {
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
const headerKeys = Object.keys(event.headers);
|
|
333
|
-
headerKeys.forEach((keyPath) => {
|
|
334
|
-
set(event, keyPath, event.headers[keyPath]);
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
_prepareUrl(webSocketUrl) {
|
|
206
|
+
prepareUrl(webSocketUrl) {
|
|
338
207
|
if (!webSocketUrl) {
|
|
339
208
|
webSocketUrl = this.webex.internal.device.webSocketUrl;
|
|
340
209
|
}
|
|
341
210
|
return Promise.resolve(webSocketUrl);
|
|
342
211
|
}
|
|
343
|
-
|
|
212
|
+
attemptConnection(socketUrl, callback, options = {}) {
|
|
344
213
|
const { isShutdownSwitchover = false, onSuccess = null } = options;
|
|
345
214
|
const socket = new Socket();
|
|
346
215
|
socket.connecting = true;
|
|
347
216
|
let newWSUrl;
|
|
348
|
-
this.
|
|
217
|
+
this.attachSocketEventListeners(socket);
|
|
349
218
|
const backoffCall = isShutdownSwitchover
|
|
350
|
-
? this.
|
|
351
|
-
: this.
|
|
219
|
+
? this.shutdownSwitchoverBackoffCall
|
|
220
|
+
: this.backoffCall;
|
|
352
221
|
if (!backoffCall) {
|
|
353
222
|
const mode = isShutdownSwitchover ? 'switchover backoff call' : 'backoffCall';
|
|
354
|
-
const msg = `${MOBIUS_SOCKET_NAMESPACE}: prevent socket open when ${mode} no longer defined
|
|
223
|
+
const msg = `${MOBIUS_SOCKET_NAMESPACE}: prevent socket open when ${mode} no longer defined`;
|
|
355
224
|
const err = new Error(msg);
|
|
356
225
|
this.logger.info(msg);
|
|
357
226
|
callback(err);
|
|
358
227
|
return Promise.reject(err);
|
|
359
228
|
}
|
|
360
229
|
if (!isShutdownSwitchover) {
|
|
361
|
-
this.
|
|
230
|
+
this.socket = socket;
|
|
362
231
|
}
|
|
363
|
-
return this.
|
|
232
|
+
return this.prepareAndOpenSocket(socket, socketUrl, isShutdownSwitchover)
|
|
364
233
|
.then((webSocketUrl) => {
|
|
365
234
|
newWSUrl = webSocketUrl;
|
|
366
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${isShutdownSwitchover ? '[shutdown] switchover' : ''} connected to mobius socket, success,
|
|
235
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${isShutdownSwitchover ? '[shutdown] switchover' : ''} connected to mobius socket, success, url: ${newWSUrl}`);
|
|
367
236
|
if (onSuccess) {
|
|
368
237
|
onSuccess(socket, webSocketUrl);
|
|
369
238
|
callback();
|
|
@@ -374,48 +243,44 @@ class MobiusSocket extends EventEmitter {
|
|
|
374
243
|
})
|
|
375
244
|
.catch((reason) => {
|
|
376
245
|
if (isShutdownSwitchover) {
|
|
377
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover attempt failed
|
|
246
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover attempt failed`, reason);
|
|
378
247
|
return callback(reason);
|
|
379
248
|
}
|
|
380
|
-
|
|
381
|
-
const backoffCallNormal = this.backoffCalls.get(sessionId);
|
|
249
|
+
const backoffCallNormal = this.backoffCall;
|
|
382
250
|
if (reason.code !== 1006 && backoffCallNormal && backoffCallNormal?.getNumRetries() > 0) {
|
|
383
|
-
this.
|
|
384
|
-
sessionId,
|
|
251
|
+
this.emitEvent('connection_failed', reason, {
|
|
385
252
|
retries: backoffCallNormal?.getNumRetries(),
|
|
386
253
|
});
|
|
387
254
|
}
|
|
388
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connection attempt failed
|
|
255
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connection attempt failed`, reason, backoffCallNormal?.getNumRetries() === 0 ? reason.stack : '');
|
|
389
256
|
if (reason instanceof UnknownResponse) {
|
|
390
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: received unknown response code
|
|
257
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: received unknown response code, refreshing device registration`);
|
|
391
258
|
return this.webex.internal.device.refresh().then(() => callback(reason));
|
|
392
259
|
}
|
|
393
260
|
if (reason instanceof NotAuthorized) {
|
|
394
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: received authorization error
|
|
261
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: received authorization error, reauthorizing`);
|
|
395
262
|
return this.webex.credentials.refresh({ force: true }).then(() => callback(reason));
|
|
396
263
|
}
|
|
397
264
|
if (reason instanceof BadRequest || reason instanceof Forbidden) {
|
|
398
|
-
this.logger.warn(`${MOBIUS_SOCKET_NAMESPACE}: received unrecoverable response from ${MOBIUS_SOCKET_NAMESPACE}
|
|
265
|
+
this.logger.warn(`${MOBIUS_SOCKET_NAMESPACE}: received unrecoverable response from ${MOBIUS_SOCKET_NAMESPACE}`);
|
|
399
266
|
backoffCallNormal?.abort();
|
|
400
267
|
return callback(reason);
|
|
401
268
|
}
|
|
402
269
|
return callback(reason);
|
|
403
270
|
})
|
|
404
271
|
.catch((reason) => {
|
|
405
|
-
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: failed to handle connection failure
|
|
272
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: failed to handle connection failure`, reason);
|
|
406
273
|
callback(reason);
|
|
407
274
|
});
|
|
408
275
|
}
|
|
409
|
-
|
|
276
|
+
prepareAndOpenSocket(socket, socketUrl, isShutdownSwitchover = false) {
|
|
410
277
|
const logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
411
|
-
return Promise.all([this.
|
|
278
|
+
return Promise.all([this.prepareUrl(socketUrl), this.webex.credentials.getUserToken()]).then(([webSocketUrl, token]) => {
|
|
412
279
|
let options = {
|
|
413
280
|
forceCloseDelay: this.config.forceCloseDelay,
|
|
414
281
|
wssResponseTimeout: this.config.wssResponseTimeout,
|
|
415
|
-
skipAckEventId: this.config.skipAckEventId,
|
|
416
|
-
skipAckEventType: this.config.skipAckEventType,
|
|
417
282
|
token: normalizeMobiusAuthToken(token.toString()),
|
|
418
|
-
refreshToken: () => this.
|
|
283
|
+
refreshToken: () => this.refreshToken(),
|
|
419
284
|
trackingId: `${this.webex.sessionId}_${Date.now()}`,
|
|
420
285
|
logger: this.logger,
|
|
421
286
|
};
|
|
@@ -426,13 +291,14 @@ class MobiusSocket extends EventEmitter {
|
|
|
426
291
|
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${customOptionsMsg}`);
|
|
427
292
|
options = { ...options, ...this.webex.config.defaultMobiusSocketOptions };
|
|
428
293
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
294
|
+
if (!isShutdownSwitchover) {
|
|
295
|
+
this.socket = socket;
|
|
296
|
+
}
|
|
297
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE} ${logPrefix} url: ${webSocketUrl}`);
|
|
432
298
|
return socket.open(webSocketUrl, options).then(() => webSocketUrl);
|
|
433
299
|
});
|
|
434
300
|
}
|
|
435
|
-
|
|
301
|
+
connectWithBackoff(webSocketUrl, context = {}) {
|
|
436
302
|
const { isShutdownSwitchover = false, attemptOptions = {} } = context;
|
|
437
303
|
return new Promise((resolve, reject) => {
|
|
438
304
|
let call;
|
|
@@ -441,44 +307,43 @@ class MobiusSocket extends EventEmitter {
|
|
|
441
307
|
? null
|
|
442
308
|
: Number(this.config.initialConnectionMaxRetries);
|
|
443
309
|
const isInitialConnectWithoutRetries = isInitialConnect && initialRetryLimit === 0;
|
|
444
|
-
const onComplete = (err
|
|
310
|
+
const onComplete = (err) => {
|
|
445
311
|
if (isShutdownSwitchover) {
|
|
446
|
-
this.
|
|
312
|
+
this.shutdownSwitchoverBackoffCall = undefined;
|
|
447
313
|
}
|
|
448
314
|
else {
|
|
449
|
-
this.
|
|
315
|
+
this.backoffCall = undefined;
|
|
450
316
|
}
|
|
451
|
-
const sessionSocket = this.sockets.get(sid);
|
|
452
317
|
if (err) {
|
|
453
318
|
const msg = isShutdownSwitchover
|
|
454
319
|
? `[shutdown] switchover failed after ${call.getNumRetries()} retries`
|
|
455
320
|
: `failed to connect after ${call.getNumRetries()} retries`;
|
|
456
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${msg};
|
|
457
|
-
if (
|
|
458
|
-
|
|
459
|
-
|
|
321
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${msg}; ${err}`);
|
|
322
|
+
if (!isShutdownSwitchover && this.socket) {
|
|
323
|
+
this.socket.connecting = false;
|
|
324
|
+
this.socket.connected = false;
|
|
460
325
|
}
|
|
461
326
|
return reject(err);
|
|
462
327
|
}
|
|
463
|
-
if (
|
|
464
|
-
|
|
465
|
-
|
|
328
|
+
if (!isShutdownSwitchover && this.socket) {
|
|
329
|
+
this.socket.connecting = false;
|
|
330
|
+
this.socket.connected = true;
|
|
466
331
|
}
|
|
467
332
|
if (!isShutdownSwitchover) {
|
|
468
|
-
this.connecting =
|
|
469
|
-
this.connected =
|
|
333
|
+
this.connecting = false;
|
|
334
|
+
this.connected = true;
|
|
470
335
|
this.hasEverConnected = true;
|
|
471
|
-
this.
|
|
472
|
-
this.
|
|
336
|
+
this.startTokenRefreshTimer();
|
|
337
|
+
this.emitEvent('online');
|
|
473
338
|
}
|
|
474
339
|
return resolve();
|
|
475
340
|
};
|
|
476
341
|
call = backoff.call((callback) => {
|
|
477
342
|
const attemptNum = call.getNumRetries();
|
|
478
343
|
const attemptLogPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
479
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: executing ${attemptLogPrefix} attempt ${attemptNum}
|
|
480
|
-
this.
|
|
481
|
-
}, (err) => onComplete(err
|
|
344
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: executing ${attemptLogPrefix} attempt ${attemptNum}`);
|
|
345
|
+
this.attemptConnection(webSocketUrl, callback, attemptOptions);
|
|
346
|
+
}, (err) => onComplete(err));
|
|
482
347
|
call.setStrategy(new backoff.ExponentialStrategy({
|
|
483
348
|
initialDelay: this.config.backoffTimeReset,
|
|
484
349
|
maxDelay: this.config.backoffTimeMax,
|
|
@@ -493,94 +358,70 @@ class MobiusSocket extends EventEmitter {
|
|
|
493
358
|
call.failAfter(this.config.maxRetries);
|
|
494
359
|
}
|
|
495
360
|
if (isShutdownSwitchover) {
|
|
496
|
-
this.
|
|
361
|
+
this.shutdownSwitchoverBackoffCall = call;
|
|
497
362
|
}
|
|
498
363
|
else {
|
|
499
|
-
this.
|
|
364
|
+
this.backoffCall = call;
|
|
500
365
|
}
|
|
501
366
|
call.on('abort', () => {
|
|
502
367
|
const msg = isShutdownSwitchover ? 'Shutdown Switchover' : 'Connection';
|
|
503
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${msg} aborted
|
|
504
|
-
reject(new Error(`MobiusSocket ${msg} Aborted
|
|
368
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${msg} aborted`);
|
|
369
|
+
reject(new Error(`MobiusSocket ${msg} Aborted`));
|
|
505
370
|
});
|
|
506
371
|
call.on('callback', (err) => {
|
|
507
372
|
if (err) {
|
|
508
373
|
if (isInitialConnectWithoutRetries) {
|
|
509
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: initial connect failed
|
|
374
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: initial connect failed; retries already disabled`);
|
|
510
375
|
return;
|
|
511
376
|
}
|
|
512
377
|
const number = call.getNumRetries();
|
|
513
378
|
const delay = Math.min(call.strategy_.nextBackoffDelay_, this.config.backoffTimeMax);
|
|
514
379
|
const callbackLogPrefix = isShutdownSwitchover ? '[shutdown] switchover' : '';
|
|
515
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${callbackLogPrefix} failed to connect; attempting retry ${number + 1} in ${delay} ms
|
|
380
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${callbackLogPrefix} failed to connect; attempting retry ${number + 1} in ${delay} ms`);
|
|
516
381
|
if (process.env.NODE_ENV === 'development') {
|
|
517
382
|
this.logger.debug(`${MOBIUS_SOCKET_NAMESPACE}: `, err, err.stack);
|
|
518
383
|
}
|
|
519
384
|
return;
|
|
520
385
|
}
|
|
521
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connected
|
|
386
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connected`);
|
|
522
387
|
});
|
|
523
388
|
call.start();
|
|
524
389
|
});
|
|
525
390
|
}
|
|
526
|
-
|
|
391
|
+
emitEvent(eventName, ...args) {
|
|
527
392
|
try {
|
|
528
|
-
if (!
|
|
393
|
+
if (!eventName) {
|
|
529
394
|
return;
|
|
530
395
|
}
|
|
531
|
-
|
|
532
|
-
this.emit(`${eventName}${suffix}`, ...args);
|
|
396
|
+
this.emit(eventName, ...args);
|
|
533
397
|
}
|
|
534
398
|
catch (error) {
|
|
535
|
-
|
|
536
|
-
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred in event handler:`, error, ' with args: ', [sessionId, eventName, ...args]);
|
|
537
|
-
}
|
|
538
|
-
catch (logError) {
|
|
539
|
-
console.error('MobiusSocket _emit error handling failed:', logError);
|
|
540
|
-
}
|
|
399
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred in event handler:`, error, ' with args: ', [eventName, ...args]);
|
|
541
400
|
}
|
|
542
401
|
}
|
|
543
|
-
|
|
544
|
-
if (!
|
|
545
|
-
return [];
|
|
546
|
-
}
|
|
547
|
-
const [namespace, name] = eventType.split('.');
|
|
548
|
-
const handlers = [];
|
|
549
|
-
if (!this.webex[namespace] && !this.webex.internal[namespace]) {
|
|
550
|
-
return handlers;
|
|
551
|
-
}
|
|
552
|
-
const handlerName = camelCase(`process_${name}_event`);
|
|
553
|
-
if ((this.webex[namespace] || this.webex.internal[namespace])[handlerName]) {
|
|
554
|
-
handlers.push({
|
|
555
|
-
name: handlerName,
|
|
556
|
-
namespace,
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
return handlers;
|
|
560
|
-
}
|
|
561
|
-
_startTokenRefreshTimer() {
|
|
562
|
-
if (this._tokenRefreshTimer || !this.hasConnectedSockets()) {
|
|
402
|
+
startTokenRefreshTimer() {
|
|
403
|
+
if (this.tokenRefreshTimer || !this.connected) {
|
|
563
404
|
return;
|
|
564
405
|
}
|
|
565
|
-
this.
|
|
566
|
-
this.
|
|
406
|
+
this.tokenRefreshTimer = setInterval(() => {
|
|
407
|
+
this.refreshToken().catch((error) => {
|
|
567
408
|
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: periodic token refresh failed`, error);
|
|
568
409
|
});
|
|
569
410
|
}, TOKEN_REFRESH_INTERVAL_MS);
|
|
570
411
|
}
|
|
571
|
-
|
|
572
|
-
if (!this.
|
|
412
|
+
stopTokenRefreshTimer() {
|
|
413
|
+
if (!this.tokenRefreshTimer) {
|
|
573
414
|
return;
|
|
574
415
|
}
|
|
575
|
-
clearInterval(this.
|
|
576
|
-
this.
|
|
416
|
+
clearInterval(this.tokenRefreshTimer);
|
|
417
|
+
this.tokenRefreshTimer = undefined;
|
|
577
418
|
}
|
|
578
|
-
|
|
579
|
-
if (this.
|
|
580
|
-
return this.
|
|
419
|
+
refreshToken() {
|
|
420
|
+
if (this.tokenRefreshInFlight) {
|
|
421
|
+
return this.tokenRefreshInFlight;
|
|
581
422
|
}
|
|
582
|
-
if (!this.
|
|
583
|
-
this.
|
|
423
|
+
if (!this.connected) {
|
|
424
|
+
this.stopTokenRefreshTimer();
|
|
584
425
|
return Promise.resolve();
|
|
585
426
|
}
|
|
586
427
|
const tokenPromise = this.webex.credentials.canRefresh
|
|
@@ -588,176 +429,145 @@ class MobiusSocket extends EventEmitter {
|
|
|
588
429
|
.refresh({ force: true })
|
|
589
430
|
.then(() => this.webex.credentials.getUserToken())
|
|
590
431
|
: this.webex.credentials.getUserToken();
|
|
591
|
-
this.
|
|
432
|
+
this.tokenRefreshInFlight = tokenPromise
|
|
592
433
|
.then((token) => {
|
|
593
434
|
if (!token) {
|
|
594
435
|
throw new Error('Mobius token refresh did not return a token');
|
|
595
436
|
}
|
|
596
437
|
const refreshedToken = normalizeMobiusAuthToken(token.toString());
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
if (socket?.connected) {
|
|
600
|
-
authPayloadPromises.push(socket.refresh(refreshedToken));
|
|
601
|
-
}
|
|
438
|
+
if (this.socket?.connected) {
|
|
439
|
+
return this.socket.refresh(refreshedToken);
|
|
602
440
|
}
|
|
603
|
-
return
|
|
441
|
+
return undefined;
|
|
604
442
|
})
|
|
605
443
|
.catch((error) => {
|
|
606
|
-
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: failed to refresh/re-auth Mobius
|
|
444
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: failed to refresh/re-auth Mobius socket`, error);
|
|
607
445
|
throw error;
|
|
608
446
|
})
|
|
609
447
|
.finally(() => {
|
|
610
|
-
this.
|
|
448
|
+
this.tokenRefreshInFlight = undefined;
|
|
611
449
|
});
|
|
612
|
-
return this.
|
|
450
|
+
return this.tokenRefreshInFlight;
|
|
613
451
|
}
|
|
614
|
-
|
|
452
|
+
onclose(event, sourceSocket) {
|
|
615
453
|
try {
|
|
616
454
|
const reason = event.reason && event.reason.toLowerCase();
|
|
617
|
-
const sessionSocket = this.sockets.get(sessionId);
|
|
618
455
|
let socketUrl;
|
|
619
|
-
|
|
620
|
-
const isActiveSocket = sourceSocket === sessionSocket;
|
|
456
|
+
const isActiveSocket = sourceSocket === this.socket;
|
|
621
457
|
if (sourceSocket) {
|
|
622
458
|
socketUrl = sourceSocket.url;
|
|
623
459
|
}
|
|
624
|
-
this.sockets.delete(sessionId);
|
|
625
460
|
if (isActiveSocket) {
|
|
626
|
-
if (
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
}
|
|
631
|
-
this._emit(sessionId, 'offline', event);
|
|
632
|
-
}
|
|
633
|
-
this.connecting = this.hasConnectingSockets();
|
|
634
|
-
this.connected = this.hasConnectedSockets();
|
|
635
|
-
if (!this.hasConnectedSockets()) {
|
|
636
|
-
this._stopTokenRefreshTimer();
|
|
461
|
+
if (this.socket) {
|
|
462
|
+
this.socket.removeAllListeners();
|
|
463
|
+
this.socket = undefined;
|
|
464
|
+
this.emitEvent('offline', event);
|
|
637
465
|
}
|
|
466
|
+
this.connecting = false;
|
|
467
|
+
this.connected = false;
|
|
468
|
+
this.stopTokenRefreshTimer();
|
|
638
469
|
}
|
|
639
470
|
else {
|
|
640
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] non-active socket closed, code=${event.code}
|
|
471
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] non-active socket closed, code=${event.code}`);
|
|
641
472
|
if (sourceSocket) {
|
|
642
473
|
sourceSocket.removeAllListeners();
|
|
643
474
|
}
|
|
644
475
|
}
|
|
645
476
|
switch (event.code) {
|
|
646
477
|
case 1003:
|
|
647
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: service rejected last message
|
|
478
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: service rejected last message; will not reconnect: ${event.reason}`);
|
|
648
479
|
if (isActiveSocket)
|
|
649
|
-
this.
|
|
480
|
+
this.emitEvent('offline.permanent', event);
|
|
650
481
|
break;
|
|
651
482
|
case 4000:
|
|
652
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket
|
|
483
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket replaced; will not reconnect`);
|
|
653
484
|
if (isActiveSocket)
|
|
654
|
-
this.
|
|
485
|
+
this.emitEvent('offline.replaced', event);
|
|
655
486
|
break;
|
|
656
487
|
case 4001:
|
|
657
488
|
if (isActiveSocket) {
|
|
658
|
-
this.logger.warn(`${MOBIUS_SOCKET_NAMESPACE}: active socket closed with 4001; shutdown switchover failed
|
|
659
|
-
this.
|
|
489
|
+
this.logger.warn(`${MOBIUS_SOCKET_NAMESPACE}: active socket closed with 4001; shutdown switchover failed`);
|
|
490
|
+
this.emitEvent('offline.permanent', event);
|
|
660
491
|
}
|
|
661
492
|
else {
|
|
662
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: old socket closed with 4001 (replaced during shutdown); no reconnect needed
|
|
663
|
-
this.
|
|
493
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: old socket closed with 4001 (replaced during shutdown); no reconnect needed`);
|
|
494
|
+
this.emitEvent('offline.replaced', event);
|
|
664
495
|
}
|
|
665
496
|
break;
|
|
666
497
|
case 1001:
|
|
667
498
|
case 1005:
|
|
668
499
|
case 1006:
|
|
669
500
|
case 1011:
|
|
670
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket
|
|
501
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket disconnected; reconnecting`);
|
|
671
502
|
if (isActiveSocket) {
|
|
672
|
-
this.
|
|
673
|
-
this.
|
|
674
|
-
this._reconnect(socketUrl, sessionId);
|
|
503
|
+
this.emitEvent('offline.transient', event);
|
|
504
|
+
this.reconnect(socketUrl);
|
|
675
505
|
}
|
|
676
506
|
break;
|
|
677
507
|
case 1000:
|
|
678
508
|
case 3050:
|
|
679
509
|
if (normalReconnectReasons.includes(reason)) {
|
|
680
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket
|
|
510
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket disconnected; reconnecting`);
|
|
681
511
|
if (isActiveSocket) {
|
|
682
|
-
this.
|
|
683
|
-
this.
|
|
684
|
-
this._reconnect(socketUrl, sessionId);
|
|
512
|
+
this.emitEvent('offline.transient', event);
|
|
513
|
+
this.reconnect(socketUrl);
|
|
685
514
|
}
|
|
686
515
|
}
|
|
687
516
|
else {
|
|
688
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket
|
|
517
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket disconnected; will not reconnect: ${event.reason}`);
|
|
689
518
|
if (isActiveSocket)
|
|
690
|
-
this.
|
|
519
|
+
this.emitEvent('offline.permanent', event);
|
|
691
520
|
}
|
|
692
521
|
break;
|
|
693
522
|
default:
|
|
694
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket
|
|
523
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket disconnected unexpectedly; will not reconnect`);
|
|
695
524
|
if (isActiveSocket)
|
|
696
|
-
this.
|
|
525
|
+
this.emitEvent('offline.permanent', event);
|
|
697
526
|
}
|
|
698
527
|
}
|
|
699
528
|
catch (error) {
|
|
700
|
-
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred in close handler
|
|
529
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred in close handler`, error);
|
|
701
530
|
}
|
|
702
531
|
}
|
|
703
|
-
|
|
704
|
-
this._setTimeOffset(sessionId, event);
|
|
532
|
+
onmessage(event) {
|
|
705
533
|
const envelope = event.data;
|
|
706
|
-
if (process.env.
|
|
707
|
-
this.logger.debug(`${MOBIUS_SOCKET_NAMESPACE}: message envelope
|
|
534
|
+
if (process.env.ENABLE_MOBIUS_LOGGING) {
|
|
535
|
+
this.logger.debug(`${MOBIUS_SOCKET_NAMESPACE}: message envelope: `, envelope);
|
|
708
536
|
}
|
|
709
|
-
envelope.sessionId = sessionId;
|
|
710
537
|
if (envelope && envelope.type === 'shutdown') {
|
|
711
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] imminent shutdown message received
|
|
712
|
-
this.
|
|
713
|
-
this.
|
|
538
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] imminent shutdown message received`);
|
|
539
|
+
this.emitEvent('event:mobius_shutdown_imminent', envelope);
|
|
540
|
+
this.handleImminentShutdown();
|
|
714
541
|
return Promise.resolve();
|
|
715
542
|
}
|
|
716
|
-
if (this.
|
|
543
|
+
if (this.trackAsyncEventAndShouldSuppressDuplicate(envelope)) {
|
|
717
544
|
return Promise.resolve();
|
|
718
545
|
}
|
|
719
546
|
if (envelope.type) {
|
|
720
|
-
this.
|
|
547
|
+
this.emitEvent(`event:${envelope.type}`, envelope);
|
|
721
548
|
}
|
|
722
|
-
envelope.sessionId = sessionId;
|
|
723
549
|
const data = envelope.data || envelope;
|
|
724
|
-
this._applyOverrides(data);
|
|
725
550
|
const eventType = data?.eventType || envelope.eventType;
|
|
726
551
|
if (!eventType) {
|
|
727
|
-
this.
|
|
552
|
+
this.emitEvent('event', envelope);
|
|
728
553
|
return Promise.resolve();
|
|
729
554
|
}
|
|
730
|
-
|
|
731
|
-
.
|
|
732
|
-
const { namespace, name } = handler;
|
|
733
|
-
return new Promise((resolve) => {
|
|
734
|
-
resolve((this.webex[namespace] || this.webex.internal[namespace])[name](data));
|
|
735
|
-
}).catch((reason) => this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred in autowired event handler for ${eventType} from ${sessionId}`, reason));
|
|
736
|
-
}), Promise.resolve())
|
|
737
|
-
.then(() => {
|
|
738
|
-
this._emit(sessionId, 'event', envelope);
|
|
555
|
+
try {
|
|
556
|
+
this.emitEvent('event', envelope);
|
|
739
557
|
const [namespace] = eventType.split('.');
|
|
740
|
-
|
|
741
|
-
|
|
558
|
+
this.emitEvent(`event:${namespace}`, envelope);
|
|
559
|
+
if (namespace !== eventType) {
|
|
560
|
+
this.emitEvent(`event:${eventType}`, envelope);
|
|
742
561
|
}
|
|
743
|
-
else {
|
|
744
|
-
this._emit(sessionId, `event:${namespace}`, envelope);
|
|
745
|
-
this._emit(sessionId, `event:${eventType}`, envelope);
|
|
746
|
-
}
|
|
747
|
-
})
|
|
748
|
-
.catch((reason) => {
|
|
749
|
-
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred processing socket message from ${sessionId}`, reason);
|
|
750
|
-
});
|
|
751
|
-
}
|
|
752
|
-
_setTimeOffset(sessionId, event) {
|
|
753
|
-
const { wsWriteTimestamp } = event.data;
|
|
754
|
-
if (typeof wsWriteTimestamp === 'number' && wsWriteTimestamp > 0) {
|
|
755
|
-
this.mercuryTimeOffset = Date.now() - wsWriteTimestamp;
|
|
756
562
|
}
|
|
563
|
+
catch (reason) {
|
|
564
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred processing socket message`, reason);
|
|
565
|
+
}
|
|
566
|
+
return Promise.resolve();
|
|
757
567
|
}
|
|
758
|
-
|
|
759
|
-
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: reconnecting
|
|
760
|
-
return this.connect(webSocketUrl || this.socketUrl
|
|
568
|
+
reconnect(webSocketUrl) {
|
|
569
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: reconnecting`);
|
|
570
|
+
return this.connect(webSocketUrl || this.socketUrl);
|
|
761
571
|
}
|
|
762
572
|
}
|
|
763
573
|
export default MobiusSocket;
|