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