@socket-mesh/server 17.3.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 +97 -0
- package/action.d.ts +109 -0
- package/action.js +112 -0
- package/events.d.ts +54 -0
- package/events.js +1 -0
- package/inbound-packet.d.ts +48 -0
- package/inbound-packet.js +18 -0
- package/index.d.ts +28 -0
- package/index.js +35 -0
- package/middleware-stream.d.ts +7 -0
- package/middleware-stream.js +7 -0
- package/middleware-type.d.ts +6 -0
- package/middleware-type.js +7 -0
- package/outbound-packet.d.ts +25 -0
- package/outbound-packet.js +3 -0
- package/package.json +60 -0
- package/request.d.ts +26 -0
- package/request.js +22 -0
- package/server-options.d.ts +41 -0
- package/server-options.js +1 -0
- package/server.d.ts +98 -0
- package/server.js +361 -0
- package/serversocket.d.ts +181 -0
- package/serversocket.js +1294 -0
- package/socket-state.d.ts +5 -0
- package/socket-state.js +6 -0
package/serversocket.js
ADDED
|
@@ -0,0 +1,1294 @@
|
|
|
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
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
11
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
12
|
+
var m = o[Symbol.asyncIterator], i;
|
|
13
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
14
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
15
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
16
|
+
};
|
|
17
|
+
import cloneDeep from 'clone-deep';
|
|
18
|
+
import { AuthState } from "@socket-mesh/auth";
|
|
19
|
+
import { WritableConsumableStream } from "@socket-mesh/writable-consumable-stream";
|
|
20
|
+
import { AsyncStreamEmitter } from "@socket-mesh/async-stream-emitter";
|
|
21
|
+
import { StreamDemux, StreamDemuxWrapper } from "@socket-mesh/stream-demux";
|
|
22
|
+
import { Request } from "@socket-mesh/request";
|
|
23
|
+
import { dehydrateError, hydrateError, socketProtocolIgnoreStatuses, socketProtocolErrorStatuses, InvalidArgumentsError, SocketProtocolError, TimeoutError, BadConnectionError, InvalidActionError, AuthError, AuthTokenExpiredError, AuthTokenInvalidError, AuthTokenNotBeforeError, AuthTokenError, BrokerError } from "@socket-mesh/errors";
|
|
24
|
+
import { SocketState } from "./socket-state";
|
|
25
|
+
import { MiddlewareType } from "./middleware-type";
|
|
26
|
+
import { ActionAuthenticate, ActionHandshakeSC, ActionInvoke, ActionMessage, ActionPublishIn, ActionPublishOut, ActionSubscribe, ActionTransmit } from "./action";
|
|
27
|
+
import { MiddlewareStream } from './middleware-stream';
|
|
28
|
+
import { isAuthenticatePacket, isHandshakePacket, isPublishPacket, isRemoveAuthTokenPacket, isSubscribePacket, isUnsubscribePacket } from './inbound-packet';
|
|
29
|
+
import { isPublishOutPacket } from './outbound-packet';
|
|
30
|
+
import { AuthenticateRequest, HandshakeRequest, UnsubscribeRequest, isPublishRequest, isSubscribeRequest } from './request';
|
|
31
|
+
const HANDSHAKE_REJECTION_STATUS_CODE = 4008;
|
|
32
|
+
export class ServerSocket extends AsyncStreamEmitter {
|
|
33
|
+
constructor(id, server, socket, protocolVersion) {
|
|
34
|
+
super();
|
|
35
|
+
this.id = id;
|
|
36
|
+
this.server = server;
|
|
37
|
+
this.socket = socket;
|
|
38
|
+
this.state = SocketState.CONNECTING;
|
|
39
|
+
this.authState = AuthState.UNAUTHENTICATED;
|
|
40
|
+
this.protocolVersion = protocolVersion;
|
|
41
|
+
this._receiverDemux = new StreamDemux();
|
|
42
|
+
this.receiver = new StreamDemuxWrapper(this._receiverDemux);
|
|
43
|
+
this._procedureDemux = new StreamDemux();
|
|
44
|
+
this.procedure = new StreamDemuxWrapper(this._procedureDemux);
|
|
45
|
+
this.request = this.socket.upgradeReq;
|
|
46
|
+
this.inboundReceivedMessageCount = 0;
|
|
47
|
+
this.inboundProcessedMessageCount = 0;
|
|
48
|
+
this.outboundPreparedMessageCount = 0;
|
|
49
|
+
this.outboundSentMessageCount = 0;
|
|
50
|
+
this.cloneData = this.server.options.cloneData;
|
|
51
|
+
this.inboundMessageStream = new WritableConsumableStream();
|
|
52
|
+
this.outboundPacketStream = new WritableConsumableStream();
|
|
53
|
+
this.middlewareHandshakeStream = this.request['handshakeStream'];
|
|
54
|
+
this.middlewareInboundRawStream = new MiddlewareStream(MiddlewareType.MIDDLEWARE_INBOUND_RAW);
|
|
55
|
+
this.middlewareInboundStream = new MiddlewareStream(MiddlewareType.MIDDLEWARE_INBOUND);
|
|
56
|
+
this.middlewareOutboundStream = new MiddlewareStream(MiddlewareType.MIDDLEWARE_OUTBOUND);
|
|
57
|
+
if (this.request.socket) {
|
|
58
|
+
this.remoteAddress = this.request.socket.remoteAddress;
|
|
59
|
+
this.remoteFamily = this.request.socket.remoteFamily;
|
|
60
|
+
this.remotePort = this.request.socket.remotePort;
|
|
61
|
+
}
|
|
62
|
+
// else {
|
|
63
|
+
// this.remoteAddress = this.request.remoteAddress;
|
|
64
|
+
// this.remoteFamily = this.request.remoteFamily;
|
|
65
|
+
// this.remotePort = this.request.remotePort;
|
|
66
|
+
//}
|
|
67
|
+
// if (this.request.forwardedForAddress) {
|
|
68
|
+
// this.forwardedForAddress = this.request.forwardedForAddress;
|
|
69
|
+
// }
|
|
70
|
+
this.isBufferingBatch = false;
|
|
71
|
+
this.isBatching = false;
|
|
72
|
+
this.batchOnHandshake = this.server.options.batchOnHandshake;
|
|
73
|
+
this.batchOnHandshakeDuration = this.server.options.batchOnHandshakeDuration;
|
|
74
|
+
this.batchInterval = this.server.options.batchInterval;
|
|
75
|
+
this._batchBuffer = [];
|
|
76
|
+
this._batchingIntervalId = null;
|
|
77
|
+
this._cid = 1;
|
|
78
|
+
this._callbackMap = {};
|
|
79
|
+
this.channelSubscriptions = {};
|
|
80
|
+
this.channelSubscriptionsCount = 0;
|
|
81
|
+
this.socket.on('error', (err) => {
|
|
82
|
+
this.emitError(err);
|
|
83
|
+
});
|
|
84
|
+
this.socket.on('close', (code, reasonBuffer) => {
|
|
85
|
+
let reason = reasonBuffer.toString();
|
|
86
|
+
this._destroy(code, reason);
|
|
87
|
+
});
|
|
88
|
+
let pongMessage;
|
|
89
|
+
if (this.protocolVersion === 1) {
|
|
90
|
+
pongMessage = '#2';
|
|
91
|
+
this._sendPing = () => {
|
|
92
|
+
if (this.state !== SocketState.CLOSED) {
|
|
93
|
+
this.send('#1');
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
pongMessage = '';
|
|
99
|
+
this._sendPing = () => {
|
|
100
|
+
if (this.state !== SocketState.CLOSED) {
|
|
101
|
+
this.send('');
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (!this.server.pingTimeoutDisabled) {
|
|
106
|
+
this._pingIntervalTicker = setInterval(() => {
|
|
107
|
+
this._sendPing();
|
|
108
|
+
}, this.server.pingInterval);
|
|
109
|
+
}
|
|
110
|
+
this._resetPongTimeout();
|
|
111
|
+
this._handshakeTimeoutRef = setTimeout(() => {
|
|
112
|
+
this._handleHandshakeTimeout();
|
|
113
|
+
}, this.server.handshakeTimeout);
|
|
114
|
+
this.server.pendingClients[this.id] = this;
|
|
115
|
+
this.server.pendingClientsCount++;
|
|
116
|
+
this._handleInboundMessageStream(pongMessage);
|
|
117
|
+
this._handleOutboundPacketStream();
|
|
118
|
+
// Receive incoming raw messages
|
|
119
|
+
this.socket.on('message', (messageBuffer, isBinary) => __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
let message = isBinary ? messageBuffer : messageBuffer.toString();
|
|
121
|
+
this.inboundReceivedMessageCount++;
|
|
122
|
+
let isPong = message === pongMessage;
|
|
123
|
+
if (isPong) {
|
|
124
|
+
this._resetPongTimeout();
|
|
125
|
+
}
|
|
126
|
+
if (this.server.hasMiddleware(MiddlewareType.MIDDLEWARE_INBOUND_RAW)) {
|
|
127
|
+
const action = new ActionMessage(this, message);
|
|
128
|
+
try {
|
|
129
|
+
const { data } = yield this.server._processMiddlewareAction(this.middlewareInboundRawStream, action, this);
|
|
130
|
+
message = data;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
this.inboundProcessedMessageCount++;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
this.inboundMessageStream.write(message);
|
|
138
|
+
this.emit('message', { message });
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
getBackpressure() {
|
|
142
|
+
return Math.max(this.getInboundBackpressure(), this.getOutboundBackpressure(), this.getListenerBackpressure(), this.receiver.getBackpressure(), this.procedure.getBackpressure());
|
|
143
|
+
}
|
|
144
|
+
;
|
|
145
|
+
getInboundBackpressure() {
|
|
146
|
+
return this.inboundReceivedMessageCount - this.inboundProcessedMessageCount;
|
|
147
|
+
}
|
|
148
|
+
;
|
|
149
|
+
getOutboundBackpressure() {
|
|
150
|
+
return this.outboundPreparedMessageCount - this.outboundSentMessageCount;
|
|
151
|
+
}
|
|
152
|
+
;
|
|
153
|
+
_startBatchOnHandshake() {
|
|
154
|
+
this._startBatching();
|
|
155
|
+
setTimeout(() => {
|
|
156
|
+
if (!this.isBatching) {
|
|
157
|
+
this._stopBatching();
|
|
158
|
+
}
|
|
159
|
+
}, this.batchOnHandshakeDuration);
|
|
160
|
+
}
|
|
161
|
+
;
|
|
162
|
+
_handleInboundMessageStream(pongMessage) {
|
|
163
|
+
var _a, e_1, _b, _c;
|
|
164
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
165
|
+
try {
|
|
166
|
+
for (var _d = true, _e = __asyncValues(this.inboundMessageStream), _f; _f = yield _e.next(), _a = _f.done, !_a;) {
|
|
167
|
+
_c = _f.value;
|
|
168
|
+
_d = false;
|
|
169
|
+
try {
|
|
170
|
+
let message = _c;
|
|
171
|
+
this.inboundProcessedMessageCount++;
|
|
172
|
+
let isPong = message === pongMessage;
|
|
173
|
+
if (isPong) {
|
|
174
|
+
if (this.server.strictHandshake && this.state === SocketState.CONNECTING) {
|
|
175
|
+
this._destroy(4009);
|
|
176
|
+
this.socket.close(4009);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
let token = this.getAuthToken();
|
|
180
|
+
if (this.isAuthTokenExpired(token)) {
|
|
181
|
+
this.deauthenticate();
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
let packet;
|
|
186
|
+
try {
|
|
187
|
+
packet = this.decode(message);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
if (error.name === 'Error') {
|
|
191
|
+
error.name = 'InvalidMessageError';
|
|
192
|
+
}
|
|
193
|
+
this.emitError(error);
|
|
194
|
+
if (this.server.strictHandshake && this.state === SocketState.CONNECTING) {
|
|
195
|
+
this._destroy(4009);
|
|
196
|
+
this.socket.close(4009);
|
|
197
|
+
}
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (Array.isArray(packet)) {
|
|
201
|
+
let len = packet.length;
|
|
202
|
+
for (let i = 0; i < len; i++) {
|
|
203
|
+
yield this._processInboundPacket(packet[i], message);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
yield this._processInboundPacket(packet, message);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
_d = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
216
|
+
finally {
|
|
217
|
+
try {
|
|
218
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
219
|
+
}
|
|
220
|
+
finally { if (e_1) throw e_1.error; }
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
;
|
|
225
|
+
_handleHandshakeTimeout() {
|
|
226
|
+
this.disconnect(4005);
|
|
227
|
+
}
|
|
228
|
+
;
|
|
229
|
+
_processHandshakeRequest(request) {
|
|
230
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
231
|
+
let data = request.data || { authToken: '' };
|
|
232
|
+
let signedAuthToken = data.authToken;
|
|
233
|
+
clearTimeout(this._handshakeTimeoutRef);
|
|
234
|
+
const authInfo = yield this._validateAuthToken(signedAuthToken);
|
|
235
|
+
const action = new ActionHandshakeSC(this, request, authInfo);
|
|
236
|
+
try {
|
|
237
|
+
yield this.server._processMiddlewareAction(this.middlewareHandshakeStream, action);
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
if (error.statusCode == null) {
|
|
241
|
+
error.statusCode = HANDSHAKE_REJECTION_STATUS_CODE;
|
|
242
|
+
}
|
|
243
|
+
request.error(error);
|
|
244
|
+
this.disconnect(error.statusCode);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const oldAuthState = this.authState;
|
|
248
|
+
let authError = undefined;
|
|
249
|
+
try {
|
|
250
|
+
yield this._processAuthentication(authInfo);
|
|
251
|
+
if (this.state === SocketState.CLOSED) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
if (signedAuthToken != null) {
|
|
257
|
+
// Because the token is optional as part of the handshake, we don't count
|
|
258
|
+
// it as an error if the token wasn't provided.
|
|
259
|
+
authError = dehydrateError(error);
|
|
260
|
+
if (error.isBadToken) {
|
|
261
|
+
this.deauthenticate();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const clientSocketStatus = {
|
|
266
|
+
id: this.id,
|
|
267
|
+
pingTimeout: this.server.pingTimeout,
|
|
268
|
+
isAuthenticated: !!this.authToken,
|
|
269
|
+
authError: authError
|
|
270
|
+
};
|
|
271
|
+
const serverSocketStatus = {
|
|
272
|
+
id: this.id,
|
|
273
|
+
pingTimeout: this.server.pingTimeout,
|
|
274
|
+
isAuthenticated: !!this.authToken,
|
|
275
|
+
authError: authError
|
|
276
|
+
};
|
|
277
|
+
if (this.server.pendingClients[this.id]) {
|
|
278
|
+
delete this.server.pendingClients[this.id];
|
|
279
|
+
this.server.pendingClientsCount--;
|
|
280
|
+
}
|
|
281
|
+
this.server.clients[this.id] = this;
|
|
282
|
+
this.server.clientsCount++;
|
|
283
|
+
this.state = SocketState.OPEN;
|
|
284
|
+
if (clientSocketStatus.isAuthenticated) {
|
|
285
|
+
// Needs to be executed after the connection event to allow
|
|
286
|
+
// consumers to be setup from inside the connection loop.
|
|
287
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
288
|
+
yield this.listen('connect').once();
|
|
289
|
+
this.triggerAuthenticationEvents(oldAuthState);
|
|
290
|
+
}))();
|
|
291
|
+
}
|
|
292
|
+
// Treat authentication failure as a 'soft' error
|
|
293
|
+
request.end(clientSocketStatus);
|
|
294
|
+
if (this.batchOnHandshake) {
|
|
295
|
+
this._startBatchOnHandshake();
|
|
296
|
+
}
|
|
297
|
+
this.emit('connect', serverSocketStatus);
|
|
298
|
+
this.server.emit('connection', Object.assign({ socket: this }, serverSocketStatus));
|
|
299
|
+
this.middlewareHandshakeStream.close();
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
_processAuthenticateRequest(request) {
|
|
303
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
304
|
+
let signedAuthToken = request.data;
|
|
305
|
+
let oldAuthState = this.authState;
|
|
306
|
+
let authInfo = yield this._validateAuthToken(signedAuthToken);
|
|
307
|
+
try {
|
|
308
|
+
yield this._processAuthentication(authInfo);
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
if (error instanceof AuthTokenError) {
|
|
312
|
+
this.deauthenticate();
|
|
313
|
+
request.error(error);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
request.end({
|
|
317
|
+
isAuthenticated: !!this.authToken,
|
|
318
|
+
authError: signedAuthToken == null ? null : dehydrateError(error)
|
|
319
|
+
});
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
this.triggerAuthenticationEvents(oldAuthState);
|
|
323
|
+
request.end({
|
|
324
|
+
isAuthenticated: !!this.authToken,
|
|
325
|
+
authError: null
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
_subscribeSocket(channelName, subscriptionOptions) {
|
|
330
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
331
|
+
if (channelName === undefined || !subscriptionOptions) {
|
|
332
|
+
throw new InvalidActionError(`Socket ${this.id} provided a malformated channel payload`);
|
|
333
|
+
}
|
|
334
|
+
if (this.server.socketChannelLimit && this.channelSubscriptionsCount >= this.server.socketChannelLimit) {
|
|
335
|
+
throw new InvalidActionError(`Socket ${this.id} tried to exceed the channel subscription limit of ${this.server.socketChannelLimit}`);
|
|
336
|
+
}
|
|
337
|
+
if (typeof channelName !== 'string') {
|
|
338
|
+
throw new InvalidActionError(`Socket ${this.id} provided an invalid channel name`);
|
|
339
|
+
}
|
|
340
|
+
if (this.channelSubscriptionsCount == null) {
|
|
341
|
+
this.channelSubscriptionsCount = 0;
|
|
342
|
+
}
|
|
343
|
+
if (this.channelSubscriptions[channelName] == null) {
|
|
344
|
+
this.channelSubscriptions[channelName] = true;
|
|
345
|
+
this.channelSubscriptionsCount++;
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
this.server.brokerEngine.subscribeSocket(this, channelName);
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
delete this.channelSubscriptions[channelName];
|
|
352
|
+
this.channelSubscriptionsCount--;
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
this.emit('subscribe', {
|
|
356
|
+
channel: channelName,
|
|
357
|
+
subscriptionOptions
|
|
358
|
+
});
|
|
359
|
+
this.server.emit('subscription', {
|
|
360
|
+
socket: this,
|
|
361
|
+
channel: channelName,
|
|
362
|
+
subscriptionOptions
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
;
|
|
367
|
+
_processSubscribeRequest(request) {
|
|
368
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
369
|
+
let subscriptionOptions = Object.assign({}, request.data);
|
|
370
|
+
let channelName = subscriptionOptions.channel;
|
|
371
|
+
delete subscriptionOptions.channel;
|
|
372
|
+
if (this.state === SocketState.OPEN) {
|
|
373
|
+
try {
|
|
374
|
+
yield this._subscribeSocket(channelName, subscriptionOptions);
|
|
375
|
+
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
let error = new BrokerError(`Failed to subscribe socket to the ${channelName} channel - ${err}`);
|
|
378
|
+
this.emitError(error);
|
|
379
|
+
request.error(error);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
request.end();
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
// This is an invalid state; it means the client tried to subscribe before
|
|
386
|
+
// having completed the handshake.
|
|
387
|
+
let error = new InvalidActionError('Cannot subscribe socket to a channel before it has completed the handshake');
|
|
388
|
+
this.emitError(error);
|
|
389
|
+
request.error(error);
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
_unsubscribeFromAllChannels() {
|
|
393
|
+
const channels = Object.keys(this.channelSubscriptions);
|
|
394
|
+
return Promise.all(channels.map((channel) => this._unsubscribe(channel)));
|
|
395
|
+
}
|
|
396
|
+
;
|
|
397
|
+
_unsubscribe(channel) {
|
|
398
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
399
|
+
if (typeof channel !== 'string') {
|
|
400
|
+
throw new InvalidActionError(`Socket ${this.id} tried to unsubscribe from an invalid channel name`);
|
|
401
|
+
}
|
|
402
|
+
if (!this.channelSubscriptions[channel]) {
|
|
403
|
+
throw new InvalidActionError(`Socket ${this.id} tried to unsubscribe from a channel which it is not subscribed to`);
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
yield this.server.brokerEngine.unsubscribeSocket(this, channel);
|
|
407
|
+
delete this.channelSubscriptions[channel];
|
|
408
|
+
if (this.channelSubscriptionsCount != null) {
|
|
409
|
+
this.channelSubscriptionsCount--;
|
|
410
|
+
}
|
|
411
|
+
this.emit('unsubscribe', { channel });
|
|
412
|
+
this.server.emit('unsubscription', { socket: this, channel });
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
const error = new BrokerError(`Failed to unsubscribe socket from the ${channel} channel - ${err}`);
|
|
416
|
+
this.emitError(error);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
;
|
|
421
|
+
_processUnsubscribePacket(packet) {
|
|
422
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
423
|
+
let channel = packet.data;
|
|
424
|
+
try {
|
|
425
|
+
yield this._unsubscribe(channel);
|
|
426
|
+
}
|
|
427
|
+
catch (err) {
|
|
428
|
+
let error = new BrokerError(`Failed to unsubscribe socket from the ${channel} channel - ${err}`);
|
|
429
|
+
this.emitError(error);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
;
|
|
434
|
+
_processUnsubscribeRequest(request) {
|
|
435
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
436
|
+
const channel = request.data;
|
|
437
|
+
try {
|
|
438
|
+
yield this._unsubscribe(channel);
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
let error = new BrokerError(`Failed to unsubscribe socket from the ${channel} channel - ${err}`);
|
|
442
|
+
this.emitError(error);
|
|
443
|
+
request.error(error);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
request.end();
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
_processInboundPublishPacket(packet) {
|
|
450
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
451
|
+
let data = packet.data || {};
|
|
452
|
+
if (!data.channel || typeof data.channel !== 'string') {
|
|
453
|
+
let error = new InvalidActionError(`Socket ${this.id} tried to invoke publish to an invalid "${data.channel}" channel`);
|
|
454
|
+
this.emitError(error);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
yield this.server.exchange.invokePublish(data.channel, data.data);
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
this.emitError(error);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
_processInboundPublishRequest(request) {
|
|
466
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
467
|
+
let data = request.data || {};
|
|
468
|
+
if (typeof data.channel !== 'string') {
|
|
469
|
+
let error = new InvalidActionError(`Socket ${this.id} tried to transmit publish to an invalid "${data.channel}" channel`);
|
|
470
|
+
this.emitError(error);
|
|
471
|
+
request.error(error);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
yield this.server.exchange.invokePublish(data.channel, data.data);
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
this.emitError(error);
|
|
479
|
+
request.error(error);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
request.end();
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
_processInboundPacket(packet, message) {
|
|
486
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
487
|
+
if (packet && packet.event != null) {
|
|
488
|
+
let isRPC = ('cid' in packet);
|
|
489
|
+
if (isHandshakePacket(packet)) {
|
|
490
|
+
const request = new HandshakeRequest(this, packet.cid, packet.event, packet.data);
|
|
491
|
+
yield this._processHandshakeRequest(request);
|
|
492
|
+
this._procedureDemux.write(request.procedure, request);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if (this.server.strictHandshake && this.state === SocketState.CONNECTING) {
|
|
496
|
+
this._destroy(4009);
|
|
497
|
+
this.socket.close(4009);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (isAuthenticatePacket(packet)) {
|
|
501
|
+
// Let AGServer handle these events.
|
|
502
|
+
const request = new AuthenticateRequest(this, packet.cid, packet.event, packet.data);
|
|
503
|
+
yield this._processAuthenticateRequest(request);
|
|
504
|
+
this._procedureDemux.write(request.procedure, request);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (isRemoveAuthTokenPacket(packet)) {
|
|
508
|
+
this.deauthenticateSelf();
|
|
509
|
+
this._receiverDemux.write(packet.event, packet.data);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
if (isUnsubscribePacket(packet)) {
|
|
513
|
+
if (isRPC) {
|
|
514
|
+
const request = new UnsubscribeRequest(this, packet.cid, packet.event, packet.data);
|
|
515
|
+
yield this._processUnsubscribeRequest(request);
|
|
516
|
+
this._procedureDemux.write(request.procedure, request);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
yield this._processUnsubscribePacket(packet);
|
|
520
|
+
this._receiverDemux.write(packet.event, packet.data);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
let action;
|
|
524
|
+
if (isPublishPacket(packet)) {
|
|
525
|
+
if (!this.server.allowClientPublish) {
|
|
526
|
+
let error = new InvalidActionError('Client publish feature is disabled');
|
|
527
|
+
this.emitError(error);
|
|
528
|
+
if (isRPC) {
|
|
529
|
+
const request = new Request(this, packet.cid, packet.event, packet.data);
|
|
530
|
+
request.error(error);
|
|
531
|
+
}
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
action = new ActionPublishIn(this, packet.data);
|
|
535
|
+
}
|
|
536
|
+
else if (isSubscribePacket(packet)) {
|
|
537
|
+
action = new ActionSubscribe(this, packet.data);
|
|
538
|
+
}
|
|
539
|
+
else if (isRPC) {
|
|
540
|
+
action = new ActionInvoke(this, packet.event, packet.data);
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
action = new ActionTransmit(this, packet.event, packet.data);
|
|
544
|
+
}
|
|
545
|
+
const tokenExpiredError = this._processAuthTokenExpiry();
|
|
546
|
+
if (tokenExpiredError) {
|
|
547
|
+
action.authTokenExpiredError = tokenExpiredError;
|
|
548
|
+
}
|
|
549
|
+
let newData;
|
|
550
|
+
if (isRPC) {
|
|
551
|
+
let request = new Request(this, packet.cid, packet.event, packet.data);
|
|
552
|
+
try {
|
|
553
|
+
let { data } = yield this.server._processMiddlewareAction(this.middlewareInboundStream, action, this);
|
|
554
|
+
newData = data;
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
request.error(error);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (isSubscribeRequest(request)) {
|
|
561
|
+
if (!request.data) {
|
|
562
|
+
request.data = {};
|
|
563
|
+
}
|
|
564
|
+
request.data.data = newData;
|
|
565
|
+
yield this._processSubscribeRequest(request);
|
|
566
|
+
}
|
|
567
|
+
else if (isPublishRequest(request)) {
|
|
568
|
+
if (!request.data) {
|
|
569
|
+
request.data = {};
|
|
570
|
+
}
|
|
571
|
+
request.data.data = newData;
|
|
572
|
+
yield this._processInboundPublishRequest(request);
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
request.data = newData;
|
|
576
|
+
}
|
|
577
|
+
this._procedureDemux.write(request.procedure, request);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
try {
|
|
581
|
+
let { data } = yield this.server._processMiddlewareAction(this.middlewareInboundStream, action, this);
|
|
582
|
+
newData = data;
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (isPublishPacket(packet)) {
|
|
588
|
+
if (!packet.data) {
|
|
589
|
+
packet.data = {};
|
|
590
|
+
}
|
|
591
|
+
packet.data.data = newData;
|
|
592
|
+
yield this._processInboundPublishPacket(packet);
|
|
593
|
+
}
|
|
594
|
+
this._receiverDemux.write(packet.event, newData);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
if (this.server.strictHandshake && this.state === SocketState.CONNECTING) {
|
|
598
|
+
this._destroy(4009);
|
|
599
|
+
this.socket.close(4009);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (packet && 'rid' in packet) {
|
|
603
|
+
// If incoming message is a response to a previously sent message
|
|
604
|
+
const ret = this._callbackMap[packet.rid];
|
|
605
|
+
if (ret) {
|
|
606
|
+
clearTimeout(ret.timeout);
|
|
607
|
+
delete this._callbackMap[packet.rid];
|
|
608
|
+
const rehydratedError = hydrateError(packet.error);
|
|
609
|
+
ret.callback(rehydratedError, packet.data);
|
|
610
|
+
}
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
// The last remaining case is to treat the message as raw
|
|
614
|
+
this.emit('raw', { message });
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
;
|
|
618
|
+
_resetPongTimeout() {
|
|
619
|
+
if (this.server.pingTimeoutDisabled) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
clearTimeout(this._pingTimeoutTicker);
|
|
623
|
+
this._pingTimeoutTicker = setTimeout(() => {
|
|
624
|
+
this._destroy(4001);
|
|
625
|
+
this.socket.close(4001);
|
|
626
|
+
}, this.server.pingTimeout);
|
|
627
|
+
}
|
|
628
|
+
_nextCallId() {
|
|
629
|
+
return this._cid++;
|
|
630
|
+
}
|
|
631
|
+
getState() {
|
|
632
|
+
return this.state;
|
|
633
|
+
}
|
|
634
|
+
getBytesReceived() {
|
|
635
|
+
return this.socket.bytesReceived;
|
|
636
|
+
}
|
|
637
|
+
emitError(error) {
|
|
638
|
+
this.emit('error', { error });
|
|
639
|
+
this.server.emitWarning(error);
|
|
640
|
+
}
|
|
641
|
+
_abortAllPendingEventsDueToBadConnection(failureType) {
|
|
642
|
+
Object.keys(this._callbackMap || {}).forEach((i) => {
|
|
643
|
+
const eventObject = this._callbackMap[i];
|
|
644
|
+
delete this._callbackMap[i];
|
|
645
|
+
clearTimeout(eventObject.timeout);
|
|
646
|
+
delete eventObject.timeout;
|
|
647
|
+
const errorMessage = `Event "${eventObject.event}" was aborted due to a bad connection`;
|
|
648
|
+
const badConnectionError = new BadConnectionError(errorMessage, failureType);
|
|
649
|
+
const callback = eventObject.callback;
|
|
650
|
+
delete eventObject.callback;
|
|
651
|
+
callback.call(eventObject, badConnectionError, eventObject);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
closeAllMiddlewares() {
|
|
655
|
+
this.middlewareHandshakeStream.close();
|
|
656
|
+
this.middlewareInboundRawStream.close();
|
|
657
|
+
this.middlewareInboundStream.close();
|
|
658
|
+
this.middlewareOutboundStream.close();
|
|
659
|
+
}
|
|
660
|
+
closeInput() {
|
|
661
|
+
this.inboundMessageStream.close();
|
|
662
|
+
}
|
|
663
|
+
closeOutput() {
|
|
664
|
+
this.outboundPacketStream.close();
|
|
665
|
+
}
|
|
666
|
+
closeIO() {
|
|
667
|
+
this.closeInput();
|
|
668
|
+
this.closeOutput();
|
|
669
|
+
}
|
|
670
|
+
closeAllStreams() {
|
|
671
|
+
this.closeAllMiddlewares();
|
|
672
|
+
this.closeIO();
|
|
673
|
+
this.receiver.close();
|
|
674
|
+
this.procedure.close();
|
|
675
|
+
this.closeListeners();
|
|
676
|
+
}
|
|
677
|
+
killAllMiddlewares() {
|
|
678
|
+
this.middlewareHandshakeStream.kill();
|
|
679
|
+
this.middlewareInboundRawStream.kill();
|
|
680
|
+
this.middlewareInboundStream.kill();
|
|
681
|
+
this.middlewareOutboundStream.kill();
|
|
682
|
+
}
|
|
683
|
+
killInput() {
|
|
684
|
+
this.inboundMessageStream.kill();
|
|
685
|
+
}
|
|
686
|
+
killOutput() {
|
|
687
|
+
this.outboundPacketStream.kill();
|
|
688
|
+
}
|
|
689
|
+
killIO() {
|
|
690
|
+
this.killInput();
|
|
691
|
+
this.killOutput();
|
|
692
|
+
}
|
|
693
|
+
killAllStreams() {
|
|
694
|
+
this.killAllMiddlewares();
|
|
695
|
+
this.killIO();
|
|
696
|
+
this.receiver.kill();
|
|
697
|
+
this.procedure.kill();
|
|
698
|
+
this.killListeners();
|
|
699
|
+
}
|
|
700
|
+
_destroy(code, reason) {
|
|
701
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
702
|
+
clearInterval(this._pingIntervalTicker);
|
|
703
|
+
clearTimeout(this._pingTimeoutTicker);
|
|
704
|
+
this._cancelBatching();
|
|
705
|
+
if (this.state === SocketState.CLOSED) {
|
|
706
|
+
this._abortAllPendingEventsDueToBadConnection('connectAbort');
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
if (!reason) {
|
|
710
|
+
reason = socketProtocolErrorStatuses[code];
|
|
711
|
+
}
|
|
712
|
+
const prevState = this.state;
|
|
713
|
+
this.state = SocketState.CLOSED;
|
|
714
|
+
if (prevState === SocketState.CONNECTING) {
|
|
715
|
+
this._abortAllPendingEventsDueToBadConnection('connectAbort');
|
|
716
|
+
this.emit('connectAbort', { code, reason });
|
|
717
|
+
this.server.emit('connectionAbort', { socket: this, code, reason });
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
this._abortAllPendingEventsDueToBadConnection('disconnect');
|
|
721
|
+
this.emit('disconnect', { code, reason });
|
|
722
|
+
this.server.emit('disconnection', { socket: this, code, reason });
|
|
723
|
+
}
|
|
724
|
+
this.emit('close', { code, reason });
|
|
725
|
+
this.server.emit('closure', { socket: this, code, reason });
|
|
726
|
+
clearTimeout(this._handshakeTimeoutRef);
|
|
727
|
+
let isClientFullyConnected = !!this.server.clients[this.id];
|
|
728
|
+
if (isClientFullyConnected) {
|
|
729
|
+
delete this.server.clients[this.id];
|
|
730
|
+
this.server.clientsCount--;
|
|
731
|
+
}
|
|
732
|
+
let isClientPending = !!this.server.pendingClients[this.id];
|
|
733
|
+
if (isClientPending) {
|
|
734
|
+
delete this.server.pendingClients[this.id];
|
|
735
|
+
this.server.pendingClientsCount--;
|
|
736
|
+
}
|
|
737
|
+
if (!socketProtocolIgnoreStatuses[code]) {
|
|
738
|
+
let closeMessage;
|
|
739
|
+
if (reason) {
|
|
740
|
+
closeMessage = `Socket connection closed with status code ${code} and reason: ${reason}`;
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
closeMessage = `Socket connection closed with status code ${code}`;
|
|
744
|
+
}
|
|
745
|
+
let err = new SocketProtocolError(socketProtocolErrorStatuses[code] || closeMessage, code);
|
|
746
|
+
this.emitError(err);
|
|
747
|
+
}
|
|
748
|
+
yield this._unsubscribeFromAllChannels();
|
|
749
|
+
let cleanupMode = this.server.options.socketStreamCleanupMode;
|
|
750
|
+
if (cleanupMode === 'kill') {
|
|
751
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
752
|
+
yield this.listen('end').once();
|
|
753
|
+
this.killAllStreams();
|
|
754
|
+
}))();
|
|
755
|
+
}
|
|
756
|
+
else if (cleanupMode === 'close') {
|
|
757
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
758
|
+
yield this.listen('end').once();
|
|
759
|
+
this.closeAllStreams();
|
|
760
|
+
}))();
|
|
761
|
+
}
|
|
762
|
+
this.emit('end');
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
disconnect(code, reason) {
|
|
767
|
+
code = code || 1000;
|
|
768
|
+
if (typeof code !== 'number') {
|
|
769
|
+
let err = new InvalidArgumentsError('If specified, the code argument must be a number');
|
|
770
|
+
this.emitError(err);
|
|
771
|
+
}
|
|
772
|
+
if (this.state !== SocketState.CLOSED) {
|
|
773
|
+
this._destroy(code, reason);
|
|
774
|
+
this.socket.close(code, reason);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
terminate() {
|
|
778
|
+
this.socket.terminate();
|
|
779
|
+
}
|
|
780
|
+
send(data, options) {
|
|
781
|
+
const cb = (error) => {
|
|
782
|
+
if (error) {
|
|
783
|
+
this.emitError(error);
|
|
784
|
+
this._destroy(1006, error.toString());
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
if (options) {
|
|
788
|
+
this.socket.send(data, options, cb);
|
|
789
|
+
}
|
|
790
|
+
else {
|
|
791
|
+
this.socket.send(data, cb);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
decode(message) {
|
|
795
|
+
return this.server.codec.decode(message);
|
|
796
|
+
}
|
|
797
|
+
encode(object) {
|
|
798
|
+
return this.server.codec.encode(object);
|
|
799
|
+
}
|
|
800
|
+
startBatch() {
|
|
801
|
+
this.isBufferingBatch = true;
|
|
802
|
+
this._batchBuffer = [];
|
|
803
|
+
}
|
|
804
|
+
flushBatch() {
|
|
805
|
+
this.isBufferingBatch = false;
|
|
806
|
+
if (!this._batchBuffer.length) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
let serializedBatch = this.serializeObject(this._batchBuffer);
|
|
810
|
+
this._batchBuffer = [];
|
|
811
|
+
this.send(serializedBatch);
|
|
812
|
+
}
|
|
813
|
+
cancelBatch() {
|
|
814
|
+
this.isBufferingBatch = false;
|
|
815
|
+
this._batchBuffer = [];
|
|
816
|
+
}
|
|
817
|
+
_startBatching() {
|
|
818
|
+
if (this._batchingIntervalId != null) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
this.startBatch();
|
|
822
|
+
this._batchingIntervalId = setInterval(() => {
|
|
823
|
+
this.flushBatch();
|
|
824
|
+
this.startBatch();
|
|
825
|
+
}, this.batchInterval);
|
|
826
|
+
}
|
|
827
|
+
startBatching() {
|
|
828
|
+
this.isBatching = true;
|
|
829
|
+
this._startBatching();
|
|
830
|
+
}
|
|
831
|
+
_stopBatching() {
|
|
832
|
+
if (this._batchingIntervalId != null) {
|
|
833
|
+
clearInterval(this._batchingIntervalId);
|
|
834
|
+
}
|
|
835
|
+
this._batchingIntervalId = null;
|
|
836
|
+
this.flushBatch();
|
|
837
|
+
}
|
|
838
|
+
stopBatching() {
|
|
839
|
+
this.isBatching = false;
|
|
840
|
+
this._stopBatching();
|
|
841
|
+
}
|
|
842
|
+
_cancelBatching() {
|
|
843
|
+
if (this._batchingIntervalId != null) {
|
|
844
|
+
clearInterval(this._batchingIntervalId);
|
|
845
|
+
}
|
|
846
|
+
this._batchingIntervalId = null;
|
|
847
|
+
this.cancelBatch();
|
|
848
|
+
}
|
|
849
|
+
cancelBatching() {
|
|
850
|
+
this.isBatching = false;
|
|
851
|
+
this._cancelBatching();
|
|
852
|
+
}
|
|
853
|
+
;
|
|
854
|
+
serializeObject(object) {
|
|
855
|
+
let str;
|
|
856
|
+
try {
|
|
857
|
+
str = this.encode(object);
|
|
858
|
+
}
|
|
859
|
+
catch (error) {
|
|
860
|
+
this.emitError(error);
|
|
861
|
+
return null;
|
|
862
|
+
}
|
|
863
|
+
return str;
|
|
864
|
+
}
|
|
865
|
+
;
|
|
866
|
+
sendObject(object) {
|
|
867
|
+
if (this.isBufferingBatch) {
|
|
868
|
+
this._batchBuffer.push(object);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
let str = this.serializeObject(object);
|
|
872
|
+
if (str != null) {
|
|
873
|
+
this.send(str);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
_handleOutboundPacketStream() {
|
|
877
|
+
var _a, e_2, _b, _c;
|
|
878
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
879
|
+
try {
|
|
880
|
+
for (var _d = true, _e = __asyncValues(this.outboundPacketStream), _f; _f = yield _e.next(), _a = _f.done, !_a;) {
|
|
881
|
+
_c = _f.value;
|
|
882
|
+
_d = false;
|
|
883
|
+
try {
|
|
884
|
+
let packet = _c;
|
|
885
|
+
if ('resolve' in packet) {
|
|
886
|
+
// Invoke has no middleware, so there is no need to await here.
|
|
887
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
888
|
+
let result;
|
|
889
|
+
try {
|
|
890
|
+
result = yield this._invoke(packet);
|
|
891
|
+
}
|
|
892
|
+
catch (error) {
|
|
893
|
+
packet.reject(error);
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
packet.resolve(result);
|
|
897
|
+
}))();
|
|
898
|
+
this.outboundSentMessageCount++;
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
yield this._processTransmit(packet);
|
|
902
|
+
this.outboundSentMessageCount++;
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
_d = true;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
910
|
+
finally {
|
|
911
|
+
try {
|
|
912
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
913
|
+
}
|
|
914
|
+
finally { if (e_2) throw e_2.error; }
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
_transmit(event, data, options) {
|
|
919
|
+
if (this.cloneData) {
|
|
920
|
+
data = cloneDeep(data);
|
|
921
|
+
}
|
|
922
|
+
this.outboundPreparedMessageCount++;
|
|
923
|
+
this.outboundPacketStream.write({
|
|
924
|
+
event,
|
|
925
|
+
data,
|
|
926
|
+
options
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
transmit(event, data, options) {
|
|
930
|
+
if (this.state !== SocketState.OPEN) {
|
|
931
|
+
let error = new BadConnectionError(`Socket transmit "${event}" was aborted due to a bad connection`, 'connectAbort');
|
|
932
|
+
this.emitError(error);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
this._transmit(event, data, options);
|
|
936
|
+
}
|
|
937
|
+
invoke(event, data, options) {
|
|
938
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
939
|
+
if (this.state !== SocketState.OPEN) {
|
|
940
|
+
const error = new BadConnectionError(`Socket invoke "${event}" was aborted due to a bad connection`, 'connectAbort');
|
|
941
|
+
this.emitError(error);
|
|
942
|
+
throw error;
|
|
943
|
+
}
|
|
944
|
+
if (this.cloneData) {
|
|
945
|
+
data = cloneDeep(data);
|
|
946
|
+
}
|
|
947
|
+
this.outboundPreparedMessageCount++;
|
|
948
|
+
return new Promise((resolve, reject) => {
|
|
949
|
+
this.outboundPacketStream.write({
|
|
950
|
+
event,
|
|
951
|
+
data,
|
|
952
|
+
options,
|
|
953
|
+
resolve,
|
|
954
|
+
reject
|
|
955
|
+
});
|
|
956
|
+
});
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
emit(eventName, data) {
|
|
960
|
+
super.emit(eventName, data);
|
|
961
|
+
}
|
|
962
|
+
listen(eventName) {
|
|
963
|
+
return super.listen(eventName);
|
|
964
|
+
}
|
|
965
|
+
_processTransmit(packet) {
|
|
966
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
967
|
+
let newData;
|
|
968
|
+
let useCache = packet.options ? packet.options.useCache : false;
|
|
969
|
+
if (isPublishOutPacket(packet)) {
|
|
970
|
+
const action = new ActionPublishOut(this);
|
|
971
|
+
if (packet.data !== undefined) {
|
|
972
|
+
action.channel = packet.data.channel;
|
|
973
|
+
action.data = packet.data.data;
|
|
974
|
+
}
|
|
975
|
+
useCache = !this.server.hasMiddleware(this.middlewareOutboundStream.type);
|
|
976
|
+
try {
|
|
977
|
+
const { data, options } = yield this.server._processMiddlewareAction(this.middlewareOutboundStream, action, this);
|
|
978
|
+
newData = data;
|
|
979
|
+
useCache = options == null ? useCache : options.useCache;
|
|
980
|
+
}
|
|
981
|
+
catch (error) {
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
else {
|
|
986
|
+
newData = packet.data;
|
|
987
|
+
}
|
|
988
|
+
if (packet.options && useCache && packet.options.stringifiedData != null && !this.isBufferingBatch) {
|
|
989
|
+
// Optimized
|
|
990
|
+
this.send(packet.options.stringifiedData);
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
const eventObject = {
|
|
994
|
+
event: packet.event
|
|
995
|
+
};
|
|
996
|
+
if (isPublishOutPacket(packet)) {
|
|
997
|
+
eventObject.data = packet.data || {};
|
|
998
|
+
eventObject.data.data = newData;
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
eventObject.data = newData;
|
|
1002
|
+
}
|
|
1003
|
+
this.sendObject(eventObject);
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
_invoke({ event, data, options }) {
|
|
1008
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1009
|
+
options = options || {};
|
|
1010
|
+
return new Promise((resolve, reject) => {
|
|
1011
|
+
const eventObject = {
|
|
1012
|
+
event,
|
|
1013
|
+
cid: this._nextCallId()
|
|
1014
|
+
};
|
|
1015
|
+
if (data !== undefined) {
|
|
1016
|
+
eventObject.data = data;
|
|
1017
|
+
}
|
|
1018
|
+
const ackTimeout = options.ackTimeout == null ? this.server.ackTimeout : options.ackTimeout;
|
|
1019
|
+
const timeout = setTimeout(() => {
|
|
1020
|
+
let error = new TimeoutError(`Event response for "${event}" timed out`);
|
|
1021
|
+
delete this._callbackMap[eventObject.cid];
|
|
1022
|
+
reject(error);
|
|
1023
|
+
}, ackTimeout);
|
|
1024
|
+
this._callbackMap[eventObject.cid] = {
|
|
1025
|
+
event,
|
|
1026
|
+
callback: (err, result) => {
|
|
1027
|
+
if (err) {
|
|
1028
|
+
reject(err);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
resolve(result);
|
|
1032
|
+
},
|
|
1033
|
+
timeout
|
|
1034
|
+
};
|
|
1035
|
+
if (options.useCache && options.stringifiedData != null && !this.isBufferingBatch) {
|
|
1036
|
+
// Optimized
|
|
1037
|
+
this.send(options.stringifiedData);
|
|
1038
|
+
}
|
|
1039
|
+
else {
|
|
1040
|
+
this.sendObject(eventObject);
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
triggerAuthenticationEvents(oldAuthState) {
|
|
1046
|
+
if (oldAuthState !== AuthState.AUTHENTICATED) {
|
|
1047
|
+
let stateChangeData = {
|
|
1048
|
+
oldAuthState,
|
|
1049
|
+
newAuthState: this.authState,
|
|
1050
|
+
authToken: this.authToken
|
|
1051
|
+
};
|
|
1052
|
+
this.emit('authStateChange', stateChangeData);
|
|
1053
|
+
this.server.emit('authenticationStateChange', Object.assign({ socket: this }, stateChangeData));
|
|
1054
|
+
}
|
|
1055
|
+
this.emit('authenticate', { authToken: this.authToken });
|
|
1056
|
+
this.server.emit('authentication', {
|
|
1057
|
+
socket: this,
|
|
1058
|
+
authToken: this.authToken
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
setAuthToken(data, options) {
|
|
1062
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1063
|
+
if (this.state === SocketState.CONNECTING) {
|
|
1064
|
+
const err = new InvalidActionError('Cannot call setAuthToken before completing the handshake');
|
|
1065
|
+
this.emitError(err);
|
|
1066
|
+
throw err;
|
|
1067
|
+
}
|
|
1068
|
+
const authToken = cloneDeep(data);
|
|
1069
|
+
const oldAuthState = this.authState;
|
|
1070
|
+
this.authState = AuthState.AUTHENTICATED;
|
|
1071
|
+
if (options == null) {
|
|
1072
|
+
options = {};
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
options = Object.assign({}, options);
|
|
1076
|
+
if (options.algorithm != null) {
|
|
1077
|
+
delete options.algorithm;
|
|
1078
|
+
let err = new InvalidArgumentsError('Cannot change auth token algorithm at runtime - It must be specified as a config option on launch');
|
|
1079
|
+
this.emitError(err);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
options.mutatePayload = true;
|
|
1083
|
+
const rejectOnFailedDelivery = options.rejectOnFailedDelivery;
|
|
1084
|
+
delete options.rejectOnFailedDelivery;
|
|
1085
|
+
const defaultSignatureOptions = this.server.defaultSignatureOptions;
|
|
1086
|
+
// We cannot have the exp claim on the token and the expiresIn option
|
|
1087
|
+
// set at the same time or else auth.signToken will throw an error.
|
|
1088
|
+
let expiresIn;
|
|
1089
|
+
if (options.expiresIn == null) {
|
|
1090
|
+
expiresIn = defaultSignatureOptions.expiresIn;
|
|
1091
|
+
}
|
|
1092
|
+
else {
|
|
1093
|
+
expiresIn = options.expiresIn;
|
|
1094
|
+
}
|
|
1095
|
+
if (authToken) {
|
|
1096
|
+
if (authToken.exp == null) {
|
|
1097
|
+
options.expiresIn = expiresIn;
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
delete options.expiresIn;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
options.expiresIn = expiresIn;
|
|
1105
|
+
}
|
|
1106
|
+
// Always use the default algorithm since it cannot be changed at runtime.
|
|
1107
|
+
if (defaultSignatureOptions.algorithm != null) {
|
|
1108
|
+
options.algorithm = defaultSignatureOptions.algorithm;
|
|
1109
|
+
}
|
|
1110
|
+
this.authToken = authToken;
|
|
1111
|
+
let signedAuthToken;
|
|
1112
|
+
try {
|
|
1113
|
+
signedAuthToken = yield this.server.auth.signToken(authToken, this.server.signatureKey, options);
|
|
1114
|
+
}
|
|
1115
|
+
catch (error) {
|
|
1116
|
+
this.emitError(error);
|
|
1117
|
+
this._destroy(4002, error.toString());
|
|
1118
|
+
this.socket.close(4002);
|
|
1119
|
+
throw error;
|
|
1120
|
+
}
|
|
1121
|
+
if (this.authToken === authToken) {
|
|
1122
|
+
this.signedAuthToken = signedAuthToken;
|
|
1123
|
+
this.emit('authTokenSigned', { signedAuthToken });
|
|
1124
|
+
}
|
|
1125
|
+
this.triggerAuthenticationEvents(oldAuthState);
|
|
1126
|
+
const tokenData = {
|
|
1127
|
+
token: signedAuthToken
|
|
1128
|
+
};
|
|
1129
|
+
if (rejectOnFailedDelivery) {
|
|
1130
|
+
try {
|
|
1131
|
+
yield this.invoke('#setAuthToken', tokenData);
|
|
1132
|
+
}
|
|
1133
|
+
catch (err) {
|
|
1134
|
+
const error = new AuthError(`Failed to deliver auth token to client - ${err}`);
|
|
1135
|
+
this.emitError(error);
|
|
1136
|
+
throw error;
|
|
1137
|
+
}
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
this.transmit('#setAuthToken', tokenData);
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
getAuthToken() {
|
|
1144
|
+
return this.authToken;
|
|
1145
|
+
}
|
|
1146
|
+
deauthenticateSelf() {
|
|
1147
|
+
const oldAuthState = this.authState;
|
|
1148
|
+
const oldAuthToken = this.authToken;
|
|
1149
|
+
this.signedAuthToken = null;
|
|
1150
|
+
this.authToken = null;
|
|
1151
|
+
this.authState = AuthState.UNAUTHENTICATED;
|
|
1152
|
+
if (oldAuthState !== AuthState.UNAUTHENTICATED) {
|
|
1153
|
+
const stateChangeData = {
|
|
1154
|
+
oldAuthState,
|
|
1155
|
+
newAuthState: this.authState
|
|
1156
|
+
};
|
|
1157
|
+
this.emit('authStateChange', stateChangeData);
|
|
1158
|
+
this.server.emit('authenticationStateChange', Object.assign({ socket: this }, stateChangeData));
|
|
1159
|
+
}
|
|
1160
|
+
this.emit('deauthenticate', { oldAuthToken });
|
|
1161
|
+
this.server.emit('deauthentication', {
|
|
1162
|
+
socket: this,
|
|
1163
|
+
oldAuthToken
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
deauthenticate(options) {
|
|
1167
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1168
|
+
this.deauthenticateSelf();
|
|
1169
|
+
if (options && options.rejectOnFailedDelivery) {
|
|
1170
|
+
try {
|
|
1171
|
+
yield this.invoke('#removeAuthToken');
|
|
1172
|
+
}
|
|
1173
|
+
catch (error) {
|
|
1174
|
+
this.emitError(error);
|
|
1175
|
+
if (options && options.rejectOnFailedDelivery) {
|
|
1176
|
+
throw error;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
this._transmit('#removeAuthToken');
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
kickOut(channel, message) {
|
|
1185
|
+
const channels = channel ? [channel] : Object.keys(this.channelSubscriptions);
|
|
1186
|
+
return Promise.all(channels.map((channelName) => {
|
|
1187
|
+
this.transmit('#kickOut', { channel: channelName, message });
|
|
1188
|
+
return this._unsubscribe(channelName);
|
|
1189
|
+
}));
|
|
1190
|
+
}
|
|
1191
|
+
subscriptions() {
|
|
1192
|
+
return Object.keys(this.channelSubscriptions);
|
|
1193
|
+
}
|
|
1194
|
+
isSubscribed(channel) {
|
|
1195
|
+
return !!this.channelSubscriptions[channel];
|
|
1196
|
+
}
|
|
1197
|
+
_processAuthTokenExpiry() {
|
|
1198
|
+
const token = this.getAuthToken();
|
|
1199
|
+
if (this.isAuthTokenExpired(token)) {
|
|
1200
|
+
this.deauthenticate();
|
|
1201
|
+
return new AuthTokenExpiredError('The socket auth token has expired', new Date(token.exp));
|
|
1202
|
+
}
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
isAuthTokenExpired(token) {
|
|
1206
|
+
if ((token === null || token === void 0 ? void 0 : token.exp) != null) {
|
|
1207
|
+
const currentTime = Date.now();
|
|
1208
|
+
const expiryMilliseconds = token.exp * 1000;
|
|
1209
|
+
return currentTime > expiryMilliseconds;
|
|
1210
|
+
}
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
_processTokenError(err) {
|
|
1214
|
+
if (err.name === 'TokenExpiredError' && 'expiredAt' in err) {
|
|
1215
|
+
return new AuthTokenExpiredError(err.message, err.expiredAt);
|
|
1216
|
+
}
|
|
1217
|
+
if (err.name === 'JsonWebTokenError') {
|
|
1218
|
+
return new AuthTokenInvalidError(err.message);
|
|
1219
|
+
}
|
|
1220
|
+
if (err.name === 'NotBeforeError' && 'date' in err) {
|
|
1221
|
+
// In this case, the token is good; it's just not active yet.
|
|
1222
|
+
return new AuthTokenNotBeforeError(err.message, err.date);
|
|
1223
|
+
}
|
|
1224
|
+
return new AuthTokenError(err.message);
|
|
1225
|
+
}
|
|
1226
|
+
_emitBadAuthTokenError(error, signedAuthToken) {
|
|
1227
|
+
this.emit('badAuthToken', {
|
|
1228
|
+
authError: error,
|
|
1229
|
+
signedAuthToken
|
|
1230
|
+
});
|
|
1231
|
+
this.server.emit('badSocketAuthToken', {
|
|
1232
|
+
socket: this,
|
|
1233
|
+
authError: error,
|
|
1234
|
+
signedAuthToken
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
_validateAuthToken(signedAuthToken) {
|
|
1238
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1239
|
+
const verificationOptions = Object.assign({}, this.server.defaultVerificationOptions);
|
|
1240
|
+
let authToken;
|
|
1241
|
+
try {
|
|
1242
|
+
authToken = yield this.server.auth.verifyToken(signedAuthToken, this.server.verificationKey, verificationOptions);
|
|
1243
|
+
}
|
|
1244
|
+
catch (error) {
|
|
1245
|
+
const authTokenError = this._processTokenError(error);
|
|
1246
|
+
return {
|
|
1247
|
+
signedAuthToken,
|
|
1248
|
+
authTokenError,
|
|
1249
|
+
authToken: null,
|
|
1250
|
+
authState: AuthState.UNAUTHENTICATED
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
return {
|
|
1254
|
+
signedAuthToken,
|
|
1255
|
+
authTokenError: null,
|
|
1256
|
+
authToken,
|
|
1257
|
+
authState: AuthState.AUTHENTICATED
|
|
1258
|
+
};
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
_processAuthentication({ signedAuthToken, authTokenError, authToken }) {
|
|
1262
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1263
|
+
if (authTokenError) {
|
|
1264
|
+
this.signedAuthToken = null;
|
|
1265
|
+
this.authToken = null;
|
|
1266
|
+
this.authState = AuthState.UNAUTHENTICATED;
|
|
1267
|
+
// If the error is related to the JWT being badly formatted, then we will
|
|
1268
|
+
// treat the error as a socket error.
|
|
1269
|
+
if (signedAuthToken != null) {
|
|
1270
|
+
this.emitError(authTokenError);
|
|
1271
|
+
if (authTokenError instanceof AuthTokenError) {
|
|
1272
|
+
this._emitBadAuthTokenError(authTokenError, signedAuthToken);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
throw authTokenError;
|
|
1276
|
+
}
|
|
1277
|
+
this.signedAuthToken = signedAuthToken;
|
|
1278
|
+
this.authToken = authToken;
|
|
1279
|
+
this.authState = AuthState.AUTHENTICATED;
|
|
1280
|
+
const action = new ActionAuthenticate(this, authToken, signedAuthToken);
|
|
1281
|
+
try {
|
|
1282
|
+
yield this.server._processMiddlewareAction(this.middlewareInboundStream, action, this);
|
|
1283
|
+
}
|
|
1284
|
+
catch (error) {
|
|
1285
|
+
this.authToken = null;
|
|
1286
|
+
this.authState = AuthState.UNAUTHENTICATED;
|
|
1287
|
+
if (error instanceof AuthTokenError) {
|
|
1288
|
+
this._emitBadAuthTokenError(error, signedAuthToken);
|
|
1289
|
+
}
|
|
1290
|
+
throw error;
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|