@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/request.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Request } from "@socket-mesh/request";
|
|
2
|
+
export class HandshakeRequest extends Request {
|
|
3
|
+
constructor(socket, id, procedureName, data) {
|
|
4
|
+
super(socket, id, procedureName, data);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class UnsubscribeRequest extends Request {
|
|
8
|
+
constructor(socket, id, procedureName, data) {
|
|
9
|
+
super(socket, id, procedureName, data);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class AuthenticateRequest extends Request {
|
|
13
|
+
constructor(socket, id, procedureName, data) {
|
|
14
|
+
super(socket, id, procedureName, data);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function isSubscribeRequest(request) {
|
|
18
|
+
return request.procedure === '#subscribe';
|
|
19
|
+
}
|
|
20
|
+
export function isPublishRequest(request) {
|
|
21
|
+
return request.procedure === '#publish';
|
|
22
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { CodecEngine } from "@socket-mesh/formatter";
|
|
3
|
+
import ws from "ws";
|
|
4
|
+
import jwt from "jsonwebtoken";
|
|
5
|
+
import { Server as HttpServer } from 'http';
|
|
6
|
+
import { AuthEngine } from "@socket-mesh/server-auth";
|
|
7
|
+
export type ProtocolVersions = 1 | 2;
|
|
8
|
+
export interface ServerOptions {
|
|
9
|
+
httpServer?: HttpServer;
|
|
10
|
+
wsEngine?: string | {
|
|
11
|
+
Server: ws.Server;
|
|
12
|
+
};
|
|
13
|
+
wsEngineServerOptions?: ws.ServerOptions;
|
|
14
|
+
authKey?: jwt.Secret;
|
|
15
|
+
perMessageDeflate?: boolean | ws.PerMessageDeflateOptions;
|
|
16
|
+
authPrivateKey?: jwt.Secret;
|
|
17
|
+
authPublicKey?: jwt.Secret;
|
|
18
|
+
authDefaultExpiry?: number;
|
|
19
|
+
authAlgorithm?: jwt.Algorithm;
|
|
20
|
+
protocolVersion?: ProtocolVersions;
|
|
21
|
+
handshakeTimeout?: number;
|
|
22
|
+
ackTimeout?: number;
|
|
23
|
+
origins?: string;
|
|
24
|
+
socketChannelLimit?: number;
|
|
25
|
+
pingInterval?: number;
|
|
26
|
+
pingTimeout?: number;
|
|
27
|
+
pingTimeoutDisabled?: boolean;
|
|
28
|
+
middlewareEmitFailures?: boolean;
|
|
29
|
+
path?: string;
|
|
30
|
+
allowClientPublish?: boolean;
|
|
31
|
+
batchOnHandshake?: boolean;
|
|
32
|
+
batchOnHandshakeDuration?: number;
|
|
33
|
+
batchInterval?: number;
|
|
34
|
+
socketStreamCleanupMode?: 'kill' | 'close';
|
|
35
|
+
authVerifyAlgorithms?: jwt.Algorithm[];
|
|
36
|
+
authEngine?: AuthEngine;
|
|
37
|
+
codecEngine?: CodecEngine;
|
|
38
|
+
cloneData?: boolean;
|
|
39
|
+
strictHandshake?: boolean;
|
|
40
|
+
[additionalOptions: string]: any;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/server.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { AuthEngine } from "@socket-mesh/server-auth";
|
|
4
|
+
import { CodecEngine } from "@socket-mesh/formatter";
|
|
5
|
+
import { Broker, Packet } from "@socket-mesh/simple-broker";
|
|
6
|
+
import { AsyncStreamEmitter } from "@socket-mesh/async-stream-emitter";
|
|
7
|
+
import { MiddlewareType } from "./middleware-type";
|
|
8
|
+
import { MiddlewareStream } from "./middleware-stream";
|
|
9
|
+
import { ActionAuthenticate, ActionHandshakeSC, ActionHandshakeWS, ActionInvoke, ActionMessage, ActionPublishIn, ActionPublishOut, ActionResult, ActionSubscribe, ActionTransmit, AuthInfo, MiddlewareAction } from "./action";
|
|
10
|
+
import { ProtocolVersions, ServerOptions } from "./server-options";
|
|
11
|
+
import ws from "ws";
|
|
12
|
+
import jwt from "jsonwebtoken";
|
|
13
|
+
import { Server as HttpServer } from 'http';
|
|
14
|
+
import { ServerSocket } from "./serversocket";
|
|
15
|
+
import { ChannelOptions, Client } from "@socket-mesh/channel";
|
|
16
|
+
import { AuthStateChanged, Authenticated, BadAuthToken, Closed, ConnectAborted, Connected, Deauthenticated, Disconnected, ErrorOccurred, ServerSocketEvents, Subscribed, Unsubscribed, WarningOccurred } from "./events";
|
|
17
|
+
export type ServerEvent<T, U extends ServerSocketEvents | {}> = {
|
|
18
|
+
socket: ServerSocket<T>;
|
|
19
|
+
} & U;
|
|
20
|
+
type ServerEvents<T> = ServerEvent<T, Connected> | ServerEvent<T, ConnectAborted> | ServerEvent<T, Disconnected> | ServerEvent<T, Closed> | {} | ServerEvent<T, BadAuthToken> | ServerEvent<T, Authenticated> | ServerEvent<T, AuthStateChanged> | ServerEvent<T, Deauthenticated> | ServerEvent<T, {}> | ErrorOccurred | WarningOccurred | ServerEvent<T, Subscribed> | ServerEvent<T, Unsubscribed>;
|
|
21
|
+
export declare class Server<T> extends AsyncStreamEmitter<ServerEvents<T>> {
|
|
22
|
+
allowClientPublish: boolean;
|
|
23
|
+
auth: AuthEngine;
|
|
24
|
+
clientsCount: number;
|
|
25
|
+
codec: CodecEngine;
|
|
26
|
+
defaultVerificationOptions: jwt.VerifyOptions;
|
|
27
|
+
defaultSignatureOptions: jwt.SignOptions;
|
|
28
|
+
isReady: boolean;
|
|
29
|
+
httpServer?: HttpServer;
|
|
30
|
+
middlewareEmitFailures: boolean;
|
|
31
|
+
pendingClientsCount: number;
|
|
32
|
+
ackTimeout: number;
|
|
33
|
+
handshakeTimeout: number;
|
|
34
|
+
options: ServerOptions;
|
|
35
|
+
path: string;
|
|
36
|
+
perMessageDeflate?: boolean | ws.PerMessageDeflateOptions;
|
|
37
|
+
pingInterval: number;
|
|
38
|
+
pingTimeout: number;
|
|
39
|
+
pingTimeoutDisabled: boolean;
|
|
40
|
+
protocolVersion: ProtocolVersions;
|
|
41
|
+
origins: string;
|
|
42
|
+
signatureKey: jwt.Secret;
|
|
43
|
+
strictHandshake: boolean;
|
|
44
|
+
socketChannelLimit?: number;
|
|
45
|
+
verificationKey: jwt.Secret;
|
|
46
|
+
readonly clients: {
|
|
47
|
+
[id: string]: ServerSocket<T>;
|
|
48
|
+
};
|
|
49
|
+
readonly pendingClients: {
|
|
50
|
+
[id: string]: ServerSocket<T>;
|
|
51
|
+
};
|
|
52
|
+
readonly brokerEngine: Broker<T>;
|
|
53
|
+
readonly exchange: Client<Packet<T>>;
|
|
54
|
+
readonly wsServer: ws.Server;
|
|
55
|
+
private readonly _allowAllOrigins;
|
|
56
|
+
private readonly _path;
|
|
57
|
+
private readonly _middleware;
|
|
58
|
+
constructor(options: ServerOptions);
|
|
59
|
+
setAuthEngine(authEngine: AuthEngine): void;
|
|
60
|
+
setCodecEngine(codecEngine: CodecEngine): void;
|
|
61
|
+
emitError(error: Error): void;
|
|
62
|
+
emitWarning(warning: Error): void;
|
|
63
|
+
private _handleServerError;
|
|
64
|
+
private _handleSocketConnection;
|
|
65
|
+
emit(eventName: 'connection', data: ServerEvent<T, Connected>): void;
|
|
66
|
+
emit(eventName: 'connectionAbort', data: ServerEvent<T, ConnectAborted>): void;
|
|
67
|
+
emit(eventName: 'disconnection', data: ServerEvent<T, Disconnected>): void;
|
|
68
|
+
emit(eventName: 'closure', data: ServerEvent<T, Closed>): void;
|
|
69
|
+
emit(eventName: 'ready', data: {}): void;
|
|
70
|
+
emit(eventName: 'authentication', data: ServerEvent<T, Authenticated>): void;
|
|
71
|
+
emit(eventName: 'badSocketAuthToken', data: ServerEvent<T, BadAuthToken>): void;
|
|
72
|
+
emit(eventName: 'error', data: ErrorOccurred): void;
|
|
73
|
+
emit(eventName: 'handshake', data: ServerEvent<T, {}>): void;
|
|
74
|
+
emit(eventName: 'warning', data: WarningOccurred): void;
|
|
75
|
+
emit(eventName: 'subscription', data: ServerEvent<T, Subscribed>): void;
|
|
76
|
+
emit(eventName: 'unsubscription', data: ServerEvent<T, Unsubscribed>): void;
|
|
77
|
+
emit(eventName: 'authenticationStateChange', data: ServerEvent<T, AuthStateChanged>): void;
|
|
78
|
+
emit(eventName: 'deauthentication', data: ServerEvent<T, Deauthenticated>): void;
|
|
79
|
+
close(keepSocketsOpen: boolean): Promise<void>;
|
|
80
|
+
getPath(): string;
|
|
81
|
+
generateId(): string;
|
|
82
|
+
setMiddleware(type: MiddlewareType.MIDDLEWARE_HANDSHAKE, middleware: (stream: MiddlewareStream<void>) => void): void;
|
|
83
|
+
setMiddleware(type: MiddlewareType.MIDDLEWARE_INBOUND_RAW, middleware: (stream: MiddlewareStream<string | ArrayBuffer | Buffer[]>) => void): void;
|
|
84
|
+
setMiddleware(type: MiddlewareType.MIDDLEWARE_INBOUND, middleware: (stream: MiddlewareStream<ChannelOptions | T | void>) => void): void;
|
|
85
|
+
setMiddleware(type: MiddlewareType.MIDDLEWARE_OUTBOUND, middleware: (stream: MiddlewareStream<T>) => void): void;
|
|
86
|
+
removeMiddleware(type: MiddlewareType): void;
|
|
87
|
+
hasMiddleware(type: MiddlewareType): boolean;
|
|
88
|
+
_processMiddlewareAction(middlewareStream: MiddlewareStream<void | T | ChannelOptions>, action: ActionSubscribe<T> | ActionTransmit<T> | ActionInvoke<T> | ActionPublishIn<T>, socket?: ServerSocket<T>): Promise<ActionResult<T>>;
|
|
89
|
+
_processMiddlewareAction(middlewareStream: MiddlewareStream<void | T | ChannelOptions>, action: ActionAuthenticate<T>, socket?: ServerSocket<T>): Promise<ActionResult<void>>;
|
|
90
|
+
_processMiddlewareAction(middlewareStream: MiddlewareStream<T>, action: ActionPublishOut<T>, socket?: ServerSocket<T>): Promise<ActionResult<T>>;
|
|
91
|
+
_processMiddlewareAction(middlewareStream: MiddlewareStream<ChannelOptions>, action: ActionSubscribe<T>, socket?: ServerSocket<T>): Promise<ActionResult<ChannelOptions>>;
|
|
92
|
+
_processMiddlewareAction(middlewareStream: MiddlewareStream<void>, action: ActionHandshakeWS, socket?: ServerSocket<T>): Promise<ActionResult<void>>;
|
|
93
|
+
_processMiddlewareAction(middlewareStream: MiddlewareStream<void>, action: ActionHandshakeSC<T>, socket?: ServerSocket<T>): Promise<ActionResult<void>>;
|
|
94
|
+
_processMiddlewareAction(middlewareStream: MiddlewareStream<string | ArrayBuffer | Buffer[]>, action: ActionMessage<T>, socket?: ServerSocket<T>): Promise<ActionResult<string | ArrayBuffer | Buffer[]>>;
|
|
95
|
+
_processMiddlewareAction(middlewareStream: MiddlewareStream<T | ChannelOptions | AuthInfo | string | ArrayBuffer | Buffer[] | void>, action: MiddlewareAction<T>, socket?: ServerSocket<T>): Promise<ActionResult<T | ChannelOptions | AuthInfo | string | ArrayBuffer | Buffer[] | void>>;
|
|
96
|
+
private _verifyHandshake;
|
|
97
|
+
}
|
|
98
|
+
export {};
|
package/server.js
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
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 { AuthEngine } from "@socket-mesh/server-auth";
|
|
18
|
+
import formatter from "@socket-mesh/formatter";
|
|
19
|
+
import { SimpleBroker } from "@socket-mesh/simple-broker";
|
|
20
|
+
import { AsyncStreamEmitter } from "@socket-mesh/async-stream-emitter";
|
|
21
|
+
import { InvalidArgumentsError, InvalidOptionsError, InvalidActionError, ServerProtocolError, SilentMiddlewareBlockedError } from "@socket-mesh/errors";
|
|
22
|
+
import { MiddlewareType } from "./middleware-type";
|
|
23
|
+
import { MiddlewareStream } from "./middleware-stream";
|
|
24
|
+
import { ActionHandshakeWS } from "./action";
|
|
25
|
+
import base64id from 'base64id';
|
|
26
|
+
import { ServerSocket } from "./serversocket";
|
|
27
|
+
import url from "url";
|
|
28
|
+
import crypto from "crypto";
|
|
29
|
+
export class Server extends AsyncStreamEmitter {
|
|
30
|
+
constructor(options) {
|
|
31
|
+
super();
|
|
32
|
+
this._verifyHandshake = function (info, callback) {
|
|
33
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
34
|
+
let req = info.req;
|
|
35
|
+
let origin = info.origin;
|
|
36
|
+
if (origin === 'null' || origin == null) {
|
|
37
|
+
origin = '*';
|
|
38
|
+
}
|
|
39
|
+
let ok = false;
|
|
40
|
+
if (this._allowAllOrigins) {
|
|
41
|
+
ok = true;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
try {
|
|
45
|
+
let parts = url.parse(origin);
|
|
46
|
+
parts.port = parts.port || (parts.protocol === 'https:' ? '443' : '80');
|
|
47
|
+
ok = !!(~this.origins.indexOf(parts.hostname + ':' + parts.port) ||
|
|
48
|
+
~this.origins.indexOf(parts.hostname + ':*') ||
|
|
49
|
+
~this.origins.indexOf('*:' + parts.port));
|
|
50
|
+
}
|
|
51
|
+
catch (e) { }
|
|
52
|
+
}
|
|
53
|
+
const middlewareHandshakeStream = new MiddlewareStream(MiddlewareType.MIDDLEWARE_HANDSHAKE);
|
|
54
|
+
req['handshakeStream'] = middlewareHandshakeStream;
|
|
55
|
+
const handshakeMiddleware = this._middleware[MiddlewareType.MIDDLEWARE_HANDSHAKE];
|
|
56
|
+
if (handshakeMiddleware) {
|
|
57
|
+
handshakeMiddleware(middlewareHandshakeStream);
|
|
58
|
+
}
|
|
59
|
+
const action = new ActionHandshakeWS(req);
|
|
60
|
+
try {
|
|
61
|
+
yield this._processMiddlewareAction(middlewareHandshakeStream, action);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
middlewareHandshakeStream.close();
|
|
65
|
+
callback(false, 401, typeof error === 'string' ? error : error.message);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (ok) {
|
|
69
|
+
callback(true);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const error = new ServerProtocolError(`Failed to authorize socket handshake - Invalid origin: ${origin}`);
|
|
73
|
+
this.emitWarning(error);
|
|
74
|
+
middlewareHandshakeStream.close();
|
|
75
|
+
callback(false, 403, error.message);
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
let opts = {
|
|
79
|
+
brokerEngine: new SimpleBroker(),
|
|
80
|
+
wsEngine: 'ws',
|
|
81
|
+
wsEngineServerOptions: {},
|
|
82
|
+
maxPayload: null,
|
|
83
|
+
allowClientPublish: true,
|
|
84
|
+
ackTimeout: 10000,
|
|
85
|
+
handshakeTimeout: 10000,
|
|
86
|
+
strictHandshake: true,
|
|
87
|
+
pingTimeout: 20000,
|
|
88
|
+
pingTimeoutDisabled: false,
|
|
89
|
+
pingInterval: 8000,
|
|
90
|
+
origins: '*:*',
|
|
91
|
+
path: '/socketcluster/',
|
|
92
|
+
protocolVersion: 2,
|
|
93
|
+
authDefaultExpiry: 86400,
|
|
94
|
+
batchOnHandshake: false,
|
|
95
|
+
batchOnHandshakeDuration: 400,
|
|
96
|
+
batchInterval: 50,
|
|
97
|
+
middlewareEmitFailures: true,
|
|
98
|
+
socketStreamCleanupMode: 'kill',
|
|
99
|
+
cloneData: false
|
|
100
|
+
};
|
|
101
|
+
this.options = Object.assign(opts, options);
|
|
102
|
+
this._middleware = {};
|
|
103
|
+
this.origins = opts.origins;
|
|
104
|
+
this._allowAllOrigins = this.origins.indexOf('*:*') !== -1;
|
|
105
|
+
this.ackTimeout = opts.ackTimeout;
|
|
106
|
+
this.handshakeTimeout = opts.handshakeTimeout;
|
|
107
|
+
this.pingInterval = opts.pingInterval;
|
|
108
|
+
this.pingTimeout = opts.pingTimeout;
|
|
109
|
+
this.pingTimeoutDisabled = opts.pingTimeoutDisabled;
|
|
110
|
+
this.allowClientPublish = opts.allowClientPublish;
|
|
111
|
+
this.perMessageDeflate = opts.perMessageDeflate;
|
|
112
|
+
this.httpServer = opts.httpServer;
|
|
113
|
+
this.socketChannelLimit = opts.socketChannelLimit;
|
|
114
|
+
this.protocolVersion = opts.protocolVersion;
|
|
115
|
+
this.strictHandshake = opts.strictHandshake;
|
|
116
|
+
this.brokerEngine = opts.brokerEngine;
|
|
117
|
+
this.middlewareEmitFailures = opts.middlewareEmitFailures;
|
|
118
|
+
this._path = opts.path;
|
|
119
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
var _a, e_1, _b, _c;
|
|
121
|
+
try {
|
|
122
|
+
for (var _d = true, _e = __asyncValues(this.brokerEngine.listen('error')), _f; _f = yield _e.next(), _a = _f.done, !_a;) {
|
|
123
|
+
_c = _f.value;
|
|
124
|
+
_d = false;
|
|
125
|
+
try {
|
|
126
|
+
let { error } = _c;
|
|
127
|
+
this.emitWarning(error);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
_d = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
135
|
+
finally {
|
|
136
|
+
try {
|
|
137
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
138
|
+
}
|
|
139
|
+
finally { if (e_1) throw e_1.error; }
|
|
140
|
+
}
|
|
141
|
+
}))();
|
|
142
|
+
if (this.brokerEngine.isReady) {
|
|
143
|
+
this.isReady = true;
|
|
144
|
+
this.emit('ready', {});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.isReady = false;
|
|
148
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
yield this.brokerEngine.listen('ready').once();
|
|
150
|
+
this.isReady = true;
|
|
151
|
+
this.emit('ready', {});
|
|
152
|
+
}))();
|
|
153
|
+
}
|
|
154
|
+
let wsEngine = typeof opts.wsEngine === 'string' ? require(opts.wsEngine) : opts.wsEngine;
|
|
155
|
+
if (!wsEngine || !wsEngine.Server) {
|
|
156
|
+
throw new InvalidOptionsError('The wsEngine option must be a path or module name which points ' +
|
|
157
|
+
'to a valid WebSocket engine module with a compatible interface');
|
|
158
|
+
}
|
|
159
|
+
let WSServer = wsEngine.Server;
|
|
160
|
+
if (opts.authPrivateKey != null || opts.authPublicKey != null) {
|
|
161
|
+
if (opts.authPrivateKey == null) {
|
|
162
|
+
throw new InvalidOptionsError('The authPrivateKey option must be specified if authPublicKey is specified');
|
|
163
|
+
}
|
|
164
|
+
else if (opts.authPublicKey == null) {
|
|
165
|
+
throw new InvalidOptionsError('The authPublicKey option must be specified if authPrivateKey is specified');
|
|
166
|
+
}
|
|
167
|
+
this.signatureKey = opts.authPrivateKey;
|
|
168
|
+
this.verificationKey = opts.authPublicKey;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
if (opts.authKey == null) {
|
|
172
|
+
opts.authKey = crypto.randomBytes(32).toString('hex');
|
|
173
|
+
}
|
|
174
|
+
this.signatureKey = opts.authKey;
|
|
175
|
+
this.verificationKey = opts.authKey;
|
|
176
|
+
}
|
|
177
|
+
this.defaultVerificationOptions = {};
|
|
178
|
+
if (opts.authVerifyAlgorithms != null) {
|
|
179
|
+
this.defaultVerificationOptions.algorithms = opts.authVerifyAlgorithms;
|
|
180
|
+
}
|
|
181
|
+
else if (opts.authAlgorithm != null) {
|
|
182
|
+
this.defaultVerificationOptions.algorithms = [opts.authAlgorithm];
|
|
183
|
+
}
|
|
184
|
+
this.defaultSignatureOptions = {
|
|
185
|
+
expiresIn: opts.authDefaultExpiry
|
|
186
|
+
};
|
|
187
|
+
if (opts.authAlgorithm != null) {
|
|
188
|
+
this.defaultSignatureOptions.algorithm = opts.authAlgorithm;
|
|
189
|
+
}
|
|
190
|
+
if (opts.authEngine) {
|
|
191
|
+
this.auth = opts.authEngine;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
// Default authentication engine
|
|
195
|
+
this.auth = new AuthEngine();
|
|
196
|
+
}
|
|
197
|
+
if (opts.codecEngine) {
|
|
198
|
+
this.codec = opts.codecEngine;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// Default codec engine
|
|
202
|
+
this.codec = formatter;
|
|
203
|
+
}
|
|
204
|
+
this.brokerEngine.setCodecEngine(this.codec);
|
|
205
|
+
this.exchange = this.brokerEngine.exchange();
|
|
206
|
+
this.clients = {};
|
|
207
|
+
this.clientsCount = 0;
|
|
208
|
+
this.pendingClients = {};
|
|
209
|
+
this.pendingClientsCount = 0;
|
|
210
|
+
let wsServerOptions = opts.wsEngineServerOptions || {};
|
|
211
|
+
wsServerOptions.server = this.httpServer;
|
|
212
|
+
wsServerOptions.verifyClient = this._verifyHandshake.bind(this);
|
|
213
|
+
if (wsServerOptions.path == null && this._path != null) {
|
|
214
|
+
wsServerOptions.path = this._path;
|
|
215
|
+
}
|
|
216
|
+
if (wsServerOptions.perMessageDeflate == null && this.perMessageDeflate != null) {
|
|
217
|
+
wsServerOptions.perMessageDeflate = this.perMessageDeflate;
|
|
218
|
+
}
|
|
219
|
+
if (wsServerOptions.handleProtocols == null && opts.handleProtocols != null) {
|
|
220
|
+
wsServerOptions.handleProtocols = opts.handleProtocols;
|
|
221
|
+
}
|
|
222
|
+
if (wsServerOptions.maxPayload == null && opts.maxPayload != null) {
|
|
223
|
+
wsServerOptions.maxPayload = opts.maxPayload;
|
|
224
|
+
}
|
|
225
|
+
if (wsServerOptions.clientTracking == null) {
|
|
226
|
+
wsServerOptions.clientTracking = false;
|
|
227
|
+
}
|
|
228
|
+
this.wsServer = new WSServer(wsServerOptions);
|
|
229
|
+
this.wsServer.on('error', this._handleServerError.bind(this));
|
|
230
|
+
this.wsServer.on('connection', this._handleSocketConnection.bind(this));
|
|
231
|
+
}
|
|
232
|
+
setAuthEngine(authEngine) {
|
|
233
|
+
this.auth = authEngine;
|
|
234
|
+
}
|
|
235
|
+
setCodecEngine(codecEngine) {
|
|
236
|
+
this.codec = codecEngine;
|
|
237
|
+
this.brokerEngine.setCodecEngine(codecEngine);
|
|
238
|
+
}
|
|
239
|
+
emitError(error) {
|
|
240
|
+
this.emit('error', { error });
|
|
241
|
+
}
|
|
242
|
+
emitWarning(warning) {
|
|
243
|
+
this.emit('warning', { warning });
|
|
244
|
+
}
|
|
245
|
+
_handleServerError(error) {
|
|
246
|
+
if (typeof error === 'string') {
|
|
247
|
+
error = new ServerProtocolError(error);
|
|
248
|
+
}
|
|
249
|
+
this.emitError(error);
|
|
250
|
+
}
|
|
251
|
+
_handleSocketConnection(wsSocket, upgradeReq) {
|
|
252
|
+
if (!wsSocket.upgradeReq) {
|
|
253
|
+
// Normalize ws modules to match.
|
|
254
|
+
wsSocket.upgradeReq = upgradeReq;
|
|
255
|
+
}
|
|
256
|
+
let socketId = this.generateId();
|
|
257
|
+
let agSocket = new ServerSocket(socketId, this, wsSocket, this.protocolVersion);
|
|
258
|
+
//agSocket.exchange = this.exchange;
|
|
259
|
+
const inboundRawMiddleware = this._middleware[MiddlewareType.MIDDLEWARE_INBOUND_RAW];
|
|
260
|
+
if (inboundRawMiddleware) {
|
|
261
|
+
inboundRawMiddleware(agSocket.middlewareInboundRawStream);
|
|
262
|
+
}
|
|
263
|
+
const inboundMiddleware = this._middleware[MiddlewareType.MIDDLEWARE_INBOUND];
|
|
264
|
+
if (inboundMiddleware) {
|
|
265
|
+
inboundMiddleware(agSocket.middlewareInboundStream);
|
|
266
|
+
}
|
|
267
|
+
const outboundMiddleware = this._middleware[MiddlewareType.MIDDLEWARE_OUTBOUND];
|
|
268
|
+
if (outboundMiddleware) {
|
|
269
|
+
outboundMiddleware(agSocket.middlewareOutboundStream);
|
|
270
|
+
}
|
|
271
|
+
// Emit event to signal that a socket handshake has been initiated.
|
|
272
|
+
this.emit('handshake', { socket: agSocket });
|
|
273
|
+
}
|
|
274
|
+
emit(eventName, data) {
|
|
275
|
+
super.emit(eventName, data);
|
|
276
|
+
}
|
|
277
|
+
close(keepSocketsOpen) {
|
|
278
|
+
this.isReady = false;
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
this.wsServer.close((err) => {
|
|
281
|
+
if (err) {
|
|
282
|
+
reject(err);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
resolve();
|
|
286
|
+
});
|
|
287
|
+
if (!keepSocketsOpen) {
|
|
288
|
+
for (let socket of Object.values(this.clients)) {
|
|
289
|
+
socket.terminate();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
getPath() {
|
|
295
|
+
return this._path;
|
|
296
|
+
}
|
|
297
|
+
generateId() {
|
|
298
|
+
return base64id.generateId();
|
|
299
|
+
}
|
|
300
|
+
setMiddleware(type, middleware) {
|
|
301
|
+
if (type !== MiddlewareType.MIDDLEWARE_HANDSHAKE &&
|
|
302
|
+
type !== MiddlewareType.MIDDLEWARE_INBOUND_RAW &&
|
|
303
|
+
type !== MiddlewareType.MIDDLEWARE_INBOUND &&
|
|
304
|
+
type !== MiddlewareType.MIDDLEWARE_OUTBOUND) {
|
|
305
|
+
throw new InvalidArgumentsError(`Middleware type "${type}" is not supported`);
|
|
306
|
+
}
|
|
307
|
+
if (this._middleware[type]) {
|
|
308
|
+
throw new InvalidActionError(`Middleware type "${type}" has already been set`);
|
|
309
|
+
}
|
|
310
|
+
this._middleware[type] = middleware;
|
|
311
|
+
}
|
|
312
|
+
removeMiddleware(type) {
|
|
313
|
+
delete this._middleware[type];
|
|
314
|
+
}
|
|
315
|
+
hasMiddleware(type) {
|
|
316
|
+
return !!this._middleware[type];
|
|
317
|
+
}
|
|
318
|
+
_processMiddlewareAction(middlewareStream, action, socket) {
|
|
319
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
320
|
+
if (!this.hasMiddleware(middlewareStream.type)) {
|
|
321
|
+
return { data: 'data' in action ? action.data : undefined };
|
|
322
|
+
}
|
|
323
|
+
middlewareStream.write(action);
|
|
324
|
+
let newData;
|
|
325
|
+
let options;
|
|
326
|
+
try {
|
|
327
|
+
const result = yield action.promise;
|
|
328
|
+
if (result) {
|
|
329
|
+
newData = result.data;
|
|
330
|
+
options = result.options;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
let clientError;
|
|
335
|
+
if (!error) {
|
|
336
|
+
error = new SilentMiddlewareBlockedError(`The ${action.type} Action was blocked by ${middlewareStream.type} middleware`, middlewareStream.type);
|
|
337
|
+
clientError = error;
|
|
338
|
+
}
|
|
339
|
+
else if (error.silent) {
|
|
340
|
+
clientError = new SilentMiddlewareBlockedError(`The ${action.type} Action was blocked by ${middlewareStream.type} middleware`, middlewareStream.type);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
clientError = error;
|
|
344
|
+
}
|
|
345
|
+
if (this.middlewareEmitFailures) {
|
|
346
|
+
if (socket) {
|
|
347
|
+
socket.emitError(error);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
this.emitWarning(error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
throw clientError;
|
|
354
|
+
}
|
|
355
|
+
if (newData === undefined && 'data' in action) {
|
|
356
|
+
newData = action.data;
|
|
357
|
+
}
|
|
358
|
+
return { data: newData, options };
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|