@gleamkit/engine.io 6.6.3

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.
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uServer = void 0;
4
+ const debug_1 = require("debug");
5
+ const server_1 = require("./server");
6
+ const transports_uws_1 = require("./transports-uws");
7
+ const debug = (0, debug_1.default)("engine:uws");
8
+ /**
9
+ * An Engine.IO server based on the `uWebSockets.js` package.
10
+ */
11
+ // TODO export it into its own package
12
+ class uServer extends server_1.BaseServer {
13
+ init() { }
14
+ cleanup() { }
15
+ /**
16
+ * Prepares a request by processing the query string.
17
+ *
18
+ * @private
19
+ */
20
+ prepare(req, res) {
21
+ req.method = req.getMethod().toUpperCase();
22
+ req.url = req.getUrl();
23
+ const params = new URLSearchParams(req.getQuery());
24
+ req._query = Object.fromEntries(params.entries());
25
+ req.headers = {};
26
+ req.forEach((key, value) => {
27
+ req.headers[key] = value;
28
+ });
29
+ req.connection = {
30
+ remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString(),
31
+ };
32
+ res.onAborted(() => {
33
+ debug("response has been aborted");
34
+ });
35
+ }
36
+ createTransport(transportName, req) {
37
+ return new transports_uws_1.default[transportName](req);
38
+ }
39
+ /**
40
+ * Attach the engine to a µWebSockets.js server
41
+ * @param app
42
+ * @param options
43
+ */
44
+ attach(app /* : TemplatedApp */, options = {}) {
45
+ const path = this._computePath(options);
46
+ app
47
+ .any(path, this.handleRequest.bind(this))
48
+ //
49
+ .ws(path, {
50
+ compression: options.compression,
51
+ idleTimeout: options.idleTimeout,
52
+ maxBackpressure: options.maxBackpressure,
53
+ maxPayloadLength: this.opts.maxHttpBufferSize,
54
+ upgrade: this.handleUpgrade.bind(this),
55
+ open: (ws) => {
56
+ const transport = ws.getUserData().transport;
57
+ transport.socket = ws;
58
+ transport.writable = true;
59
+ transport.emit("ready");
60
+ },
61
+ message: (ws, message, isBinary) => {
62
+ ws.getUserData().transport.onData(isBinary ? message : Buffer.from(message).toString());
63
+ },
64
+ close: (ws, code, message) => {
65
+ ws.getUserData().transport.onClose(code, message);
66
+ },
67
+ });
68
+ }
69
+ _applyMiddlewares(req, res, callback) {
70
+ if (this.middlewares.length === 0) {
71
+ return callback();
72
+ }
73
+ // needed to buffer headers until the status is computed
74
+ req.res = new ResponseWrapper(res);
75
+ super._applyMiddlewares(req, req.res, (err) => {
76
+ // some middlewares (like express-session) wait for the writeHead() call to flush their headers
77
+ // see https://github.com/expressjs/session/blob/1010fadc2f071ddf2add94235d72224cf65159c6/index.js#L220-L244
78
+ req.res.writeHead();
79
+ callback(err);
80
+ });
81
+ }
82
+ handleRequest(res, req) {
83
+ debug('handling "%s" http request "%s"', req.getMethod(), req.getUrl());
84
+ this.prepare(req, res);
85
+ req.res = res;
86
+ const callback = (errorCode, errorContext) => {
87
+ if (errorCode !== undefined) {
88
+ this.emit("connection_error", {
89
+ req,
90
+ code: errorCode,
91
+ message: server_1.Server.errorMessages[errorCode],
92
+ context: errorContext,
93
+ });
94
+ this.abortRequest(req.res, errorCode, errorContext);
95
+ return;
96
+ }
97
+ if (req._query.sid) {
98
+ debug("setting new request for existing client");
99
+ // @ts-ignore
100
+ this.clients[req._query.sid].transport.onRequest(req);
101
+ }
102
+ else {
103
+ const closeConnection = (errorCode, errorContext) => this.abortRequest(res, errorCode, errorContext);
104
+ this.handshake(req._query.transport, req, closeConnection);
105
+ }
106
+ };
107
+ this._applyMiddlewares(req, res, (err) => {
108
+ if (err) {
109
+ callback(server_1.Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
110
+ }
111
+ else {
112
+ this.verify(req, false, callback);
113
+ }
114
+ });
115
+ }
116
+ handleUpgrade(res, req, context) {
117
+ debug("on upgrade");
118
+ this.prepare(req, res);
119
+ req.res = res;
120
+ const callback = async (errorCode, errorContext) => {
121
+ if (errorCode !== undefined) {
122
+ this.emit("connection_error", {
123
+ req,
124
+ code: errorCode,
125
+ message: server_1.Server.errorMessages[errorCode],
126
+ context: errorContext,
127
+ });
128
+ this.abortRequest(res, errorCode, errorContext);
129
+ return;
130
+ }
131
+ const id = req._query.sid;
132
+ let transport;
133
+ if (id) {
134
+ const client = this.clients[id];
135
+ if (!client) {
136
+ debug("upgrade attempt for closed client");
137
+ return res.close();
138
+ }
139
+ else if (client.upgrading) {
140
+ debug("transport has already been trying to upgrade");
141
+ return res.close();
142
+ }
143
+ else if (client.upgraded) {
144
+ debug("transport had already been upgraded");
145
+ return res.close();
146
+ }
147
+ else {
148
+ debug("upgrading existing transport");
149
+ transport = this.createTransport(req._query.transport, req);
150
+ client._maybeUpgrade(transport);
151
+ }
152
+ }
153
+ else {
154
+ transport = await this.handshake(req._query.transport, req, (errorCode, errorContext) => this.abortRequest(res, errorCode, errorContext));
155
+ if (!transport) {
156
+ return;
157
+ }
158
+ }
159
+ // calling writeStatus() triggers the flushing of any header added in a middleware
160
+ req.res.writeStatus("101 Switching Protocols");
161
+ res.upgrade({
162
+ transport,
163
+ }, req.getHeader("sec-websocket-key"), req.getHeader("sec-websocket-protocol"), req.getHeader("sec-websocket-extensions"), context);
164
+ };
165
+ this._applyMiddlewares(req, res, (err) => {
166
+ if (err) {
167
+ callback(server_1.Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
168
+ }
169
+ else {
170
+ this.verify(req, true, callback);
171
+ }
172
+ });
173
+ }
174
+ abortRequest(res, errorCode, errorContext) {
175
+ const statusCode = errorCode === server_1.Server.errors.FORBIDDEN
176
+ ? "403 Forbidden"
177
+ : "400 Bad Request";
178
+ const message = errorContext && errorContext.message
179
+ ? errorContext.message
180
+ : server_1.Server.errorMessages[errorCode];
181
+ res.writeStatus(statusCode);
182
+ res.writeHeader("Content-Type", "application/json");
183
+ res.end(JSON.stringify({
184
+ code: errorCode,
185
+ message,
186
+ }));
187
+ }
188
+ }
189
+ exports.uServer = uServer;
190
+ class ResponseWrapper {
191
+ constructor(res) {
192
+ this.res = res;
193
+ this.statusWritten = false;
194
+ this.headers = [];
195
+ this.isAborted = false;
196
+ }
197
+ set statusCode(status) {
198
+ if (!status) {
199
+ return;
200
+ }
201
+ // FIXME: handle all status codes?
202
+ this.writeStatus(status === 200 ? "200 OK" : "204 No Content");
203
+ }
204
+ writeHead(status) {
205
+ this.statusCode = status;
206
+ }
207
+ setHeader(key, value) {
208
+ if (Array.isArray(value)) {
209
+ value.forEach((val) => {
210
+ this.writeHeader(key, val);
211
+ });
212
+ }
213
+ else {
214
+ this.writeHeader(key, value);
215
+ }
216
+ }
217
+ removeHeader() {
218
+ // FIXME: not implemented
219
+ }
220
+ // needed by vary: https://github.com/jshttp/vary/blob/5d725d059b3871025cf753e9dfa08924d0bcfa8f/index.js#L134
221
+ getHeader() { }
222
+ writeStatus(status) {
223
+ if (this.isAborted)
224
+ return;
225
+ this.res.writeStatus(status);
226
+ this.statusWritten = true;
227
+ this.writeBufferedHeaders();
228
+ return this;
229
+ }
230
+ writeHeader(key, value) {
231
+ if (this.isAborted)
232
+ return;
233
+ if (key === "Content-Length") {
234
+ // the content length is automatically added by uWebSockets.js
235
+ return;
236
+ }
237
+ if (this.statusWritten) {
238
+ this.res.writeHeader(key, value);
239
+ }
240
+ else {
241
+ this.headers.push([key, value]);
242
+ }
243
+ }
244
+ writeBufferedHeaders() {
245
+ this.headers.forEach(([key, value]) => {
246
+ this.res.writeHeader(key, value);
247
+ });
248
+ }
249
+ end(data) {
250
+ if (this.isAborted)
251
+ return;
252
+ this.res.cork(() => {
253
+ if (!this.statusWritten) {
254
+ // status will be inferred as "200 OK"
255
+ this.writeBufferedHeaders();
256
+ }
257
+ this.res.end(data);
258
+ });
259
+ }
260
+ onData(fn) {
261
+ if (this.isAborted)
262
+ return;
263
+ this.res.onData(fn);
264
+ }
265
+ onAborted(fn) {
266
+ if (this.isAborted)
267
+ return;
268
+ this.res.onAborted(() => {
269
+ // Any attempt to use the UWS response object after abort will throw!
270
+ this.isAborted = true;
271
+ fn();
272
+ });
273
+ }
274
+ cork(fn) {
275
+ if (this.isAborted)
276
+ return;
277
+ this.res.cork(fn);
278
+ }
279
+ }
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@gleamkit/engine.io",
3
+ "version": "6.6.3",
4
+ "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server",
5
+ "type": "commonjs",
6
+ "main": "./build/engine.io.js",
7
+ "types": "./build/engine.io.d.ts",
8
+ "exports": {
9
+ "types": "./build/engine.io.d.ts",
10
+ "import": "./wrapper.mjs",
11
+ "require": "./build/engine.io.js"
12
+ },
13
+ "author": "Guillermo Rauch <guillermo@learnboost.com>",
14
+ "contributors": [
15
+ {
16
+ "name": "Eugen Dueck",
17
+ "web": "https://github.com/EugenDueck"
18
+ },
19
+ {
20
+ "name": "Afshin Mehrabani",
21
+ "web": "https://github.com/afshinm"
22
+ },
23
+ {
24
+ "name": "Christoph Dorn",
25
+ "web": "https://github.com/cadorn"
26
+ },
27
+ {
28
+ "name": "Mark Mokryn",
29
+ "email": "mokesmokes@gmail.com"
30
+ }
31
+ ],
32
+ "license": "MIT",
33
+ "dependencies": {
34
+ "@types/cookie": "^0.4.1",
35
+ "@types/cors": "^2.8.12",
36
+ "@types/node": ">=10.0.0",
37
+ "accepts": "~1.3.4",
38
+ "base64id": "2.0.0",
39
+ "cookie": "~0.7.2",
40
+ "cors": "~2.8.5",
41
+ "debug": "~4.3.1",
42
+ "engine.io-parser": "~5.2.1",
43
+ "ws": "npm:@gleamkit/ws@8.21.1"
44
+ },
45
+ "scripts": {
46
+ "compile": "rimraf ./build && tsc",
47
+ "test": "npm run compile && npm run format:check && npm run test:default && npm run test:compat-v3",
48
+ "test:default": "mocha --bail --exit",
49
+ "test:compat-v3": "EIO_CLIENT=3 mocha --exit",
50
+ "test:eiows": "EIO_WS_ENGINE=eiows mocha --exit",
51
+ "test:uws": "EIO_WS_ENGINE=uws mocha --exit",
52
+ "format:check": "prettier --check \"wrapper.mjs\" \"lib/**/*.ts\" \"test/**/*.js\" \"test/webtransport.mjs\"",
53
+ "format:fix": "prettier --write \"wrapper.mjs\" \"lib/**/*.ts\" \"test/**/*.js\" \"test/webtransport.mjs\"",
54
+ "prepack": "echo 'skip compile — build already present'"
55
+ },
56
+ "homepage": "https://github.com/socketio/socket.io/tree/main/packages/engine.io#readme",
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "git+https://github.com/socketio/socket.io.git"
60
+ },
61
+ "bugs": {
62
+ "url": "https://github.com/socketio/socket.io/issues"
63
+ },
64
+ "files": [
65
+ "build/",
66
+ "wrapper.mjs"
67
+ ],
68
+ "engines": {
69
+ "node": ">=10.2.0"
70
+ }
71
+ }
package/wrapper.mjs ADDED
@@ -0,0 +1,10 @@
1
+ export {
2
+ Server,
3
+ Socket,
4
+ Transport,
5
+ transports,
6
+ listen,
7
+ attach,
8
+ parser,
9
+ protocol,
10
+ } from "./build/engine.io.js";