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