@socket-mesh/client 18.0.8 → 18.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/client-auth-engine.d.ts +22 -0
- package/dist/client-auth-engine.js +52 -0
- package/dist/client-channels.d.ts +22 -0
- package/dist/client-channels.js +149 -0
- package/dist/client-socket-options.d.ts +24 -0
- package/dist/client-socket-options.js +6 -0
- package/dist/client-socket.d.ts +26 -0
- package/dist/client-socket.js +100 -0
- package/dist/client-transport.d.ts +52 -0
- package/dist/client-transport.js +261 -0
- package/dist/handlers/index.d.ts +4 -0
- package/dist/handlers/index.js +4 -0
- package/dist/handlers/kickout.d.ts +6 -0
- package/dist/handlers/kickout.js +5 -0
- package/dist/handlers/publish.d.ts +7 -0
- package/dist/handlers/publish.js +3 -0
- package/dist/handlers/remove-auth-token.d.ts +2 -0
- package/dist/handlers/remove-auth-token.js +3 -0
- package/dist/handlers/set-auth-token.d.ts +3 -0
- package/dist/handlers/set-auth-token.js +3 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/maps/client-map.d.ts +21 -0
- package/dist/maps/client-map.js +1 -0
- package/dist/maps/index.d.ts +3 -0
- package/dist/maps/index.js +3 -0
- package/dist/maps/server-map.d.ts +27 -0
- package/dist/maps/server-map.js +1 -0
- package/dist/maps/socket-map.d.ts +17 -0
- package/dist/maps/socket-map.js +1 -0
- package/dist/plugins/batching-plugin.d.ts +41 -0
- package/dist/plugins/batching-plugin.js +111 -0
- package/dist/plugins/in-order-plugin.d.ts +10 -0
- package/dist/plugins/in-order-plugin.js +70 -0
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.js +3 -0
- package/dist/plugins/offline-plugin.d.ts +13 -0
- package/dist/plugins/offline-plugin.js +47 -0
- package/package.json +54 -58
- package/auth.d.ts +0 -21
- package/auth.js +0 -49
- package/auth.js.map +0 -1
- package/client-options.d.ts +0 -53
- package/client-options.js +0 -2
- package/client-options.js.map +0 -1
- package/clientsocket.d.ts +0 -135
- package/clientsocket.js +0 -898
- package/clientsocket.js.map +0 -1
- package/events.d.ts +0 -54
- package/events.js +0 -2
- package/events.js.map +0 -1
- package/factory.d.ts +0 -3
- package/factory.js +0 -37
- package/factory.js.map +0 -1
- package/index.d.ts +0 -7
- package/index.js +0 -10
- package/index.js.map +0 -1
- package/remote-procedure.d.ts +0 -6
- package/remote-procedure.js +0 -10
- package/remote-procedure.js.map +0 -1
- package/socket-state.d.ts +0 -5
- package/socket-state.js +0 -7
- package/socket-state.js.map +0 -1
- package/transport-handlers.d.ts +0 -37
- package/transport-handlers.js +0 -2
- package/transport-handlers.js.map +0 -1
- package/transport.d.ts +0 -84
- package/transport.js +0 -441
- package/transport.js.map +0 -1
- package/wait.d.ts +0 -1
- package/wait.js +0 -8
- package/wait.js.map +0 -1
- package/ws-browser.d.ts +0 -15
- package/ws-browser.js +0 -40
- package/ws-browser.js.map +0 -1
package/clientsocket.js
DELETED
|
@@ -1,898 +0,0 @@
|
|
|
1
|
-
import { StreamDemux, StreamDemuxWrapper } from "@socket-mesh/stream-demux";
|
|
2
|
-
import { LocalStorageAuthEngine } from "./auth.js";
|
|
3
|
-
import { AuthState } from "@socket-mesh/auth";
|
|
4
|
-
import formatter from "@socket-mesh/formatter";
|
|
5
|
-
import { Transport } from "./transport.js";
|
|
6
|
-
import { List, Item } from "linked-list";
|
|
7
|
-
import cloneDeep from "clone-deep";
|
|
8
|
-
import { Buffer } from "buffer";
|
|
9
|
-
import { wait } from "./wait.js";
|
|
10
|
-
import { ChannelState, Client } from "@socket-mesh/channel";
|
|
11
|
-
import { SocketState } from "./socket-state.js";
|
|
12
|
-
import { hydrateError, InvalidArgumentsError, InvalidMessageError, SocketProtocolError, TimeoutError, BadConnectionError, socketProtocolIgnoreStatuses, socketProtocolErrorStatuses } from "@socket-mesh/errors";
|
|
13
|
-
import { RemoteProcedure } from "./remote-procedure.js";
|
|
14
|
-
class EventNode extends Item {
|
|
15
|
-
constructor() {
|
|
16
|
-
super();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
export class ClientSocket extends Client {
|
|
20
|
-
constructor(socketOptions) {
|
|
21
|
-
super();
|
|
22
|
-
this.authState = AuthState.UNAUTHENTICATED;
|
|
23
|
-
this.id = null;
|
|
24
|
-
this.state = SocketState.CLOSED;
|
|
25
|
-
let opts = Object.assign({
|
|
26
|
-
path: '/socketcluster/',
|
|
27
|
-
secure: false,
|
|
28
|
-
protocolScheme: null,
|
|
29
|
-
socketPath: null,
|
|
30
|
-
autoConnect: true,
|
|
31
|
-
autoReconnect: true,
|
|
32
|
-
autoSubscribeOnConnect: true,
|
|
33
|
-
connectTimeout: 20000,
|
|
34
|
-
ackTimeout: 10000,
|
|
35
|
-
timestampRequests: false,
|
|
36
|
-
timestampParam: 't',
|
|
37
|
-
binaryType: 'arraybuffer',
|
|
38
|
-
batchOnHandshake: false,
|
|
39
|
-
batchOnHandshakeDuration: 100,
|
|
40
|
-
batchInterval: 50,
|
|
41
|
-
protocolVersion: 2,
|
|
42
|
-
wsOptions: {},
|
|
43
|
-
cloneData: false
|
|
44
|
-
}, socketOptions);
|
|
45
|
-
if (opts.authTokenName == null) {
|
|
46
|
-
const authHostString = opts.host ? `.${opts.host}` : `.${opts.hostname || 'localhost'}${opts.port ? `:${opts.port}` : ''}`;
|
|
47
|
-
opts.authTokenName = `socketcluster.authToken${authHostString}`;
|
|
48
|
-
}
|
|
49
|
-
this.version = opts.version || null;
|
|
50
|
-
this.protocolVersion = opts.protocolVersion;
|
|
51
|
-
this.signedAuthToken = null;
|
|
52
|
-
this.authToken = null;
|
|
53
|
-
this.pendingReconnect = false;
|
|
54
|
-
this.pendingReconnectTimeout = null;
|
|
55
|
-
this.preparingPendingSubscriptions = false;
|
|
56
|
-
this.clientId = opts.clientId;
|
|
57
|
-
this.wsOptions = opts.wsOptions;
|
|
58
|
-
this.connectTimeout = opts.connectTimeout;
|
|
59
|
-
this.ackTimeout = opts.ackTimeout;
|
|
60
|
-
this.channelPrefix = opts.channelPrefix || null;
|
|
61
|
-
this.authTokenName = opts.authTokenName;
|
|
62
|
-
// pingTimeout will be connectTimeout at the start, but it will
|
|
63
|
-
// be updated with values provided by the 'connect' event
|
|
64
|
-
opts.pingTimeout = opts.connectTimeout;
|
|
65
|
-
this.pingTimeout = opts.pingTimeout;
|
|
66
|
-
this.pingTimeoutDisabled = !!opts.pingTimeoutDisabled;
|
|
67
|
-
const maxTimeout = Math.pow(2, 31) - 1;
|
|
68
|
-
if (this.connectTimeout > maxTimeout) {
|
|
69
|
-
throw new InvalidArgumentsError('The connectTimeout value provided exceeded the maximum amount allowed');
|
|
70
|
-
}
|
|
71
|
-
if (this.ackTimeout > maxTimeout) {
|
|
72
|
-
throw new InvalidArgumentsError('The ackTimeout value provided exceeded the maximum amount allowed');
|
|
73
|
-
}
|
|
74
|
-
if (this.pingTimeout > maxTimeout) {
|
|
75
|
-
throw new InvalidArgumentsError('The pingTimeout value provided exceeded the maximum amount allowed');
|
|
76
|
-
}
|
|
77
|
-
this.connectAttempts = 0;
|
|
78
|
-
this.isBatching = false;
|
|
79
|
-
this.batchOnHandshake = opts.batchOnHandshake;
|
|
80
|
-
this.batchOnHandshakeDuration = opts.batchOnHandshakeDuration;
|
|
81
|
-
this._batchingIntervalId = null;
|
|
82
|
-
this._outboundBuffer = new List();
|
|
83
|
-
this._receiverDemux = new StreamDemux();
|
|
84
|
-
this.receiver = new StreamDemuxWrapper(this._receiverDemux);
|
|
85
|
-
this._procedureDemux = new StreamDemux();
|
|
86
|
-
this.procedure = new RemoteProcedure(this._procedureDemux);
|
|
87
|
-
this.options = opts;
|
|
88
|
-
this._cid = 1;
|
|
89
|
-
this.options.callIdGenerator = () => {
|
|
90
|
-
return this._cid++;
|
|
91
|
-
};
|
|
92
|
-
if (this.options.autoReconnect) {
|
|
93
|
-
if (this.options.autoReconnectOptions == null) {
|
|
94
|
-
this.options.autoReconnectOptions = {};
|
|
95
|
-
}
|
|
96
|
-
// Add properties to the this.options.autoReconnectOptions object.
|
|
97
|
-
// We assign the reference to a reconnectOptions variable to avoid repetition.
|
|
98
|
-
let reconnectOptions = this.options.autoReconnectOptions;
|
|
99
|
-
if (reconnectOptions.initialDelay == null) {
|
|
100
|
-
reconnectOptions.initialDelay = 10000;
|
|
101
|
-
}
|
|
102
|
-
if (reconnectOptions.randomness == null) {
|
|
103
|
-
reconnectOptions.randomness = 10000;
|
|
104
|
-
}
|
|
105
|
-
if (reconnectOptions.multiplier == null) {
|
|
106
|
-
reconnectOptions.multiplier = 1.5;
|
|
107
|
-
}
|
|
108
|
-
if (reconnectOptions.maxDelay == null) {
|
|
109
|
-
reconnectOptions.maxDelay = 60000;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
if (this.options.subscriptionRetryOptions == null) {
|
|
113
|
-
this.options.subscriptionRetryOptions = {};
|
|
114
|
-
}
|
|
115
|
-
if (this.options.authEngine) {
|
|
116
|
-
this.auth = this.options.authEngine;
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
this.auth = new LocalStorageAuthEngine();
|
|
120
|
-
}
|
|
121
|
-
if (this.options.codecEngine) {
|
|
122
|
-
this.codec = this.options.codecEngine;
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
// Default codec engine
|
|
126
|
-
this.codec = formatter;
|
|
127
|
-
}
|
|
128
|
-
if (this.options.protocol) {
|
|
129
|
-
let protocolOptionError = new InvalidArgumentsError('The protocol option does not affect socketcluster-client - ' +
|
|
130
|
-
'If you want to utilize SSL/TLS, use the secure option instead');
|
|
131
|
-
this._onError(protocolOptionError);
|
|
132
|
-
}
|
|
133
|
-
this.options.query = opts.query || {};
|
|
134
|
-
if (this.options.autoConnect) {
|
|
135
|
-
this.connect();
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
uri() {
|
|
139
|
-
return Transport.computeUri(this.options);
|
|
140
|
-
}
|
|
141
|
-
;
|
|
142
|
-
getBackpressure() {
|
|
143
|
-
return Math.max(this.getListenerBackpressure(), this.receiver.getBackpressure(), this.procedure.getBackpressure(), this.getChannelBackpressure());
|
|
144
|
-
}
|
|
145
|
-
_setAuthToken(data) {
|
|
146
|
-
this._changeToAuthenticatedState(data.token);
|
|
147
|
-
(async () => {
|
|
148
|
-
try {
|
|
149
|
-
await this.auth.saveToken(this.authTokenName, data.token, {});
|
|
150
|
-
}
|
|
151
|
-
catch (err) {
|
|
152
|
-
this._onError(err);
|
|
153
|
-
}
|
|
154
|
-
})();
|
|
155
|
-
}
|
|
156
|
-
_removeAuthToken() {
|
|
157
|
-
(async () => {
|
|
158
|
-
let oldAuthToken;
|
|
159
|
-
try {
|
|
160
|
-
oldAuthToken = await this.auth.removeToken(this.authTokenName);
|
|
161
|
-
}
|
|
162
|
-
catch (err) {
|
|
163
|
-
// Non-fatal error - Do not close the connection
|
|
164
|
-
this._onError(err);
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
this.emit('removeAuthToken', { oldAuthToken });
|
|
168
|
-
})();
|
|
169
|
-
this._changeToUnauthenticatedStateAndClearTokens();
|
|
170
|
-
}
|
|
171
|
-
getState() {
|
|
172
|
-
return this.state;
|
|
173
|
-
}
|
|
174
|
-
getBytesReceived() {
|
|
175
|
-
return this.transport.getBytesReceived();
|
|
176
|
-
}
|
|
177
|
-
async deauthenticate() {
|
|
178
|
-
(async () => {
|
|
179
|
-
let oldAuthToken;
|
|
180
|
-
try {
|
|
181
|
-
oldAuthToken = await this.auth.removeToken(this.authTokenName);
|
|
182
|
-
}
|
|
183
|
-
catch (err) {
|
|
184
|
-
this._onError(err);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
this.emit('removeAuthToken', { oldAuthToken });
|
|
188
|
-
})();
|
|
189
|
-
if (this.state !== SocketState.CLOSED) {
|
|
190
|
-
this.transmit('#removeAuthToken');
|
|
191
|
-
}
|
|
192
|
-
this._changeToUnauthenticatedStateAndClearTokens();
|
|
193
|
-
await wait(0);
|
|
194
|
-
}
|
|
195
|
-
connect(socketOptions) {
|
|
196
|
-
if (socketOptions) {
|
|
197
|
-
if (this.state !== SocketState.CLOSED) {
|
|
198
|
-
this.disconnect(1000, 'Socket was disconnected by the client to initiate a new connection');
|
|
199
|
-
}
|
|
200
|
-
this.options = {
|
|
201
|
-
...this.options,
|
|
202
|
-
...socketOptions
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
if (this.state === SocketState.CLOSED) {
|
|
206
|
-
this.pendingReconnect = false;
|
|
207
|
-
this.pendingReconnectTimeout = null;
|
|
208
|
-
clearTimeout(this._reconnectTimeoutRef);
|
|
209
|
-
this.state = SocketState.CONNECTING;
|
|
210
|
-
this.emit('connecting', {});
|
|
211
|
-
if (this.transport) {
|
|
212
|
-
this.transport.clearAllListeners();
|
|
213
|
-
}
|
|
214
|
-
const transportHandlers = {
|
|
215
|
-
onOpen: (value) => {
|
|
216
|
-
this.state = SocketState.OPEN;
|
|
217
|
-
this._onOpen(value);
|
|
218
|
-
},
|
|
219
|
-
onOpenAbort: (value) => {
|
|
220
|
-
if (this.state !== SocketState.CLOSED) {
|
|
221
|
-
this.state = SocketState.CLOSED;
|
|
222
|
-
this._destroy(value.code, value.reason, true);
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
onClose: (value) => {
|
|
226
|
-
if (this.state !== SocketState.CLOSED) {
|
|
227
|
-
this.state = SocketState.CLOSED;
|
|
228
|
-
this._destroy(value.code, value.reason);
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
|
-
onEvent: (value) => {
|
|
232
|
-
this.emit(value.event, value.data);
|
|
233
|
-
},
|
|
234
|
-
onError: (value) => {
|
|
235
|
-
this._onError(value.error);
|
|
236
|
-
},
|
|
237
|
-
onInboundInvoke: (value) => {
|
|
238
|
-
this._onInboundInvoke(value);
|
|
239
|
-
},
|
|
240
|
-
onInboundTransmit: (value) => {
|
|
241
|
-
this._onInboundTransmit(value.event, value.data);
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
this.transport = new Transport(this.auth, this.codec, this.options, this.wsOptions, transportHandlers);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
reconnect(code, reason) {
|
|
248
|
-
this.disconnect(code, reason);
|
|
249
|
-
this.connect();
|
|
250
|
-
}
|
|
251
|
-
disconnect(code = 1000, reason) {
|
|
252
|
-
if (typeof code !== 'number') {
|
|
253
|
-
throw new InvalidArgumentsError('If specified, the code argument must be a number');
|
|
254
|
-
}
|
|
255
|
-
let isConnecting = this.state === SocketState.CONNECTING;
|
|
256
|
-
if (isConnecting || this.state === SocketState.OPEN) {
|
|
257
|
-
this.state = SocketState.CLOSED;
|
|
258
|
-
this._destroy(code, reason, isConnecting);
|
|
259
|
-
this.transport.close(code, reason);
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
this.pendingReconnect = false;
|
|
263
|
-
this.pendingReconnectTimeout = null;
|
|
264
|
-
clearTimeout(this._reconnectTimeoutRef);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
_changeToUnauthenticatedStateAndClearTokens() {
|
|
268
|
-
if (this.authState !== AuthState.UNAUTHENTICATED) {
|
|
269
|
-
let oldAuthState = this.authState;
|
|
270
|
-
let oldAuthToken = this.authToken;
|
|
271
|
-
let oldSignedAuthToken = this.signedAuthToken;
|
|
272
|
-
this.authState = AuthState.UNAUTHENTICATED;
|
|
273
|
-
this.signedAuthToken = null;
|
|
274
|
-
this.authToken = null;
|
|
275
|
-
let stateChangeData = {
|
|
276
|
-
oldAuthState,
|
|
277
|
-
newAuthState: this.authState
|
|
278
|
-
};
|
|
279
|
-
this.emit('authStateChange', stateChangeData);
|
|
280
|
-
this.emit('deauthenticate', { oldSignedAuthToken, oldAuthToken });
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
_changeToAuthenticatedState(signedAuthToken) {
|
|
284
|
-
this.signedAuthToken = signedAuthToken;
|
|
285
|
-
this.authToken = this._extractAuthTokenData(signedAuthToken);
|
|
286
|
-
if (this.authState !== AuthState.AUTHENTICATED) {
|
|
287
|
-
let oldAuthState = this.authState;
|
|
288
|
-
this.authState = AuthState.AUTHENTICATED;
|
|
289
|
-
let stateChangeData = {
|
|
290
|
-
oldAuthState,
|
|
291
|
-
newAuthState: this.authState,
|
|
292
|
-
signedAuthToken: signedAuthToken,
|
|
293
|
-
authToken: this.authToken
|
|
294
|
-
};
|
|
295
|
-
if (!this.preparingPendingSubscriptions) {
|
|
296
|
-
this.processPendingSubscriptions();
|
|
297
|
-
}
|
|
298
|
-
this.emit('authStateChange', stateChangeData);
|
|
299
|
-
}
|
|
300
|
-
this.emit('authenticate', { signedAuthToken, authToken: this.authToken });
|
|
301
|
-
}
|
|
302
|
-
decodeBase64(encodedString) {
|
|
303
|
-
return Buffer.from(encodedString, 'base64').toString('utf8');
|
|
304
|
-
}
|
|
305
|
-
encodeBase64(decodedString) {
|
|
306
|
-
return Buffer.from(decodedString, 'utf8').toString('base64');
|
|
307
|
-
}
|
|
308
|
-
_extractAuthTokenData(signedAuthToken) {
|
|
309
|
-
if (typeof signedAuthToken !== 'string')
|
|
310
|
-
return null;
|
|
311
|
-
let tokenParts = signedAuthToken.split('.');
|
|
312
|
-
let encodedTokenData = tokenParts[1];
|
|
313
|
-
if (encodedTokenData != null) {
|
|
314
|
-
let tokenData = encodedTokenData;
|
|
315
|
-
try {
|
|
316
|
-
tokenData = this.decodeBase64(tokenData);
|
|
317
|
-
return JSON.parse(tokenData);
|
|
318
|
-
}
|
|
319
|
-
catch (e) {
|
|
320
|
-
return tokenData;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
getAuthToken() {
|
|
326
|
-
return this.authToken;
|
|
327
|
-
}
|
|
328
|
-
getSignedAuthToken() {
|
|
329
|
-
return this.signedAuthToken;
|
|
330
|
-
}
|
|
331
|
-
// Perform client-initiated authentication by providing an encrypted token string.
|
|
332
|
-
async authenticate(signedAuthToken) {
|
|
333
|
-
let authStatus;
|
|
334
|
-
try {
|
|
335
|
-
authStatus = await this.invoke('#authenticate', signedAuthToken);
|
|
336
|
-
}
|
|
337
|
-
catch (err) {
|
|
338
|
-
if (err.name !== 'BadConnectionError' && err.name !== 'TimeoutError') {
|
|
339
|
-
// In case of a bad/closed connection or a timeout, we maintain the last
|
|
340
|
-
// known auth state since those errors don't mean that the token is invalid.
|
|
341
|
-
this._changeToUnauthenticatedStateAndClearTokens();
|
|
342
|
-
}
|
|
343
|
-
await wait(0);
|
|
344
|
-
throw err;
|
|
345
|
-
}
|
|
346
|
-
if (authStatus?.isAuthenticated != null) {
|
|
347
|
-
// If authStatus is correctly formatted (has an isAuthenticated property),
|
|
348
|
-
// then we will rehydrate the authError.
|
|
349
|
-
if (authStatus.authError) {
|
|
350
|
-
authStatus.authError = hydrateError(authStatus.authError);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
// Some errors like BadConnectionError and TimeoutError will not pass a valid
|
|
355
|
-
// authStatus object to the current function, so we need to create it ourselves.
|
|
356
|
-
authStatus = {
|
|
357
|
-
isAuthenticated: this.authState,
|
|
358
|
-
authError: null
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
if (authStatus.isAuthenticated) {
|
|
362
|
-
this._changeToAuthenticatedState(signedAuthToken);
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
this._changeToUnauthenticatedStateAndClearTokens();
|
|
366
|
-
}
|
|
367
|
-
(async () => {
|
|
368
|
-
try {
|
|
369
|
-
await this.auth.saveToken(this.authTokenName, signedAuthToken, {});
|
|
370
|
-
}
|
|
371
|
-
catch (err) {
|
|
372
|
-
this._onError(err);
|
|
373
|
-
}
|
|
374
|
-
})();
|
|
375
|
-
await wait(0);
|
|
376
|
-
return authStatus;
|
|
377
|
-
}
|
|
378
|
-
_tryReconnect(initialDelay) {
|
|
379
|
-
let exponent = this.connectAttempts++;
|
|
380
|
-
let reconnectOptions = this.options.autoReconnectOptions;
|
|
381
|
-
let timeout;
|
|
382
|
-
if (initialDelay == null || exponent > 0) {
|
|
383
|
-
let initialTimeout = Math.round(reconnectOptions.initialDelay + (reconnectOptions.randomness || 0) * Math.random());
|
|
384
|
-
timeout = Math.round(initialTimeout * Math.pow(reconnectOptions.multiplier, exponent));
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
timeout = initialDelay;
|
|
388
|
-
}
|
|
389
|
-
if (timeout > reconnectOptions.maxDelay) {
|
|
390
|
-
timeout = reconnectOptions.maxDelay;
|
|
391
|
-
}
|
|
392
|
-
clearTimeout(this._reconnectTimeoutRef);
|
|
393
|
-
this.pendingReconnect = true;
|
|
394
|
-
this.pendingReconnectTimeout = timeout;
|
|
395
|
-
this._reconnectTimeoutRef = setTimeout(() => {
|
|
396
|
-
this.connect();
|
|
397
|
-
}, timeout);
|
|
398
|
-
}
|
|
399
|
-
_onOpen(status) {
|
|
400
|
-
if (this.isBatching) {
|
|
401
|
-
this._startBatching();
|
|
402
|
-
}
|
|
403
|
-
else if (this.batchOnHandshake) {
|
|
404
|
-
this._startBatching();
|
|
405
|
-
setTimeout(() => {
|
|
406
|
-
if (!this.isBatching) {
|
|
407
|
-
this._stopBatching();
|
|
408
|
-
}
|
|
409
|
-
}, this.batchOnHandshakeDuration);
|
|
410
|
-
}
|
|
411
|
-
this.preparingPendingSubscriptions = true;
|
|
412
|
-
if (status) {
|
|
413
|
-
this.id = status.id;
|
|
414
|
-
this.pingTimeout = status.pingTimeout;
|
|
415
|
-
if (status.isAuthenticated) {
|
|
416
|
-
this._changeToAuthenticatedState(status.authToken);
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
this._changeToUnauthenticatedStateAndClearTokens();
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
else {
|
|
423
|
-
// This can happen if auth.loadToken (in transport.js) fails with
|
|
424
|
-
// an error - This means that the signedAuthToken cannot be loaded by
|
|
425
|
-
// the auth engine and therefore, we need to unauthenticate the client.
|
|
426
|
-
this._changeToUnauthenticatedStateAndClearTokens();
|
|
427
|
-
}
|
|
428
|
-
this.connectAttempts = 0;
|
|
429
|
-
if (this.options.autoSubscribeOnConnect) {
|
|
430
|
-
this.processPendingSubscriptions();
|
|
431
|
-
}
|
|
432
|
-
// If the user invokes the callback while in autoSubscribeOnConnect mode, it
|
|
433
|
-
// won't break anything.
|
|
434
|
-
this.emit('connect', {
|
|
435
|
-
...status,
|
|
436
|
-
processPendingSubscriptions: () => {
|
|
437
|
-
this.processPendingSubscriptions();
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
if (this.state === SocketState.OPEN) {
|
|
441
|
-
this._flushOutboundBuffer();
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
_onError(error) {
|
|
445
|
-
this.emit('error', { error });
|
|
446
|
-
}
|
|
447
|
-
_suspendSubscriptions() {
|
|
448
|
-
Object.keys(this._channelMap).forEach((channelName) => {
|
|
449
|
-
let channel = this._channelMap[channelName];
|
|
450
|
-
this._triggerChannelUnsubscribe(channel, true);
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
_abortAllPendingEventsDueToBadConnection(failureType) {
|
|
454
|
-
let currentNode = this._outboundBuffer.head;
|
|
455
|
-
let nextNode;
|
|
456
|
-
while (currentNode) {
|
|
457
|
-
nextNode = currentNode.next;
|
|
458
|
-
let eventObject = currentNode.data;
|
|
459
|
-
clearTimeout(eventObject.timeout);
|
|
460
|
-
delete eventObject.timeout;
|
|
461
|
-
currentNode.detach();
|
|
462
|
-
currentNode = nextNode;
|
|
463
|
-
let callback = eventObject.callback;
|
|
464
|
-
if (callback) {
|
|
465
|
-
delete eventObject.callback;
|
|
466
|
-
let errorMessage = `Event ${eventObject.event} was aborted due to a bad connection`;
|
|
467
|
-
let error = new BadConnectionError(errorMessage, failureType);
|
|
468
|
-
callback.call(eventObject, error, eventObject);
|
|
469
|
-
}
|
|
470
|
-
// Cleanup any pending response callback in the transport layer too.
|
|
471
|
-
if (eventObject.cid) {
|
|
472
|
-
this.transport.cancelPendingResponse(eventObject.cid);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
_destroy(code, reason, openAbort) {
|
|
477
|
-
this.id = null;
|
|
478
|
-
this._cancelBatching();
|
|
479
|
-
if (this.transport) {
|
|
480
|
-
this.transport.clearAllListeners();
|
|
481
|
-
}
|
|
482
|
-
this.pendingReconnect = false;
|
|
483
|
-
this.pendingReconnectTimeout = null;
|
|
484
|
-
clearTimeout(this._reconnectTimeoutRef);
|
|
485
|
-
this._suspendSubscriptions();
|
|
486
|
-
if (openAbort) {
|
|
487
|
-
this.emit('connectAbort', { code, reason });
|
|
488
|
-
}
|
|
489
|
-
else {
|
|
490
|
-
this.emit('disconnect', { code, reason });
|
|
491
|
-
}
|
|
492
|
-
this.emit('close', { code, reason });
|
|
493
|
-
if (!socketProtocolIgnoreStatuses[code]) {
|
|
494
|
-
let closeMessage;
|
|
495
|
-
if (typeof reason === 'string') {
|
|
496
|
-
closeMessage = 'Socket connection closed with status code ' + code + ' and reason: ' + reason;
|
|
497
|
-
}
|
|
498
|
-
else {
|
|
499
|
-
closeMessage = 'Socket connection closed with status code ' + code;
|
|
500
|
-
}
|
|
501
|
-
let err = new SocketProtocolError(socketProtocolErrorStatuses[code] || closeMessage, code);
|
|
502
|
-
this._onError(err);
|
|
503
|
-
}
|
|
504
|
-
this._abortAllPendingEventsDueToBadConnection(openAbort ? 'connectAbort' : 'disconnect');
|
|
505
|
-
// Try to reconnect
|
|
506
|
-
// on server ping timeout (4000)
|
|
507
|
-
// or on client pong timeout (4001)
|
|
508
|
-
// or on close without status (1005)
|
|
509
|
-
// or on handshake failure (4003)
|
|
510
|
-
// or on handshake rejection (4008)
|
|
511
|
-
// or on socket hung up (1006)
|
|
512
|
-
if (this.options.autoReconnect) {
|
|
513
|
-
if (code === 4000 || code === 4001 || code === 1005) {
|
|
514
|
-
// If there is a ping or pong timeout or socket closes without
|
|
515
|
-
// status, don't wait before trying to reconnect - These could happen
|
|
516
|
-
// if the client wakes up after a period of inactivity and in this case we
|
|
517
|
-
// want to re-establish the connection as soon as possible.
|
|
518
|
-
this._tryReconnect(0);
|
|
519
|
-
// Codes 4500 and above will be treated as permanent disconnects.
|
|
520
|
-
// Socket will not try to auto-reconnect.
|
|
521
|
-
}
|
|
522
|
-
else if (code !== 1000 && code < 4500) {
|
|
523
|
-
this._tryReconnect();
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
_onInboundTransmit(event, data) {
|
|
528
|
-
data = data || {};
|
|
529
|
-
if (event === '#publish') {
|
|
530
|
-
if (typeof data.channel !== 'string')
|
|
531
|
-
return;
|
|
532
|
-
let undecoratedChannelName = this._undecorateChannelName(data.channel);
|
|
533
|
-
let isSubscribed = this.isSubscribed(undecoratedChannelName, true);
|
|
534
|
-
if (isSubscribed) {
|
|
535
|
-
this._channelDataDemux.write(undecoratedChannelName, data.data);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
else if (event === '#kickOut') {
|
|
539
|
-
if (typeof data.channel !== 'string')
|
|
540
|
-
return;
|
|
541
|
-
let undecoratedChannelName = this._undecorateChannelName(data.channel);
|
|
542
|
-
let channel = this._channelMap[undecoratedChannelName];
|
|
543
|
-
if (channel) {
|
|
544
|
-
this.emit('kickOut', {
|
|
545
|
-
channel: undecoratedChannelName,
|
|
546
|
-
message: data.message
|
|
547
|
-
});
|
|
548
|
-
this._channelEventDemux.write(`${undecoratedChannelName}/kickOut`, { message: data.message });
|
|
549
|
-
this._triggerChannelUnsubscribe(channel);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
else if (event === '#setAuthToken') {
|
|
553
|
-
if (data) {
|
|
554
|
-
this._setAuthToken(data);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
else if (event === '#removeAuthToken') {
|
|
558
|
-
this._removeAuthToken();
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
this._receiverDemux.write(event, data);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
_onInboundInvoke(request) {
|
|
565
|
-
let { procedure, data } = request;
|
|
566
|
-
if (procedure === '#setAuthToken') {
|
|
567
|
-
if (data) {
|
|
568
|
-
this._setAuthToken(data);
|
|
569
|
-
request.end();
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
let error = new InvalidMessageError('No token data provided by #setAuthToken event');
|
|
573
|
-
delete error.stack;
|
|
574
|
-
request.error(error);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
else if (procedure === '#removeAuthToken') {
|
|
578
|
-
this._removeAuthToken();
|
|
579
|
-
request.end();
|
|
580
|
-
}
|
|
581
|
-
else {
|
|
582
|
-
this._procedureDemux.write(procedure, request);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
decode(message) {
|
|
586
|
-
return this.transport.decode(message);
|
|
587
|
-
}
|
|
588
|
-
encode(object) {
|
|
589
|
-
return this.transport.encode(object);
|
|
590
|
-
}
|
|
591
|
-
_flushOutboundBuffer() {
|
|
592
|
-
let currentNode = this._outboundBuffer.head;
|
|
593
|
-
let nextNode;
|
|
594
|
-
while (currentNode) {
|
|
595
|
-
nextNode = currentNode.next;
|
|
596
|
-
let eventObject = currentNode.data;
|
|
597
|
-
currentNode.detach();
|
|
598
|
-
this.transport.transmitObject(eventObject);
|
|
599
|
-
currentNode = nextNode;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
_handleEventAckTimeout(eventObject, eventNode) {
|
|
603
|
-
if (eventNode) {
|
|
604
|
-
eventNode.detach();
|
|
605
|
-
}
|
|
606
|
-
delete eventObject.timeout;
|
|
607
|
-
let callback = eventObject.callback;
|
|
608
|
-
if (callback) {
|
|
609
|
-
delete eventObject.callback;
|
|
610
|
-
let error = new TimeoutError(`Event response for ${eventObject.event} event timed out`);
|
|
611
|
-
callback.call(eventObject, error, eventObject);
|
|
612
|
-
}
|
|
613
|
-
// Cleanup any pending response callback in the transport layer too.
|
|
614
|
-
if (eventObject.cid) {
|
|
615
|
-
this.transport.cancelPendingResponse(eventObject.cid);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
_processOutboundEvent(event, data, options, expectResponse) {
|
|
619
|
-
options = options || {};
|
|
620
|
-
if (this.state === SocketState.CLOSED) {
|
|
621
|
-
this.connect();
|
|
622
|
-
}
|
|
623
|
-
let eventObject = {
|
|
624
|
-
event,
|
|
625
|
-
data: this.options.cloneData ? cloneDeep(data) : data
|
|
626
|
-
};
|
|
627
|
-
let promise;
|
|
628
|
-
if (expectResponse) {
|
|
629
|
-
promise = new Promise((resolve, reject) => {
|
|
630
|
-
eventObject.callback = (err, data) => {
|
|
631
|
-
if (err) {
|
|
632
|
-
reject(err);
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
resolve(data);
|
|
636
|
-
};
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
else {
|
|
640
|
-
promise = Promise.resolve();
|
|
641
|
-
}
|
|
642
|
-
let eventNode = new EventNode();
|
|
643
|
-
eventNode.data = eventObject;
|
|
644
|
-
let ackTimeout = options.ackTimeout == null ? this.ackTimeout : options.ackTimeout;
|
|
645
|
-
eventObject.timeout = setTimeout(() => {
|
|
646
|
-
this._handleEventAckTimeout(eventObject, eventNode);
|
|
647
|
-
}, ackTimeout);
|
|
648
|
-
this._outboundBuffer.append(eventNode);
|
|
649
|
-
if (this.state === SocketState.OPEN) {
|
|
650
|
-
this._flushOutboundBuffer();
|
|
651
|
-
}
|
|
652
|
-
return promise;
|
|
653
|
-
}
|
|
654
|
-
send(data) {
|
|
655
|
-
this.transport.send(data);
|
|
656
|
-
}
|
|
657
|
-
;
|
|
658
|
-
transmit(event, data, options) {
|
|
659
|
-
return this._processOutboundEvent(event, data, options);
|
|
660
|
-
}
|
|
661
|
-
invoke(event, data, options) {
|
|
662
|
-
return this._processOutboundEvent(event, data, options, true);
|
|
663
|
-
}
|
|
664
|
-
transmitPublish(channelName, data) {
|
|
665
|
-
let pubData = {
|
|
666
|
-
channel: this._decorateChannelName(channelName),
|
|
667
|
-
data
|
|
668
|
-
};
|
|
669
|
-
return this.transmit('#publish', pubData);
|
|
670
|
-
}
|
|
671
|
-
invokePublish(channelName, data) {
|
|
672
|
-
let pubData = {
|
|
673
|
-
channel: this._decorateChannelName(channelName),
|
|
674
|
-
data
|
|
675
|
-
};
|
|
676
|
-
return this.invoke('#publish', pubData);
|
|
677
|
-
}
|
|
678
|
-
_triggerChannelSubscribe(channel, subscriptionOptions) {
|
|
679
|
-
let channelName = channel.name;
|
|
680
|
-
if (channel.state !== ChannelState.SUBSCRIBED) {
|
|
681
|
-
let oldChannelState = channel.state;
|
|
682
|
-
channel.state = ChannelState.SUBSCRIBED;
|
|
683
|
-
let stateChangeData = {
|
|
684
|
-
oldChannelState,
|
|
685
|
-
newChannelState: channel.state,
|
|
686
|
-
subscriptionOptions
|
|
687
|
-
};
|
|
688
|
-
this._channelEventDemux.write(`${channelName}/subscribeStateChange`, stateChangeData);
|
|
689
|
-
this._channelEventDemux.write(`${channelName}/subscribe`, {
|
|
690
|
-
subscriptionOptions
|
|
691
|
-
});
|
|
692
|
-
this.emit('subscribeStateChange', {
|
|
693
|
-
channel: channelName,
|
|
694
|
-
...stateChangeData
|
|
695
|
-
});
|
|
696
|
-
this.emit('subscribe', {
|
|
697
|
-
channel: channelName,
|
|
698
|
-
subscriptionOptions
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
_triggerChannelSubscribeFail(err, channel, subscriptionOptions) {
|
|
703
|
-
let channelName = channel.name;
|
|
704
|
-
let meetsAuthRequirements = !channel.options.waitForAuth || this.authState === AuthState.AUTHENTICATED;
|
|
705
|
-
let hasChannel = !!this._channelMap[channelName];
|
|
706
|
-
if (hasChannel && meetsAuthRequirements) {
|
|
707
|
-
delete this._channelMap[channelName];
|
|
708
|
-
this._channelEventDemux.write(`${channelName}/subscribeFail`, {
|
|
709
|
-
error: err,
|
|
710
|
-
subscriptionOptions
|
|
711
|
-
});
|
|
712
|
-
this.emit('subscribeFail', {
|
|
713
|
-
error: err,
|
|
714
|
-
channel: channelName,
|
|
715
|
-
subscriptionOptions: subscriptionOptions
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
// Cancel any pending subscribe callback
|
|
720
|
-
_cancelPendingSubscribeCallback(channel) {
|
|
721
|
-
if (channel._pendingSubscriptionCid != null) {
|
|
722
|
-
this.transport.cancelPendingResponse(channel._pendingSubscriptionCid);
|
|
723
|
-
delete channel._pendingSubscriptionCid;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
_decorateChannelName(channelName) {
|
|
727
|
-
if (this.channelPrefix) {
|
|
728
|
-
channelName = this.channelPrefix + channelName;
|
|
729
|
-
}
|
|
730
|
-
return channelName;
|
|
731
|
-
}
|
|
732
|
-
_undecorateChannelName(decoratedChannelName) {
|
|
733
|
-
if (this.channelPrefix && decoratedChannelName.indexOf(this.channelPrefix) === 0) {
|
|
734
|
-
return decoratedChannelName.replace(this.channelPrefix, '');
|
|
735
|
-
}
|
|
736
|
-
return decoratedChannelName;
|
|
737
|
-
}
|
|
738
|
-
startBatch() {
|
|
739
|
-
this.transport.startBatch();
|
|
740
|
-
}
|
|
741
|
-
flushBatch() {
|
|
742
|
-
this.transport.flushBatch();
|
|
743
|
-
}
|
|
744
|
-
cancelBatch() {
|
|
745
|
-
this.transport.cancelBatch();
|
|
746
|
-
}
|
|
747
|
-
_startBatching() {
|
|
748
|
-
if (this._batchingIntervalId != null) {
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
this.startBatch();
|
|
752
|
-
this._batchingIntervalId = setInterval(() => {
|
|
753
|
-
this.flushBatch();
|
|
754
|
-
this.startBatch();
|
|
755
|
-
}, this.options.batchInterval);
|
|
756
|
-
}
|
|
757
|
-
startBatching() {
|
|
758
|
-
this.isBatching = true;
|
|
759
|
-
this._startBatching();
|
|
760
|
-
}
|
|
761
|
-
_stopBatching() {
|
|
762
|
-
if (this._batchingIntervalId != null) {
|
|
763
|
-
clearInterval(this._batchingIntervalId);
|
|
764
|
-
}
|
|
765
|
-
this._batchingIntervalId = null;
|
|
766
|
-
this.flushBatch();
|
|
767
|
-
}
|
|
768
|
-
stopBatching() {
|
|
769
|
-
this.isBatching = false;
|
|
770
|
-
this._stopBatching();
|
|
771
|
-
}
|
|
772
|
-
_cancelBatching() {
|
|
773
|
-
if (this._batchingIntervalId != null) {
|
|
774
|
-
clearInterval(this._batchingIntervalId);
|
|
775
|
-
}
|
|
776
|
-
this._batchingIntervalId = null;
|
|
777
|
-
this.cancelBatch();
|
|
778
|
-
}
|
|
779
|
-
cancelBatching() {
|
|
780
|
-
this.isBatching = false;
|
|
781
|
-
this._cancelBatching();
|
|
782
|
-
}
|
|
783
|
-
_trySubscribe(channel) {
|
|
784
|
-
let meetsAuthRequirements = !channel.options.waitForAuth || this.authState === AuthState.AUTHENTICATED;
|
|
785
|
-
// We can only ever have one pending subscribe action at any given time on a channel
|
|
786
|
-
if (this.state === SocketState.OPEN &&
|
|
787
|
-
!this.preparingPendingSubscriptions &&
|
|
788
|
-
channel._pendingSubscriptionCid == null &&
|
|
789
|
-
meetsAuthRequirements) {
|
|
790
|
-
let options = {
|
|
791
|
-
noTimeout: true
|
|
792
|
-
};
|
|
793
|
-
let subscriptionOptions = {};
|
|
794
|
-
if (channel.options.waitForAuth) {
|
|
795
|
-
subscriptionOptions.waitForAuth = true;
|
|
796
|
-
}
|
|
797
|
-
if (channel.options.data) {
|
|
798
|
-
subscriptionOptions.data = channel.options.data;
|
|
799
|
-
}
|
|
800
|
-
channel._pendingSubscriptionCid = this.transport.invokeRaw('#subscribe', {
|
|
801
|
-
channel: this._decorateChannelName(channel.name),
|
|
802
|
-
...subscriptionOptions
|
|
803
|
-
}, options, (err) => {
|
|
804
|
-
if (err) {
|
|
805
|
-
if (err.name === 'BadConnectionError') {
|
|
806
|
-
// In case of a failed connection, keep the subscription
|
|
807
|
-
// as pending; it will try again on reconnect.
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
delete channel._pendingSubscriptionCid;
|
|
811
|
-
this._triggerChannelSubscribeFail(err, channel, subscriptionOptions);
|
|
812
|
-
}
|
|
813
|
-
else {
|
|
814
|
-
delete channel._pendingSubscriptionCid;
|
|
815
|
-
this._triggerChannelSubscribe(channel, subscriptionOptions);
|
|
816
|
-
}
|
|
817
|
-
});
|
|
818
|
-
this.emit('subscribeRequest', {
|
|
819
|
-
channel: channel.name,
|
|
820
|
-
subscriptionOptions
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
emit(eventName, data) {
|
|
825
|
-
super.emit(eventName, data);
|
|
826
|
-
}
|
|
827
|
-
listen(eventName) {
|
|
828
|
-
return super.listen(eventName);
|
|
829
|
-
}
|
|
830
|
-
_triggerChannelUnsubscribe(channel, setAsPending) {
|
|
831
|
-
let channelName = channel.name;
|
|
832
|
-
this._cancelPendingSubscribeCallback(channel);
|
|
833
|
-
if (channel.state === ChannelState.SUBSCRIBED) {
|
|
834
|
-
let stateChangeData = {
|
|
835
|
-
oldChannelState: channel.state,
|
|
836
|
-
newChannelState: setAsPending ? ChannelState.PENDING : ChannelState.UNSUBSCRIBED
|
|
837
|
-
};
|
|
838
|
-
this._channelEventDemux.write(`${channelName}/subscribeStateChange`, stateChangeData);
|
|
839
|
-
this._channelEventDemux.write(`${channelName}/unsubscribe`, {});
|
|
840
|
-
this.emit('subscribeStateChange', {
|
|
841
|
-
channel: channelName,
|
|
842
|
-
...stateChangeData
|
|
843
|
-
});
|
|
844
|
-
this.emit('unsubscribe', { channel: channelName });
|
|
845
|
-
}
|
|
846
|
-
if (setAsPending) {
|
|
847
|
-
channel.state = ChannelState.PENDING;
|
|
848
|
-
}
|
|
849
|
-
else {
|
|
850
|
-
delete this._channelMap[channelName];
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
_tryUnsubscribe(channel) {
|
|
854
|
-
this._triggerChannelUnsubscribe(channel);
|
|
855
|
-
if (this.state === SocketState.OPEN) {
|
|
856
|
-
let options = {
|
|
857
|
-
noTimeout: true
|
|
858
|
-
};
|
|
859
|
-
// If there is a pending subscribe action, cancel the callback
|
|
860
|
-
this._cancelPendingSubscribeCallback(channel);
|
|
861
|
-
// This operation cannot fail because the TCP protocol guarantees delivery
|
|
862
|
-
// so long as the connection remains open. If the connection closes,
|
|
863
|
-
// the server will automatically unsubscribe the client and thus complete
|
|
864
|
-
// the operation on the server side.
|
|
865
|
-
let decoratedChannelName = this._decorateChannelName(channel.name);
|
|
866
|
-
this.transport.transmit('#unsubscribe', decoratedChannelName, options);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
// ---- Channel logic ----
|
|
870
|
-
processPendingSubscriptions() {
|
|
871
|
-
this.preparingPendingSubscriptions = false;
|
|
872
|
-
let pendingChannels = [];
|
|
873
|
-
Object.keys(this._channelMap).forEach((channelName) => {
|
|
874
|
-
let channel = this._channelMap[channelName];
|
|
875
|
-
if (channel.state === ChannelState.PENDING) {
|
|
876
|
-
pendingChannels.push(channel);
|
|
877
|
-
}
|
|
878
|
-
});
|
|
879
|
-
pendingChannels.sort((a, b) => {
|
|
880
|
-
let ap = a.options.priority || 0;
|
|
881
|
-
let bp = b.options.priority || 0;
|
|
882
|
-
if (ap > bp) {
|
|
883
|
-
return -1;
|
|
884
|
-
}
|
|
885
|
-
if (ap < bp) {
|
|
886
|
-
return 1;
|
|
887
|
-
}
|
|
888
|
-
return 0;
|
|
889
|
-
});
|
|
890
|
-
pendingChannels.forEach((channel) => {
|
|
891
|
-
this._trySubscribe(channel);
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
get isBufferingBatch() {
|
|
895
|
-
return this.transport.isBufferingBatch;
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
//# sourceMappingURL=clientsocket.js.map
|