1 | ;
|
2 | /**
|
3 | * This file is part of the @egodigital/egoose distribution.
|
4 | * Copyright (c) e.GO Digital GmbH, Aachen, Germany (https://www.e-go-digital.com/)
|
5 | *
|
6 | * @egodigital/egoose is free software: you can redistribute it and/or modify
|
7 | * it under the terms of the GNU Lesser General Public License as
|
8 | * published by the Free Software Foundation, version 3.
|
9 | *
|
10 | * @egodigital/egoose is distributed in the hope that it will be useful, but
|
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13 | * Lesser General Public License for more details.
|
14 | *
|
15 | * You should have received a copy of the GNU Lesser General Public License
|
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17 | */
|
18 | Object.defineProperty(exports, "__esModule", { value: true });
|
19 | const index_1 = require("../index");
|
20 | const _ = require("lodash");
|
21 | const events = require("events");
|
22 | const http = require("http");
|
23 | const ws = require("ws");
|
24 | /**
|
25 | * A web socket host.
|
26 | */
|
27 | class WebSocketHost extends events.EventEmitter {
|
28 | /**
|
29 | * Initializes a new instance of that class.
|
30 | *
|
31 | * @param {WebSocketHostOptions} [options] Custom options for the host.
|
32 | */
|
33 | constructor(options) {
|
34 | super();
|
35 | this.options = options;
|
36 | if (!this.options) {
|
37 | this.options = {};
|
38 | }
|
39 | }
|
40 | /**
|
41 | * Gets if the server is currently running or not.
|
42 | */
|
43 | get isRunning() {
|
44 | return !!this._server;
|
45 | }
|
46 | /**
|
47 | * Starts the host.
|
48 | *
|
49 | * @return {Promise<boolean>} The promise that indicates if operation was succesfull or not.
|
50 | */
|
51 | start() {
|
52 | return new Promise(async (resolve, reject) => {
|
53 | try {
|
54 | if (this.isRunning) {
|
55 | resolve(false);
|
56 | return;
|
57 | }
|
58 | let serverFactory = this.options.serverFactory;
|
59 | if (!serverFactory) {
|
60 | serverFactory = () => {
|
61 | return http.createServer();
|
62 | };
|
63 | }
|
64 | let port = parseInt(index_1.toStringSafe(this.options.port)
|
65 | .trim());
|
66 | if (isNaN(port)) {
|
67 | port = 5979;
|
68 | }
|
69 | const NEW_SERVER = await Promise.resolve(serverFactory());
|
70 | if (!NEW_SERVER) {
|
71 | resolve(false);
|
72 | return;
|
73 | }
|
74 | NEW_SERVER.once('error', (err) => {
|
75 | reject(err);
|
76 | });
|
77 | const VERIFY_CLIENT = async (info, callback) => {
|
78 | let isValid = true;
|
79 | try {
|
80 | if (this.options.verifyClient) {
|
81 | isValid = index_1.toBooleanSafe(await Promise.resolve(this.options.verifyClient({
|
82 | isSecure: info.secure,
|
83 | request: info.req,
|
84 | })));
|
85 | }
|
86 | }
|
87 | catch (_a) {
|
88 | isValid = false;
|
89 | }
|
90 | if (isValid) {
|
91 | callback(true);
|
92 | }
|
93 | else {
|
94 | callback(false, 401);
|
95 | }
|
96 | };
|
97 | const WSS = new ws.Server({
|
98 | server: NEW_SERVER,
|
99 | verifyClient: VERIFY_CLIENT,
|
100 | });
|
101 | WSS.on('error', (err) => {
|
102 | /* ignore errors */
|
103 | });
|
104 | WSS.on('connection', (ws) => {
|
105 | try {
|
106 | const CONN = new WebSocketClient(this, ws);
|
107 | CONN.init();
|
108 | this.emit('connection', CONN);
|
109 | }
|
110 | catch (_a) {
|
111 | try {
|
112 | ws.close();
|
113 | }
|
114 | catch (_b) { }
|
115 | }
|
116 | });
|
117 | NEW_SERVER.listen(port, () => {
|
118 | this._server = NEW_SERVER;
|
119 | resolve(true);
|
120 | });
|
121 | }
|
122 | catch (e) {
|
123 | reject(e);
|
124 | }
|
125 | });
|
126 | }
|
127 | /**
|
128 | * Stops the host.
|
129 | *
|
130 | * @return {Promise<boolean>} The promise that indicates if operation was succesfull or not.
|
131 | */
|
132 | stop() {
|
133 | return new Promise((resolve, reject) => {
|
134 | try {
|
135 | const SERVER = this._server;
|
136 | if (!SERVER) {
|
137 | resolve(false);
|
138 | return;
|
139 | }
|
140 | SERVER.close((err) => {
|
141 | if (err) {
|
142 | reject(err);
|
143 | }
|
144 | else {
|
145 | this._server = null;
|
146 | resolve(true);
|
147 | }
|
148 | });
|
149 | }
|
150 | catch (e) {
|
151 | reject(e);
|
152 | }
|
153 | });
|
154 | }
|
155 | }
|
156 | exports.WebSocketHost = WebSocketHost;
|
157 | /**
|
158 | * A web socket client.
|
159 | */
|
160 | class WebSocketClient extends events.EventEmitter {
|
161 | /**
|
162 | * Initializes a new instance of that class.
|
163 | *
|
164 | * @param {WebSocketHost} host The underlying host.
|
165 | * @param {ws} socket The underlying socket.
|
166 | */
|
167 | constructor(host, socket) {
|
168 | super();
|
169 | this.host = host;
|
170 | this.socket = socket;
|
171 | }
|
172 | /**
|
173 | * Initializes the instance.
|
174 | */
|
175 | init() {
|
176 | this.socket.on('error', (err) => {
|
177 | /* ignore errors */
|
178 | });
|
179 | this.socket.on('message', (data) => {
|
180 | try {
|
181 | const MSG = JSON.parse(index_1.toStringSafe(data));
|
182 | if (MSG) {
|
183 | this.emit('message', MSG);
|
184 | const TYPE = index_1.normalizeString(MSG.type);
|
185 | this.emit('message.' + TYPE, MSG, TYPE);
|
186 | }
|
187 | }
|
188 | catch (_a) { }
|
189 | });
|
190 | this.socket.once('close', () => {
|
191 | this.emit('close');
|
192 | });
|
193 | }
|
194 | onType(checker, listener) {
|
195 | let predicate = checker;
|
196 | if (!_.isFunction(predicate)) {
|
197 | if (_.isRegExp(checker)) {
|
198 | predicate = (t) => checker.test(t);
|
199 | }
|
200 | else {
|
201 | predicate = (t) => t === index_1.normalizeString(checker);
|
202 | }
|
203 | }
|
204 | const MSG_LISTENER = (msg) => {
|
205 | const TYPE = index_1.normalizeString(msg.type);
|
206 | if (index_1.toBooleanSafe(predicate(TYPE))) {
|
207 | listener(msg.data, TYPE);
|
208 | }
|
209 | };
|
210 | this.on('message', MSG_LISTENER);
|
211 | return MSG_LISTENER;
|
212 | }
|
213 | /**
|
214 | * Sends a message to the remote client.
|
215 | *
|
216 | * @param {string} type The type.
|
217 | * @param {any} [data] The data to send.
|
218 | */
|
219 | send(type, data) {
|
220 | return new Promise((resolve, reject) => {
|
221 | try {
|
222 | const MSG = {
|
223 | data: data,
|
224 | type: index_1.normalizeString(type),
|
225 | };
|
226 | this.socket.send(JSON.stringify(MSG), (err) => {
|
227 | if (err) {
|
228 | reject(err);
|
229 | }
|
230 | else {
|
231 | resolve();
|
232 | }
|
233 | });
|
234 | }
|
235 | catch (e) {
|
236 | reject(e);
|
237 | }
|
238 | });
|
239 | }
|
240 | }
|
241 | exports.WebSocketClient = WebSocketClient;
|
242 | //# sourceMappingURL=websockets.js.map |
\ | No newline at end of file |