@webex/calling 3.12.0-next.28 → 3.12.0-next.29
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/CallingClient.js +408 -194
- package/dist/CallingClient/CallingClient.js.map +1 -1
- package/dist/CallingClient/CallingClient.test.js +119 -25
- package/dist/CallingClient/CallingClient.test.js.map +1 -1
- package/dist/CallingClient/calling/call.js +11 -8
- package/dist/CallingClient/calling/call.js.map +1 -1
- package/dist/CallingClient/calling/call.test.js +45 -4
- package/dist/CallingClient/calling/call.test.js.map +1 -1
- package/dist/CallingClient/calling/callManager.js +53 -30
- package/dist/CallingClient/calling/callManager.js.map +1 -1
- package/dist/CallingClient/calling/callManager.test.js +35 -0
- package/dist/CallingClient/calling/callManager.test.js.map +1 -1
- package/dist/CallingClient/calling/types.js +2 -0
- package/dist/CallingClient/calling/types.js.map +1 -1
- package/dist/CallingClient/constants.js +20 -2
- package/dist/CallingClient/constants.js.map +1 -1
- package/dist/CallingClient/line/line.test.js +16 -1
- package/dist/CallingClient/line/line.test.js.map +1 -1
- package/dist/CallingClient/registration/register.js +694 -406
- package/dist/CallingClient/registration/register.js.map +1 -1
- package/dist/CallingClient/registration/register.test.js +202 -21
- package/dist/CallingClient/registration/register.test.js.map +1 -1
- package/dist/CallingClient/registration/types.js.map +1 -1
- package/dist/CallingClient/registration/webWorker.js +41 -104
- package/dist/CallingClient/registration/webWorker.js.map +1 -1
- package/dist/CallingClient/registration/webWorker.test.js +39 -153
- package/dist/CallingClient/registration/webWorker.test.js.map +1 -1
- package/dist/CallingClient/registration/webWorkerStr.js +1 -1
- package/dist/CallingClient/registration/webWorkerStr.js.map +1 -1
- package/dist/CallingClient/utils/constants.js +46 -0
- package/dist/CallingClient/utils/constants.js.map +1 -0
- package/dist/CallingClient/utils/index.js +63 -0
- package/dist/CallingClient/utils/index.js.map +1 -0
- package/dist/CallingClient/utils/mobiusSocketMapper.js +122 -0
- package/dist/CallingClient/utils/mobiusSocketMapper.js.map +1 -0
- package/dist/CallingClient/utils/mobiusSocketMapper.test.js +211 -0
- package/dist/CallingClient/utils/mobiusSocketMapper.test.js.map +1 -0
- package/dist/CallingClient/utils/request.js +349 -0
- package/dist/CallingClient/utils/request.js.map +1 -0
- package/dist/CallingClient/utils/request.test.js +881 -0
- package/dist/CallingClient/utils/request.test.js.map +1 -0
- package/dist/CallingClient/utils/types.js +7 -0
- package/dist/CallingClient/utils/types.js.map +1 -0
- package/dist/CallingClient/utils/wsFeatureFlag.js +28 -0
- package/dist/CallingClient/utils/wsFeatureFlag.js.map +1 -0
- package/dist/CallingClient/utils/wsFeatureFlag.test.js +139 -0
- package/dist/CallingClient/utils/wsFeatureFlag.test.js.map +1 -0
- package/dist/Contacts/ContactsClient.test.js +3 -8
- package/dist/Contacts/ContactsClient.test.js.map +1 -1
- package/dist/Metrics/index.js +60 -0
- package/dist/Metrics/index.js.map +1 -1
- package/dist/Metrics/index.test.js +356 -0
- package/dist/Metrics/index.test.js.map +1 -1
- package/dist/Metrics/types.js +11 -1
- package/dist/Metrics/types.js.map +1 -1
- package/dist/SDKConnector/types.js.map +1 -1
- package/dist/common/Utils.js +136 -40
- package/dist/common/Utils.js.map +1 -1
- package/dist/common/testUtil.js +8 -4
- package/dist/common/testUtil.js.map +1 -1
- package/dist/common/types.js +2 -0
- package/dist/common/types.js.map +1 -1
- package/dist/mobius-socket/config.js +24 -0
- package/dist/mobius-socket/config.js.map +1 -0
- package/dist/mobius-socket/errors.js +150 -0
- package/dist/mobius-socket/errors.js.map +1 -0
- package/dist/mobius-socket/index.js +57 -0
- package/dist/mobius-socket/index.js.map +1 -0
- package/dist/mobius-socket/mobius-socket-events.test.js +485 -0
- package/dist/mobius-socket/mobius-socket-events.test.js.map +1 -0
- package/dist/mobius-socket/mobius-socket.js +804 -0
- package/dist/mobius-socket/mobius-socket.js.map +1 -0
- package/dist/mobius-socket/mobius-socket.test.js +1833 -0
- package/dist/mobius-socket/mobius-socket.test.js.map +1 -0
- package/dist/mobius-socket/socket/constants.js +34 -0
- package/dist/mobius-socket/socket/constants.js.map +1 -0
- package/dist/mobius-socket/socket/index.js +15 -0
- package/dist/mobius-socket/socket/index.js.map +1 -0
- package/dist/mobius-socket/socket/socket-base.js +604 -0
- package/dist/mobius-socket/socket/socket-base.js.map +1 -0
- package/dist/mobius-socket/socket/socket.js +19 -0
- package/dist/mobius-socket/socket/socket.js.map +1 -0
- package/dist/mobius-socket/socket/socket.shim.js +26 -0
- package/dist/mobius-socket/socket/socket.shim.js.map +1 -0
- package/dist/mobius-socket/socket/types.js +7 -0
- package/dist/mobius-socket/socket/types.js.map +1 -0
- package/dist/mobius-socket/socket.test.js +727 -0
- package/dist/mobius-socket/socket.test.js.map +1 -0
- package/dist/mobius-socket/test/mocha-helpers.js +23 -0
- package/dist/mobius-socket/test/mocha-helpers.js.map +1 -0
- package/dist/mobius-socket/test/promise-tick.js +28 -0
- package/dist/mobius-socket/test/promise-tick.js.map +1 -0
- package/dist/mobius-socket/types.js +7 -0
- package/dist/mobius-socket/types.js.map +1 -0
- package/dist/module/CallingClient/CallingClient.js +102 -5
- package/dist/module/CallingClient/calling/call.js +9 -6
- package/dist/module/CallingClient/calling/callManager.js +27 -7
- package/dist/module/CallingClient/calling/types.js +2 -0
- package/dist/module/CallingClient/constants.js +18 -0
- package/dist/module/CallingClient/registration/register.js +226 -62
- package/dist/module/CallingClient/registration/webWorker.js +42 -61
- package/dist/module/CallingClient/registration/webWorkerStr.js +47 -82
- package/dist/module/CallingClient/utils/constants.js +30 -0
- package/dist/module/CallingClient/utils/index.js +5 -0
- package/dist/module/CallingClient/utils/mobiusSocketMapper.js +72 -0
- package/dist/module/CallingClient/utils/request.js +162 -0
- package/dist/module/CallingClient/utils/types.js +1 -0
- package/dist/module/CallingClient/utils/wsFeatureFlag.js +12 -0
- package/dist/module/Metrics/index.js +46 -0
- package/dist/module/Metrics/types.js +10 -0
- package/dist/module/common/Utils.js +51 -8
- package/dist/module/common/testUtil.js +5 -1
- package/dist/module/common/types.js +2 -0
- package/dist/module/mobius-socket/config.js +15 -0
- package/dist/module/mobius-socket/errors.js +64 -0
- package/dist/module/mobius-socket/index.js +24 -0
- package/dist/module/mobius-socket/mobius-socket.js +571 -0
- package/dist/module/mobius-socket/socket/constants.js +10 -0
- package/dist/module/mobius-socket/socket/index.js +4 -0
- package/dist/module/mobius-socket/socket/socket-base.js +368 -0
- package/dist/module/mobius-socket/socket/socket.js +9 -0
- package/dist/module/mobius-socket/socket/socket.shim.js +12 -0
- package/dist/module/mobius-socket/socket/types.js +1 -0
- package/dist/module/mobius-socket/types.js +1 -0
- package/dist/types/CallingClient/CallingClient.d.ts +5 -0
- package/dist/types/CallingClient/CallingClient.d.ts.map +1 -1
- package/dist/types/CallingClient/calling/call.d.ts +1 -0
- package/dist/types/CallingClient/calling/call.d.ts.map +1 -1
- package/dist/types/CallingClient/calling/callManager.d.ts +3 -2
- package/dist/types/CallingClient/calling/callManager.d.ts.map +1 -1
- package/dist/types/CallingClient/calling/types.d.ts +21 -9
- package/dist/types/CallingClient/calling/types.d.ts.map +1 -1
- package/dist/types/CallingClient/constants.d.ts +18 -0
- package/dist/types/CallingClient/constants.d.ts.map +1 -1
- package/dist/types/CallingClient/registration/register.d.ts +6 -0
- package/dist/types/CallingClient/registration/register.d.ts.map +1 -1
- package/dist/types/CallingClient/registration/types.d.ts +3 -1
- package/dist/types/CallingClient/registration/types.d.ts.map +1 -1
- package/dist/types/CallingClient/registration/webWorker.d.ts.map +1 -1
- package/dist/types/CallingClient/registration/webWorkerStr.d.ts +1 -1
- package/dist/types/CallingClient/registration/webWorkerStr.d.ts.map +1 -1
- package/dist/types/CallingClient/utils/constants.d.ts +30 -0
- package/dist/types/CallingClient/utils/constants.d.ts.map +1 -0
- package/dist/types/CallingClient/utils/index.d.ts +6 -0
- package/dist/types/CallingClient/utils/index.d.ts.map +1 -0
- package/dist/types/CallingClient/utils/mobiusSocketMapper.d.ts +5 -0
- package/dist/types/CallingClient/utils/mobiusSocketMapper.d.ts.map +1 -0
- package/dist/types/CallingClient/utils/request.d.ts +23 -0
- package/dist/types/CallingClient/utils/request.d.ts.map +1 -0
- package/dist/types/CallingClient/utils/types.d.ts +29 -0
- package/dist/types/CallingClient/utils/types.d.ts.map +1 -0
- package/dist/types/CallingClient/utils/wsFeatureFlag.d.ts +4 -0
- package/dist/types/CallingClient/utils/wsFeatureFlag.d.ts.map +1 -0
- package/dist/types/Metrics/index.d.ts.map +1 -1
- package/dist/types/Metrics/types.d.ts +11 -1
- package/dist/types/Metrics/types.d.ts.map +1 -1
- package/dist/types/SDKConnector/types.d.ts +24 -0
- package/dist/types/SDKConnector/types.d.ts.map +1 -1
- package/dist/types/common/Utils.d.ts +8 -2
- package/dist/types/common/Utils.d.ts.map +1 -1
- package/dist/types/common/testUtil.d.ts +4 -1
- package/dist/types/common/testUtil.d.ts.map +1 -1
- package/dist/types/common/types.d.ts +3 -0
- package/dist/types/common/types.d.ts.map +1 -1
- package/dist/types/mobius-socket/config.d.ts +17 -0
- package/dist/types/mobius-socket/config.d.ts.map +1 -0
- package/dist/types/mobius-socket/errors.d.ts +32 -0
- package/dist/types/mobius-socket/errors.d.ts.map +1 -0
- package/dist/types/mobius-socket/index.d.ts +14 -0
- package/dist/types/mobius-socket/index.d.ts.map +1 -0
- package/dist/types/mobius-socket/mobius-socket.d.ts +48 -0
- package/dist/types/mobius-socket/mobius-socket.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/constants.d.ts +11 -0
- package/dist/types/mobius-socket/socket/constants.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/index.d.ts +5 -0
- package/dist/types/mobius-socket/socket/index.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/socket-base.d.ts +43 -0
- package/dist/types/mobius-socket/socket/socket-base.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/socket.d.ts +6 -0
- package/dist/types/mobius-socket/socket/socket.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/socket.shim.d.ts +6 -0
- package/dist/types/mobius-socket/socket/socket.shim.d.ts.map +1 -0
- package/dist/types/mobius-socket/socket/types.d.ts +61 -0
- package/dist/types/mobius-socket/socket/types.d.ts.map +1 -0
- package/dist/types/mobius-socket/types.d.ts +21 -0
- package/dist/types/mobius-socket/types.d.ts.map +1 -0
- package/package.json +16 -2
- package/src/mobius-socket/socket/socket.shim.ts +22 -0
- package/src/mobius-socket/socket/socket.ts +14 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file
|
|
3
|
+
*/
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
import backoff from 'backoff';
|
|
6
|
+
import Socket from './socket';
|
|
7
|
+
import { BadRequest, Forbidden, NotAuthorized, UnknownResponse } from './errors';
|
|
8
|
+
const normalReconnectReasons = ['idle', 'done (forced)'];
|
|
9
|
+
const MOBIUS_SOCKET_NAMESPACE = 'MobiusSocket';
|
|
10
|
+
const TOKEN_REFRESH_INTERVAL_MS = 1 * 60 * 60 * 1000;
|
|
11
|
+
function normalizeMobiusAuthToken(token) {
|
|
12
|
+
return token.replace(/^Bearer\s+/i, '');
|
|
13
|
+
}
|
|
14
|
+
class MobiusSocket extends EventEmitter {
|
|
15
|
+
webex;
|
|
16
|
+
config;
|
|
17
|
+
logger;
|
|
18
|
+
connected;
|
|
19
|
+
connecting;
|
|
20
|
+
hasEverConnected;
|
|
21
|
+
socket;
|
|
22
|
+
backoffCall;
|
|
23
|
+
shutdownSwitchoverBackoffCall;
|
|
24
|
+
seenAsyncEventIds;
|
|
25
|
+
connectPromise;
|
|
26
|
+
socketUrl;
|
|
27
|
+
tokenRefreshTimer;
|
|
28
|
+
tokenRefreshInFlight;
|
|
29
|
+
constructor(webex, config = {}) {
|
|
30
|
+
super();
|
|
31
|
+
if (!webex) {
|
|
32
|
+
throw new Error('A Webex instance is required when initializing MobiusSocket');
|
|
33
|
+
}
|
|
34
|
+
this.webex = webex;
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.logger = webex.logger || console;
|
|
37
|
+
this.connected = false;
|
|
38
|
+
this.connecting = false;
|
|
39
|
+
this.hasEverConnected = false;
|
|
40
|
+
this.socket = undefined;
|
|
41
|
+
this.backoffCall = undefined;
|
|
42
|
+
this.shutdownSwitchoverBackoffCall = undefined;
|
|
43
|
+
this.seenAsyncEventIds = new Map();
|
|
44
|
+
this.connectPromise = undefined;
|
|
45
|
+
this.tokenRefreshTimer = undefined;
|
|
46
|
+
this.tokenRefreshInFlight = undefined;
|
|
47
|
+
}
|
|
48
|
+
off(eventName, listener) {
|
|
49
|
+
if (listener) {
|
|
50
|
+
return super.off(eventName, listener);
|
|
51
|
+
}
|
|
52
|
+
this.removeAllListeners(eventName);
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
attachSocketEventListeners(socket) {
|
|
56
|
+
socket.on('close', (event) => this.onclose(event, socket));
|
|
57
|
+
socket.on('message', (event) => this.onmessage(event));
|
|
58
|
+
}
|
|
59
|
+
trackAsyncEventAndShouldSuppressDuplicate(envelope) {
|
|
60
|
+
if (envelope?.type !== 'async_event' || !envelope.eventId) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (this.seenAsyncEventIds.has(envelope.eventId)) {
|
|
64
|
+
const previousValue = this.seenAsyncEventIds.get(envelope.eventId) || true;
|
|
65
|
+
this.seenAsyncEventIds.delete(envelope.eventId);
|
|
66
|
+
this.seenAsyncEventIds.set(envelope.eventId, previousValue);
|
|
67
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: duplicate async_event suppressed, eventId=${envelope.eventId}`);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
this.logger.log(`${MOBIUS_SOCKET_NAMESPACE}: tracking async_event, eventId=${envelope.eventId}`);
|
|
71
|
+
this.seenAsyncEventIds.set(envelope.eventId, true);
|
|
72
|
+
if (this.config.dedupCacheMaxSize &&
|
|
73
|
+
this.seenAsyncEventIds.size > this.config.dedupCacheMaxSize) {
|
|
74
|
+
const oldestEventId = this.seenAsyncEventIds.keys().next().value || '';
|
|
75
|
+
this.seenAsyncEventIds.delete(oldestEventId);
|
|
76
|
+
this.logger.log(`${MOBIUS_SOCKET_NAMESPACE}: evicted oldest async_event from dedup cache, eventId=${oldestEventId}`);
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
handleImminentShutdown() {
|
|
81
|
+
const oldSocket = this.socket;
|
|
82
|
+
try {
|
|
83
|
+
if (this.shutdownSwitchoverBackoffCall) {
|
|
84
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover already in progress`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover start`);
|
|
88
|
+
this.connectWithBackoff(undefined, {
|
|
89
|
+
isShutdownSwitchover: true,
|
|
90
|
+
attemptOptions: {
|
|
91
|
+
isShutdownSwitchover: true,
|
|
92
|
+
onSuccess: (newSocket, webSocketUrl) => {
|
|
93
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover connected, url: ${webSocketUrl}`);
|
|
94
|
+
newSocket.connecting = false;
|
|
95
|
+
newSocket.connected = true;
|
|
96
|
+
this.socket = newSocket;
|
|
97
|
+
this.connected = true;
|
|
98
|
+
this.emitEvent('event:mobius_shutdown_switchover_complete', {
|
|
99
|
+
url: webSocketUrl,
|
|
100
|
+
});
|
|
101
|
+
if (oldSocket) {
|
|
102
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] old socket retained; server will close with 4001`);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
.then(() => {
|
|
108
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover completed successfully`);
|
|
109
|
+
})
|
|
110
|
+
.catch((err) => {
|
|
111
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover exhausted retries; will fall back to normal reconnection: `, err);
|
|
112
|
+
this.emitEvent('event:mobius_shutdown_switchover_failed', { reason: err });
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] error during switchover`, e);
|
|
117
|
+
this.shutdownSwitchoverBackoffCall = undefined;
|
|
118
|
+
this.emitEvent('event:mobius_shutdown_switchover_failed', { reason: e });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
getConnectedWebSocketUrl() {
|
|
122
|
+
if (!this.socket?.connected) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
return this.socket.url;
|
|
126
|
+
}
|
|
127
|
+
sendWssRequest(payload, options = {}) {
|
|
128
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
129
|
+
return Promise.reject(new Error('`payload` is required'));
|
|
130
|
+
}
|
|
131
|
+
if (!this.socket || !this.socket.connected) {
|
|
132
|
+
return Promise.reject(new Error('Mobius socket is not connected'));
|
|
133
|
+
}
|
|
134
|
+
return this.socket.sendRequest(payload, { timeout: options.timeout });
|
|
135
|
+
}
|
|
136
|
+
isConnected() {
|
|
137
|
+
return this.connected;
|
|
138
|
+
}
|
|
139
|
+
connect(webSocketUrl) {
|
|
140
|
+
if (this.connectPromise) {
|
|
141
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connection already in progress, returning existing promise`);
|
|
142
|
+
return this.connectPromise;
|
|
143
|
+
}
|
|
144
|
+
if (this.socket?.connected || this.socket?.connecting) {
|
|
145
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: already connected, will not connect again`);
|
|
146
|
+
return Promise.resolve();
|
|
147
|
+
}
|
|
148
|
+
if (webSocketUrl && this.socketUrl && webSocketUrl !== this.socketUrl) {
|
|
149
|
+
this.hasEverConnected = false;
|
|
150
|
+
}
|
|
151
|
+
if (webSocketUrl) {
|
|
152
|
+
this.socketUrl = webSocketUrl;
|
|
153
|
+
}
|
|
154
|
+
this.connecting = true;
|
|
155
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: starting connection attempt${Number(this.config.initialConnectionMaxRetries) === 0 && !this.hasEverConnected
|
|
156
|
+
? ' (initial retries disabled)'
|
|
157
|
+
: ''}`);
|
|
158
|
+
const connectPromise = Promise.resolve(this.webex.internal.device.registered || this.webex.internal.device.register?.())
|
|
159
|
+
.then(() => {
|
|
160
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connecting`);
|
|
161
|
+
return this.connectWithBackoff(this.socketUrl);
|
|
162
|
+
})
|
|
163
|
+
.finally(() => {
|
|
164
|
+
this.connectPromise = undefined;
|
|
165
|
+
});
|
|
166
|
+
this.connectPromise = connectPromise;
|
|
167
|
+
return connectPromise;
|
|
168
|
+
}
|
|
169
|
+
disconnect(options) {
|
|
170
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}#disconnect: connecting state: ${this.connecting},
|
|
171
|
+
connected state: ${this.connected}, socket exists: ${!!this.socket},
|
|
172
|
+
options: ${JSON.stringify(options)}`);
|
|
173
|
+
if (this.backoffCall) {
|
|
174
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: aborting connection`);
|
|
175
|
+
this.backoffCall.abort();
|
|
176
|
+
this.backoffCall = undefined;
|
|
177
|
+
}
|
|
178
|
+
if (this.shutdownSwitchoverBackoffCall) {
|
|
179
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: aborting shutdown switchover connection`);
|
|
180
|
+
this.shutdownSwitchoverBackoffCall.abort();
|
|
181
|
+
this.shutdownSwitchoverBackoffCall = undefined;
|
|
182
|
+
}
|
|
183
|
+
this.connectPromise = undefined;
|
|
184
|
+
this.seenAsyncEventIds.clear();
|
|
185
|
+
if (!this.socket) {
|
|
186
|
+
this.connected = false;
|
|
187
|
+
this.stopTokenRefreshTimer();
|
|
188
|
+
return Promise.resolve();
|
|
189
|
+
}
|
|
190
|
+
this.socket.removeAllListeners('message');
|
|
191
|
+
this.socket.connecting = false;
|
|
192
|
+
this.socket.connected = false;
|
|
193
|
+
return Promise.resolve(this.socket.close(options || undefined)).finally(() => {
|
|
194
|
+
this.connected = false;
|
|
195
|
+
this.stopTokenRefreshTimer();
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
prepareUrl(webSocketUrl) {
|
|
199
|
+
if (!webSocketUrl) {
|
|
200
|
+
webSocketUrl = this.webex.internal.device.webSocketUrl || '';
|
|
201
|
+
}
|
|
202
|
+
return Promise.resolve(webSocketUrl);
|
|
203
|
+
}
|
|
204
|
+
attemptConnection(socketUrl, callback, options = {}) {
|
|
205
|
+
const { isShutdownSwitchover = false, attemptOptions = {} } = options;
|
|
206
|
+
const { onSuccess = null } = attemptOptions;
|
|
207
|
+
const socket = new Socket();
|
|
208
|
+
socket.connecting = true;
|
|
209
|
+
let newWSUrl;
|
|
210
|
+
this.attachSocketEventListeners(socket);
|
|
211
|
+
const backoffCall = isShutdownSwitchover
|
|
212
|
+
? this.shutdownSwitchoverBackoffCall
|
|
213
|
+
: this.backoffCall;
|
|
214
|
+
if (!backoffCall) {
|
|
215
|
+
const mode = isShutdownSwitchover ? 'switchover backoff call' : 'backoffCall';
|
|
216
|
+
const msg = `${MOBIUS_SOCKET_NAMESPACE}: prevent socket open when ${mode} no longer defined`;
|
|
217
|
+
const err = new Error(msg);
|
|
218
|
+
this.logger.info(msg);
|
|
219
|
+
callback(err);
|
|
220
|
+
return Promise.reject(err);
|
|
221
|
+
}
|
|
222
|
+
if (!isShutdownSwitchover) {
|
|
223
|
+
this.socket = socket;
|
|
224
|
+
}
|
|
225
|
+
return this.prepareAndOpenSocket(socket, socketUrl, isShutdownSwitchover)
|
|
226
|
+
.then((webSocketUrl) => {
|
|
227
|
+
newWSUrl = webSocketUrl;
|
|
228
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${isShutdownSwitchover ? '[shutdown] switchover' : ''} connected to mobius socket, success, url: ${newWSUrl}`);
|
|
229
|
+
if (onSuccess) {
|
|
230
|
+
onSuccess(socket, webSocketUrl);
|
|
231
|
+
callback();
|
|
232
|
+
return Promise.resolve();
|
|
233
|
+
}
|
|
234
|
+
callback();
|
|
235
|
+
return Promise.resolve();
|
|
236
|
+
})
|
|
237
|
+
.catch((reason) => {
|
|
238
|
+
if (isShutdownSwitchover) {
|
|
239
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] switchover attempt failed`, reason);
|
|
240
|
+
return callback(reason);
|
|
241
|
+
}
|
|
242
|
+
const backoffCallNormal = this.backoffCall;
|
|
243
|
+
if (reason.code !== 1006 && backoffCallNormal && backoffCallNormal?.getNumRetries() > 0) {
|
|
244
|
+
this.emitEvent('connection_failed', reason, {
|
|
245
|
+
retries: backoffCallNormal?.getNumRetries(),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connection attempt failed`, reason, backoffCallNormal?.getNumRetries() === 0 ? reason.stack : '');
|
|
249
|
+
if (reason instanceof UnknownResponse) {
|
|
250
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: received unknown response code, refreshing device registration`);
|
|
251
|
+
return this.webex.internal.device
|
|
252
|
+
.refresh?.()
|
|
253
|
+
.then(() => callback(reason));
|
|
254
|
+
}
|
|
255
|
+
if (reason instanceof NotAuthorized) {
|
|
256
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: received authorization error, reauthorizing`);
|
|
257
|
+
return this.webex.credentials
|
|
258
|
+
.refresh?.({ force: true })
|
|
259
|
+
.then(() => callback(reason));
|
|
260
|
+
}
|
|
261
|
+
if (reason instanceof BadRequest || reason instanceof Forbidden) {
|
|
262
|
+
this.logger.warn(`${MOBIUS_SOCKET_NAMESPACE}: received unrecoverable response from ${MOBIUS_SOCKET_NAMESPACE}`);
|
|
263
|
+
backoffCallNormal?.abort();
|
|
264
|
+
return callback(reason);
|
|
265
|
+
}
|
|
266
|
+
return callback(reason);
|
|
267
|
+
})
|
|
268
|
+
.catch((reason) => {
|
|
269
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: failed to handle connection failure`, reason);
|
|
270
|
+
callback(reason);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
prepareAndOpenSocket(socket, socketUrl, isShutdownSwitchover = false) {
|
|
274
|
+
const logPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
275
|
+
return Promise.all([this.prepareUrl(socketUrl), this.webex.credentials.getUserToken()]).then(([webSocketUrl, token]) => {
|
|
276
|
+
let options = {
|
|
277
|
+
forceCloseDelay: this.config.forceCloseDelay,
|
|
278
|
+
wssResponseTimeout: this.config.wssResponseTimeout,
|
|
279
|
+
token: normalizeMobiusAuthToken(token.toString()),
|
|
280
|
+
refreshToken: () => this.refreshToken(),
|
|
281
|
+
trackingId: `${this.webex.sessionId}_${Date.now()}`,
|
|
282
|
+
logger: this.logger,
|
|
283
|
+
};
|
|
284
|
+
if (this.webex.config.defaultMobiusSocketOptions) {
|
|
285
|
+
const customOptionsMsg = isShutdownSwitchover
|
|
286
|
+
? 'setting custom options for switchover'
|
|
287
|
+
: 'setting custom options';
|
|
288
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${customOptionsMsg}`);
|
|
289
|
+
options = { ...options, ...this.webex.config.defaultMobiusSocketOptions };
|
|
290
|
+
}
|
|
291
|
+
if (!isShutdownSwitchover) {
|
|
292
|
+
this.socket = socket;
|
|
293
|
+
}
|
|
294
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE} ${logPrefix} url: ${webSocketUrl}`);
|
|
295
|
+
return socket.open(webSocketUrl, options).then(() => webSocketUrl);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
connectWithBackoff(webSocketUrl, context = {}) {
|
|
299
|
+
const { isShutdownSwitchover = false, attemptOptions = {} } = context;
|
|
300
|
+
return new Promise((resolve, reject) => {
|
|
301
|
+
let call;
|
|
302
|
+
const isInitialConnect = !isShutdownSwitchover && !this.hasEverConnected;
|
|
303
|
+
const initialRetryLimit = this.config.initialConnectionMaxRetries == null
|
|
304
|
+
? null
|
|
305
|
+
: Number(this.config.initialConnectionMaxRetries);
|
|
306
|
+
const isInitialConnectWithoutRetries = isInitialConnect && initialRetryLimit === 0;
|
|
307
|
+
const onComplete = (err) => {
|
|
308
|
+
if (isShutdownSwitchover) {
|
|
309
|
+
this.shutdownSwitchoverBackoffCall = undefined;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
this.backoffCall = undefined;
|
|
313
|
+
}
|
|
314
|
+
if (err) {
|
|
315
|
+
const msg = isShutdownSwitchover
|
|
316
|
+
? `[shutdown] switchover failed after ${call.getNumRetries()} retries`
|
|
317
|
+
: `failed to connect after ${call.getNumRetries()} retries`;
|
|
318
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${msg}; ${err}`);
|
|
319
|
+
if (!isShutdownSwitchover && this.socket) {
|
|
320
|
+
this.socket.connecting = false;
|
|
321
|
+
this.socket.connected = false;
|
|
322
|
+
}
|
|
323
|
+
return reject(err);
|
|
324
|
+
}
|
|
325
|
+
if (!isShutdownSwitchover && this.socket) {
|
|
326
|
+
this.socket.connecting = false;
|
|
327
|
+
this.socket.connected = true;
|
|
328
|
+
}
|
|
329
|
+
if (!isShutdownSwitchover) {
|
|
330
|
+
this.connecting = false;
|
|
331
|
+
this.connected = true;
|
|
332
|
+
this.hasEverConnected = true;
|
|
333
|
+
this.startTokenRefreshTimer();
|
|
334
|
+
this.emitEvent('online');
|
|
335
|
+
}
|
|
336
|
+
return resolve();
|
|
337
|
+
};
|
|
338
|
+
call = backoff.call((callback) => {
|
|
339
|
+
const attemptNum = call.getNumRetries();
|
|
340
|
+
const attemptLogPrefix = isShutdownSwitchover ? '[shutdown] switchover' : 'connection';
|
|
341
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: executing ${attemptLogPrefix} attempt ${attemptNum}`);
|
|
342
|
+
this.attemptConnection(webSocketUrl, callback, attemptOptions);
|
|
343
|
+
}, (err) => onComplete(err));
|
|
344
|
+
call.setStrategy(new backoff.ExponentialStrategy({
|
|
345
|
+
initialDelay: this.config.backoffTimeReset,
|
|
346
|
+
maxDelay: this.config.backoffTimeMax,
|
|
347
|
+
}));
|
|
348
|
+
if (isInitialConnectWithoutRetries) {
|
|
349
|
+
call.retryIf(() => false);
|
|
350
|
+
}
|
|
351
|
+
else if (isInitialConnect && initialRetryLimit !== null && initialRetryLimit > 0) {
|
|
352
|
+
call.failAfter(initialRetryLimit);
|
|
353
|
+
}
|
|
354
|
+
else if (this.config.maxRetries) {
|
|
355
|
+
call.failAfter(this.config.maxRetries);
|
|
356
|
+
}
|
|
357
|
+
if (isShutdownSwitchover) {
|
|
358
|
+
this.shutdownSwitchoverBackoffCall = call;
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
this.backoffCall = call;
|
|
362
|
+
}
|
|
363
|
+
call.on('abort', () => {
|
|
364
|
+
const msg = isShutdownSwitchover ? 'Shutdown Switchover' : 'Connection';
|
|
365
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${msg} aborted`);
|
|
366
|
+
reject(new Error(`MobiusSocket ${msg} Aborted`));
|
|
367
|
+
});
|
|
368
|
+
call.on('callback', (err) => {
|
|
369
|
+
if (err) {
|
|
370
|
+
if (isInitialConnectWithoutRetries) {
|
|
371
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: initial connect failed; retries already disabled`);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const number = call.getNumRetries();
|
|
375
|
+
const delay = Math.min(call.strategy_.nextBackoffDelay_, this.config.backoffTimeMax || Infinity);
|
|
376
|
+
const callbackLogPrefix = isShutdownSwitchover ? '[shutdown] switchover' : '';
|
|
377
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: ${callbackLogPrefix} failed to connect; attempting retry ${number + 1} in ${delay} ms`);
|
|
378
|
+
if (process.env.NODE_ENV === 'development') {
|
|
379
|
+
this.logger.debug(`${MOBIUS_SOCKET_NAMESPACE}: `, err, err.stack);
|
|
380
|
+
}
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: connected`);
|
|
384
|
+
});
|
|
385
|
+
call.start();
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
emitEvent(eventName, ...args) {
|
|
389
|
+
try {
|
|
390
|
+
if (!eventName) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this.emit(eventName, ...args);
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred in event handler:`, error, ' with args: ', [eventName, ...args]);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
startTokenRefreshTimer() {
|
|
400
|
+
if (this.tokenRefreshTimer || !this.connected) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
this.tokenRefreshTimer = setInterval(() => {
|
|
404
|
+
this.refreshToken().catch((error) => {
|
|
405
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: periodic token refresh failed`, error);
|
|
406
|
+
});
|
|
407
|
+
}, TOKEN_REFRESH_INTERVAL_MS);
|
|
408
|
+
}
|
|
409
|
+
stopTokenRefreshTimer() {
|
|
410
|
+
if (!this.tokenRefreshTimer) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
clearInterval(this.tokenRefreshTimer);
|
|
414
|
+
this.tokenRefreshTimer = undefined;
|
|
415
|
+
}
|
|
416
|
+
refreshToken() {
|
|
417
|
+
if (this.tokenRefreshInFlight) {
|
|
418
|
+
return this.tokenRefreshInFlight;
|
|
419
|
+
}
|
|
420
|
+
if (!this.connected) {
|
|
421
|
+
this.stopTokenRefreshTimer();
|
|
422
|
+
return Promise.resolve();
|
|
423
|
+
}
|
|
424
|
+
const tokenPromise = this.webex.credentials.canRefresh
|
|
425
|
+
? this.webex.credentials
|
|
426
|
+
.refresh?.({ force: true })
|
|
427
|
+
?.then(() => this.webex.credentials.getUserToken())
|
|
428
|
+
: this.webex.credentials.getUserToken();
|
|
429
|
+
this.tokenRefreshInFlight = tokenPromise
|
|
430
|
+
.then((token) => {
|
|
431
|
+
if (!token) {
|
|
432
|
+
throw new Error('Mobius token refresh did not return a token');
|
|
433
|
+
}
|
|
434
|
+
const refreshedToken = normalizeMobiusAuthToken(token.toString());
|
|
435
|
+
if (!this.socket?.connected) {
|
|
436
|
+
this.logger.warn(`${MOBIUS_SOCKET_NAMESPACE}: socket is not connected, skipping token refresh`);
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
return this.socket.refresh(refreshedToken);
|
|
440
|
+
})
|
|
441
|
+
.catch((error) => {
|
|
442
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: failed to refresh/re-auth Mobius socket`, error);
|
|
443
|
+
throw error;
|
|
444
|
+
})
|
|
445
|
+
.finally(() => {
|
|
446
|
+
this.tokenRefreshInFlight = undefined;
|
|
447
|
+
});
|
|
448
|
+
return this.tokenRefreshInFlight;
|
|
449
|
+
}
|
|
450
|
+
onclose(event, sourceSocket) {
|
|
451
|
+
try {
|
|
452
|
+
const reason = event.reason && event.reason.toLowerCase();
|
|
453
|
+
let socketUrl;
|
|
454
|
+
const isActiveSocket = sourceSocket === this.socket;
|
|
455
|
+
if (sourceSocket) {
|
|
456
|
+
socketUrl = sourceSocket.url;
|
|
457
|
+
}
|
|
458
|
+
if (isActiveSocket) {
|
|
459
|
+
if (this.socket) {
|
|
460
|
+
this.socket.removeAllListeners();
|
|
461
|
+
this.socket = undefined;
|
|
462
|
+
this.emitEvent('offline', event);
|
|
463
|
+
}
|
|
464
|
+
this.connecting = false;
|
|
465
|
+
this.connected = false;
|
|
466
|
+
this.stopTokenRefreshTimer();
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] non-active socket closed, code=${event.code}`);
|
|
470
|
+
if (sourceSocket) {
|
|
471
|
+
sourceSocket.removeAllListeners();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
switch (event.code) {
|
|
475
|
+
case 1003:
|
|
476
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: service rejected last message; will not reconnect: ${event.reason}`);
|
|
477
|
+
if (isActiveSocket)
|
|
478
|
+
this.emitEvent('offline.permanent', event);
|
|
479
|
+
break;
|
|
480
|
+
case 4000:
|
|
481
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket replaced; will not reconnect`);
|
|
482
|
+
if (isActiveSocket)
|
|
483
|
+
this.emitEvent('offline.replaced', event);
|
|
484
|
+
break;
|
|
485
|
+
case 4001:
|
|
486
|
+
if (isActiveSocket) {
|
|
487
|
+
this.logger.warn(`${MOBIUS_SOCKET_NAMESPACE}: active socket closed with 4001; shutdown switchover failed`);
|
|
488
|
+
this.emitEvent('offline.permanent', event);
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: old socket closed with 4001 (replaced during shutdown); no reconnect needed`);
|
|
492
|
+
this.emitEvent('offline.replaced', event);
|
|
493
|
+
}
|
|
494
|
+
break;
|
|
495
|
+
case 1001:
|
|
496
|
+
case 1005:
|
|
497
|
+
case 1006:
|
|
498
|
+
case 1011:
|
|
499
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket disconnected; reconnecting`);
|
|
500
|
+
if (isActiveSocket) {
|
|
501
|
+
this.emitEvent('offline.transient', event);
|
|
502
|
+
this.reconnect(socketUrl);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
case 1000:
|
|
506
|
+
case 3050:
|
|
507
|
+
if (reason && normalReconnectReasons.includes(reason)) {
|
|
508
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket disconnected; reconnecting`);
|
|
509
|
+
if (isActiveSocket) {
|
|
510
|
+
this.emitEvent('offline.transient', event);
|
|
511
|
+
this.reconnect(socketUrl);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket disconnected; will not reconnect: ${event.reason}`);
|
|
516
|
+
if (isActiveSocket)
|
|
517
|
+
this.emitEvent('offline.permanent', event);
|
|
518
|
+
}
|
|
519
|
+
break;
|
|
520
|
+
default:
|
|
521
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: socket disconnected unexpectedly; will not reconnect`);
|
|
522
|
+
if (isActiveSocket)
|
|
523
|
+
this.emitEvent('offline.permanent', event);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
catch (error) {
|
|
527
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred in close handler`, error);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
onmessage(event) {
|
|
531
|
+
const envelope = event.data;
|
|
532
|
+
if (process.env.ENABLE_MOBIUS_LOGGING) {
|
|
533
|
+
this.logger.debug(`${MOBIUS_SOCKET_NAMESPACE}: message envelope: `, envelope);
|
|
534
|
+
}
|
|
535
|
+
if (envelope && envelope.type === 'shutdown') {
|
|
536
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: [shutdown] imminent shutdown message received`);
|
|
537
|
+
this.emitEvent('event:mobius_shutdown_imminent', envelope);
|
|
538
|
+
this.handleImminentShutdown();
|
|
539
|
+
return Promise.resolve();
|
|
540
|
+
}
|
|
541
|
+
if (this.trackAsyncEventAndShouldSuppressDuplicate(envelope)) {
|
|
542
|
+
return Promise.resolve();
|
|
543
|
+
}
|
|
544
|
+
if (envelope.type) {
|
|
545
|
+
this.emitEvent(`event:${envelope.type}`, envelope);
|
|
546
|
+
}
|
|
547
|
+
const data = envelope.data || envelope;
|
|
548
|
+
const eventType = data?.eventType || envelope.eventType;
|
|
549
|
+
if (!eventType) {
|
|
550
|
+
this.emitEvent('event', envelope);
|
|
551
|
+
return Promise.resolve();
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
this.emitEvent('event', envelope);
|
|
555
|
+
const [namespace] = eventType.split('.');
|
|
556
|
+
this.emitEvent(`event:${namespace}`, envelope);
|
|
557
|
+
if (namespace !== eventType) {
|
|
558
|
+
this.emitEvent(`event:${eventType}`, envelope);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
catch (reason) {
|
|
562
|
+
this.logger.error(`${MOBIUS_SOCKET_NAMESPACE}: error occurred processing socket message`, reason);
|
|
563
|
+
}
|
|
564
|
+
return Promise.resolve();
|
|
565
|
+
}
|
|
566
|
+
reconnect(webSocketUrl) {
|
|
567
|
+
this.logger.info(`${MOBIUS_SOCKET_NAMESPACE}: reconnecting`);
|
|
568
|
+
return this.connect(webSocketUrl || this.socketUrl);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
export default MobiusSocket;
|