From 5a7fa132c442bc1e7eefa1cf38168ee951575ded Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 2 Mar 2021 08:49:58 +0100 Subject: [PATCH 01/58] feat: increase the default value of pingTimeout This value was updated from 60000 to 5000 in [1], included in `engine.io@3.2.0` (Feb 2018). The reasoning back then: Some users experienced long delays between disconnection on the server-side and on the client-side. The "disconnect" event would take a long time to fire in the browser, probably due to a timer being delayed. Hence the change. That being said, the current value (5s) now causes unexpected disconnections when a big payload is sent over a slow network, because it prevents the ping-pong packets from being exchanged between the client and the server. This can also happen when a synchronous task blocks the server for more than 5 seconds. The new value (20s) thus seems like a good balance between quick disconnection detection and tolerance to various delays. Note: pingInterval + pingTimeout is still below the threshold of React Native, which complains if a timer is set with a delay of more than 1 minute. [1]: https://github.com/socketio/engine.io/commit/65b1ad1b8a95fb0547ee3f286c1b7da299c7735a Related: - https://github.com/socketio/socket.io/issues/2770 - https://github.com/socketio/socket.io/issues/2769 - https://github.com/socketio/socket.io/issues/3054 - https://github.com/socketio/socket.io/issues/3376 --- README.md | 2 +- lib/server.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f68afe6a..3a24e6e0b 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,7 @@ to a single process. - `Object`: optional, options object - **Options** - `pingTimeout` (`Number`): how many ms without a pong packet to - consider the connection closed (`5000`) + consider the connection closed (`20000`) - `pingInterval` (`Number`): how many ms before sending a new ping packet (`25000`) - `upgradeTimeout` (`Number`): how many ms before an uncompleted transport upgrade is cancelled (`10000`) diff --git a/lib/server.js b/lib/server.js index a7007357c..644d6b9ef 100644 --- a/lib/server.js +++ b/lib/server.js @@ -23,7 +23,7 @@ class Server extends EventEmitter { this.opts = Object.assign( { wsEngine: process.env.EIO_WS_ENGINE || "ws", - pingTimeout: 5000, + pingTimeout: 20000, pingInterval: 25000, upgradeTimeout: 10000, maxHttpBufferSize: 1e6, From 868d89111de0ab5bd0e147ecaff7983afbf5d087 Mon Sep 17 00:00:00 2001 From: Simone Mazzoni Date: Sun, 7 Mar 2021 00:22:29 +0100 Subject: [PATCH 02/58] fix: set default protocol version to 3 (#616) socket.io-client-swift libs version <=15.2.0, which uses protocol version 3, do not explicitly add the EIO query parameter at transport initialization. This omission leads the server to treat the client as a client that supports the protocol version 4, previously set as default, which is not correct for those versions of the client lib. From socket.io-client-swift version v16.0.0 the EIO query parameter is explicitly passed to specify the protocol version supported, but since the allowEIO3 parameter aims to make the server compatible with previous versions which in most of the cases are already used in production and not easily upgradable, it makes more sense to default the EIO version to 3 if not explicitly set by the client since the newer client versions pass the EIO protocol version in query parameters. Related: https://github.com/socketio/socket.io/issues/3794 --- lib/server.js | 2 +- lib/transport.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/server.js b/lib/server.js index 644d6b9ef..9a267a6dd 100644 --- a/lib/server.js +++ b/lib/server.js @@ -229,7 +229,7 @@ class Server extends EventEmitter { * @api private */ async handshake(transportName, req) { - const protocol = req._query.EIO === "3" ? 3 : 4; // 4th revision by default + const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default if (protocol === 3 && !this.opts.allowEIO3) { debug("unsupported protocol version"); sendErrorMessage( diff --git a/lib/transport.js b/lib/transport.js index 7fb2603a5..0da3993cb 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -22,8 +22,8 @@ class Transport extends EventEmitter { super(); this.readyState = "open"; this.discarded = false; - this.protocol = req._query.EIO === "3" ? 3 : 4; // 4th revision by default - this.parser = this.protocol === 3 ? parser_v3 : parser_v4; + this.protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default + this.parser = this.protocol === 4 ? parser_v4 : parser_v3; } /** From edb734316f143bf0f1bbc344e966d18e2676b934 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 9 Mar 2021 08:13:28 +0100 Subject: [PATCH 03/58] feat: remove dynamic require() with wsEngine This change is necessary to get rid of: > Critical dependency: the request of a dependency is an expression when bundling the server with webpack. BREAKING CHANGE: the syntax of the "wsEngine" option is updated Before: ```js const eioServer = require("engine.io")(httpServer, { wsEngine: "eiows" }); ``` After: ```js const eioServer = require("engine.io")(httpServer, { wsEngine: require("eiows").Server }); ``` Related: https://github.com/socketio/engine.io/issues/609 --- README.md | 4 ++-- lib/server.js | 9 ++++----- test/common.js | 4 ++++ test/server.js | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3a24e6e0b..3b3659f05 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ server.on('connection', /* ... */); const engine = require('engine.io'); const httpServer = require('http').createServer().listen(3000); const server = engine.attach(httpServer, { - wsEngine: 'uws' // requires having uws as dependency + wsEngine: require('eiows').Server // requires having eiows as dependency }); server.on('connection', /* ... */); @@ -249,7 +249,7 @@ to a single process. contains the client sid to send as part of handshake response headers. This cookie might be used for sticky-session. Defaults to not sending any cookie (`false`). See [here](https://github.com/jshttp/cookie#options-1) for all supported options. - - `wsEngine` (`String`): what WebSocket server implementation to use. Specified module must conform to the `ws` interface (see [ws module api docs](https://github.com/websockets/ws/blob/master/doc/ws.md)). Default value is `ws`. An alternative c++ addon is also available by installing `uws` module. + - `wsEngine` (`Function`): what WebSocket server implementation to use. Specified module must conform to the `ws` interface (see [ws module api docs](https://github.com/websockets/ws/blob/master/doc/ws.md)). Default value is `ws`. An alternative c++ addon is also available by installing `eiows` module. - `cors` (`Object`): the options that will be forwarded to the cors module. See [there](https://github.com/expressjs/cors#configuration-options) for all available options. Defaults to no CORS allowed. - `initialPacket` (`Object`): an optional packet which will be concatenated to the handshake packet emitted by Engine.IO. - `allowEIO3` (`Boolean`): whether to support v3 Engine.IO clients (defaults to `false`) diff --git a/lib/server.js b/lib/server.js index 9a267a6dd..bf604ed48 100644 --- a/lib/server.js +++ b/lib/server.js @@ -7,6 +7,8 @@ const Socket = require("./socket"); const debug = require("debug")("engine"); const cookieMod = require("cookie"); +const DEFAULT_WS_ENGINE = require("ws").Server; + class Server extends EventEmitter { /** * Server constructor. @@ -22,7 +24,7 @@ class Server extends EventEmitter { this.opts = Object.assign( { - wsEngine: process.env.EIO_WS_ENGINE || "ws", + wsEngine: DEFAULT_WS_ENGINE, pingTimeout: 20000, pingInterval: 25000, upgradeTimeout: 10000, @@ -76,10 +78,7 @@ class Server extends EventEmitter { if (this.ws) this.ws.close(); - // add explicit require for bundlers like webpack - const wsModule = - this.opts.wsEngine === "ws" ? require("ws") : require(this.opts.wsEngine); - this.ws = new wsModule.Server({ + this.ws = new this.opts.wsEngine({ noServer: true, clientTracking: false, perMessageDeflate: this.opts.perMessageDeflate, diff --git a/test/common.js b/test/common.js index b8d0e91c3..95021ca79 100644 --- a/test/common.js +++ b/test/common.js @@ -16,6 +16,10 @@ exports.listen = (opts, fn) => { opts.allowEIO3 = true; + if (process.env.EIO_WS_ENGINE) { + opts.wsEngine = require(process.env.EIO_WS_ENGINE).Server; + } + const e = eio.listen(0, opts, () => { fn(e.httpServer.address().port); }); diff --git a/test/server.js b/test/server.js index 9d846123f..c411aae70 100644 --- a/test/server.js +++ b/test/server.js @@ -3121,7 +3121,7 @@ describe("server", () => { describe("wsEngine option", () => { it("should allow loading of other websocket server implementation like eiows", done => { const engine = listen( - { allowUpgrades: false, wsEngine: "eiows" }, + { allowUpgrades: false, wsEngine: require("eiows").Server }, port => { expect(engine.ws instanceof require("eiows").Server).to.be.ok(); const socket = new eioc.Socket("ws://localhost:%d".s(port)); From 887ba06536a1fe0cd34e8b36cd31235e4f90b31d Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 10 Mar 2021 10:20:02 +0100 Subject: [PATCH 04/58] chore(release): 5.0.0 Diff: https://github.com/socketio/engine.io/compare/4.1.1...5.0.0 --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ package-lock.json | 9 ++++----- package.json | 4 ++-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1c249e65..7177937db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +# [5.0.0](https://github.com/socketio/engine.io/compare/4.1.1...5.0.0) (2021-03-10) + + +### Bug Fixes + +* set default protocol version to 3 ([#616](https://github.com/socketio/engine.io/issues/616)) ([868d891](https://github.com/socketio/engine.io/commit/868d89111de0ab5bd0e147ecaff7983afbf5d087)) + + +### Features + +* increase the default value of pingTimeout ([5a7fa13](https://github.com/socketio/engine.io/commit/5a7fa132c442bc1e7eefa1cf38168ee951575ded)) +* remove dynamic require() with wsEngine ([edb7343](https://github.com/socketio/engine.io/commit/edb734316f143bf0f1bbc344e966d18e2676b934)) + + +### BREAKING CHANGES + +* the syntax of the "wsEngine" option is updated + +Before: + +```js +const eioServer = require("engine.io")(httpServer, { + wsEngine: "eiows" +}); +``` + +After: + +```js +const eioServer = require("engine.io")(httpServer, { + wsEngine: require("eiows").Server +}); +``` + + ## [4.1.1](https://github.com/socketio/engine.io/compare/4.1.0...4.1.1) (2021-02-02) diff --git a/package-lock.json b/package-lock.json index c335cecbd..318d833a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.1.1", + "version": "5.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -526,9 +526,9 @@ "dev": true }, "engine.io-client": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.1.1.tgz", - "integrity": "sha512-iYasV/EttP/2pLrdowe9G3zwlNIFhwny8VSIh+vPlMnYZqSzLsTzSLa9hFy015OrH1s4fzoYxeHjVkO8hSFKwg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.0.0.tgz", + "integrity": "sha512-e6GK0Fqvq45Nu/j7YdIVqXtDPvlsggAcfml3QiEiGdJ1qeh7IQU6knxSN3+yy9BmbnXtIfjo1hK4MFyHKdc9mQ==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", @@ -539,7 +539,6 @@ "parseqs": "0.0.6", "parseuri": "0.0.6", "ws": "~7.4.2", - "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" }, "dependencies": { diff --git a/package.json b/package.json index 9ab7e89e6..48c5b6b9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.1.1", + "version": "5.0.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io.js", "author": "Guillermo Rauch ", @@ -36,7 +36,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.3.0", - "engine.io-client": "4.1.1", + "engine.io-client": "5.0.0", "engine.io-client-v3": "npm:engine.io-client@3.5.0", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", From 7096e98a02295a62c8ea2aa56461d4875887092d Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 29 Apr 2021 16:41:16 +0200 Subject: [PATCH 05/58] feat: add a "connection_error" event The "connection_error" event will be emitted when one of the following errors occurs: - Transport unknown - Session ID unknown - Bad handshake method - Bad request - Forbidden - Unsupported protocol version Syntax: ```js server.on("connection_error", (err) => { console.log(err.req); // the request object console.log(err.code); // the error code, for example 1 console.log(err.message); // the error message, for example "Session ID unknown" console.log(err.context); // some additional error context }); ``` Related: - https://github.com/socketio/socket.io/issues/3819 - https://github.com/socketio/engine.io/issues/576 --- README.md | 18 +++++ lib/server.js | 148 +++++++++++++++++++++++++------------ test/common.js | 11 +++ test/server.js | 193 ++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 305 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 3b3659f05..729fca81c 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,24 @@ The main server/manager. _Inherits from EventEmitter_. - Fired when a new connection is established. - **Arguments** - `Socket`: a Socket object +- `connection_error` + - Fired when an error occurs when establishing the connection. + - **Arguments** + - `error`: an object with following properties: + - `req` (`http.IncomingMessage`): the request that was dropped + - `code` (`Number`): one of `Server.errors` + - `message` (`string`): one of `Server.errorMessages` + - `context` (`Object`): extra info about the error + +| Code | Message | +| ---- | ------- | +| 0 | "Transport unknown" +| 1 | "Session ID unknown" +| 2 | "Bad handshake method" +| 3 | "Bad request" +| 4 | "Forbidden" +| 5 | "Unsupported protocol version" + ##### Properties diff --git a/lib/server.js b/lib/server.js index bf604ed48..b3ef79625 100644 --- a/lib/server.js +++ b/lib/server.js @@ -109,15 +109,19 @@ class Server extends EventEmitter { const transport = req._query.transport; if (!~this.opts.transports.indexOf(transport)) { debug('unknown transport "%s"', transport); - return fn(Server.errors.UNKNOWN_TRANSPORT, false); + return fn(Server.errors.UNKNOWN_TRANSPORT, { transport }); } // 'Origin' header check const isOriginInvalid = checkInvalidHeaderChar(req.headers.origin); if (isOriginInvalid) { + const origin = req.headers.origin; req.headers.origin = null; debug("origin header invalid"); - return fn(Server.errors.BAD_REQUEST, false); + return fn(Server.errors.BAD_REQUEST, { + name: "INVALID_ORIGIN", + origin + }); } // sid check @@ -125,21 +129,40 @@ class Server extends EventEmitter { if (sid) { if (!this.clients.hasOwnProperty(sid)) { debug('unknown sid "%s"', sid); - return fn(Server.errors.UNKNOWN_SID, false); + return fn(Server.errors.UNKNOWN_SID, { + sid + }); } - if (!upgrade && this.clients[sid].transport.name !== transport) { + const previousTransport = this.clients[sid].transport.name; + if (!upgrade && previousTransport !== transport) { debug("bad request: unexpected transport without upgrade"); - return fn(Server.errors.BAD_REQUEST, false); + return fn(Server.errors.BAD_REQUEST, { + name: "TRANSPORT_MISMATCH", + transport, + previousTransport + }); } } else { // handshake is GET only - if ("GET" !== req.method) - return fn(Server.errors.BAD_HANDSHAKE_METHOD, false); - if (!this.opts.allowRequest) return fn(null, true); - return this.opts.allowRequest(req, fn); + if ("GET" !== req.method) { + return fn(Server.errors.BAD_HANDSHAKE_METHOD, { + method: req.method + }); + } + + if (!this.opts.allowRequest) return fn(); + + return this.opts.allowRequest(req, (message, success) => { + if (!success) { + return fn(Server.errors.FORBIDDEN, { + message + }); + } + fn(); + }); } - fn(null, true); + fn(); } /** @@ -186,9 +209,15 @@ class Server extends EventEmitter { this.prepare(req); req.res = res; - const callback = (err, success) => { - if (!success) { - sendErrorMessage(req, res, err); + const callback = (errorCode, errorContext) => { + if (errorCode !== undefined) { + this.emit("connection_error", { + req, + code: errorCode, + message: Server.errorMessages[errorCode], + context: errorContext + }); + sendErrorMessage(req, res, errorCode, errorContext); return; } @@ -231,6 +260,15 @@ class Server extends EventEmitter { const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default if (protocol === 3 && !this.opts.allowEIO3) { debug("unsupported protocol version"); + this.emit("connection_error", { + req, + code: Server.errors.UNSUPPORTED_PROTOCOL_VERSION, + message: + Server.errorMessages[Server.errors.UNSUPPORTED_PROTOCOL_VERSION], + context: { + protocol + } + }); sendErrorMessage( req, req.res, @@ -244,6 +282,15 @@ class Server extends EventEmitter { id = await this.generateId(req); } catch (e) { debug("error while generating an id"); + this.emit("connection_error", { + req, + code: Server.errors.BAD_REQUEST, + message: Server.errorMessages[Server.errors.BAD_REQUEST], + context: { + name: "ID_GENERATION_ERROR", + error: e + } + }); sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); return; } @@ -266,6 +313,15 @@ class Server extends EventEmitter { } } catch (e) { debug('error handshaking to transport "%s"', transportName); + this.emit("connection_error", { + req, + code: Server.errors.BAD_REQUEST, + message: Server.errorMessages[Server.errors.BAD_REQUEST], + context: { + name: "TRANSPORT_HANDSHAKE_ERROR", + error: e + } + }); sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); return; } @@ -304,9 +360,15 @@ class Server extends EventEmitter { this.prepare(req); const self = this; - this.verify(req, true, function(err, success) { - if (!success) { - abortConnection(socket, err); + this.verify(req, true, (errorCode, errorContext) => { + if (errorCode) { + this.emit("connection_error", { + req, + code: errorCode, + message: Server.errorMessages[errorCode], + context: errorContext + }); + abortConnection(socket, errorCode, errorContext); return; } @@ -469,52 +531,46 @@ Server.errorMessages = { /** * Sends an Engine.IO Error Message * - * @param {http.ServerResponse} response - * @param {code} error code + * @param req - the request object + * @param res - the response object + * @param errorCode - the error code + * @param errorContext - additional error context + * * @api private */ -function sendErrorMessage(req, res, code) { - const headers = { "Content-Type": "application/json" }; - - const isForbidden = !Server.errorMessages.hasOwnProperty(code); - if (isForbidden) { - res.writeHead(403, headers); - res.end( - JSON.stringify({ - code: Server.errors.FORBIDDEN, - message: code || Server.errorMessages[Server.errors.FORBIDDEN] - }) - ); - return; - } - if (res !== undefined) { - res.writeHead(400, headers); - res.end( - JSON.stringify({ - code: code, - message: Server.errorMessages[code] - }) - ); - } +function sendErrorMessage(req, res, errorCode, errorContext) { + const statusCode = errorCode === Server.errors.FORBIDDEN ? 403 : 400; + const message = + errorContext && errorContext.message + ? errorContext.message + : Server.errorMessages[errorCode]; + + res.writeHead(statusCode, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + code: errorCode, + message + }) + ); } /** * Closes the connection * * @param {net.Socket} socket - * @param {code} error code + * @param {string} errorCode - the error code + * @param {object} errorContext - additional error context + * * @api private */ -function abortConnection(socket, code) { +function abortConnection(socket, errorCode, errorContext) { socket.on("error", () => { debug("ignoring error from closed connection"); }); if (socket.writable) { - const message = Server.errorMessages.hasOwnProperty(code) - ? Server.errorMessages[code] - : String(code || ""); + const message = errorContext.message || Server.errorMessages[errorCode]; const length = Buffer.byteLength(message); socket.write( "HTTP/1.1 400 Bad Request\r\n" + diff --git a/test/common.js b/test/common.js index 95021ca79..1e747e412 100644 --- a/test/common.js +++ b/test/common.js @@ -34,3 +34,14 @@ exports.eioc = eioc; */ require("s").extend(); + +exports.createPartialDone = (done, count) => { + let i = 0; + return () => { + if (++i === count) { + done(); + } else if (i > count) { + done(new Error(`partialDone() called too many times: ${i} > ${count}`)); + } + }; +}; diff --git a/test/server.js b/test/server.js index c411aae70..53e57f2ef 100644 --- a/test/server.js +++ b/test/server.js @@ -7,8 +7,7 @@ const path = require("path"); const exec = require("child_process").exec; const zlib = require("zlib"); const eio = require(".."); -const eioc = require("./common").eioc; -const listen = require("./common").listen; +const { eioc, listen, createPartialDone } = require("./common"); const expect = require("expect.js"); const request = require("superagent"); const cookieMod = require("cookie"); @@ -18,9 +17,30 @@ const cookieMod = require("cookie"); */ describe("server", () => { + let engine, client; + + afterEach(() => { + if (engine) { + engine.httpServer.close(); + } + if (client) { + client.close(); + } + }); + describe("verification", () => { it("should disallow non-existent transports", done => { - listen(port => { + const partialDone = createPartialDone(done, 2); + + engine = listen(port => { + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(0); + expect(err.message).to.be("Transport unknown"); + expect(err.context.transport).to.be("tobi"); + partialDone(); + }); + request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "tobi" }) // no tobi transport - outrageous @@ -29,14 +49,24 @@ describe("server", () => { expect(res.status).to.be(400); expect(res.body.code).to.be(0); expect(res.body.message).to.be("Transport unknown"); - done(); + partialDone(); }); }); }); it("should disallow `constructor` as transports", done => { + const partialDone = createPartialDone(done, 2); + // make sure we check for actual properties - not those present on every {} - listen(port => { + engine = listen(port => { + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(0); + expect(err.message).to.be("Transport unknown"); + expect(err.context.transport).to.be("constructor"); + partialDone(); + }); + request .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") @@ -46,13 +76,23 @@ describe("server", () => { expect(res.status).to.be(400); expect(res.body.code).to.be(0); expect(res.body.message).to.be("Transport unknown"); - done(); + partialDone(); }); }); }); it("should disallow non-existent sids", done => { - listen(port => { + const partialDone = createPartialDone(done, 2); + + engine = listen(port => { + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(1); + expect(err.message).to.be("Session ID unknown"); + expect(err.context.sid).to.be("test"); + partialDone(); + }); + request .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") @@ -62,19 +102,29 @@ describe("server", () => { expect(res.status).to.be(400); expect(res.body.code).to.be(1); expect(res.body.message).to.be("Session ID unknown"); - done(); + partialDone(); }); }); }); it("should disallow requests that are rejected by `allowRequest`", done => { - listen( + const partialDone = createPartialDone(done, 2); + + engine = listen( { allowRequest: (req, fn) => { fn("Thou shall not pass", false); } }, port => { + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(4); + expect(err.message).to.be("Forbidden"); + expect(err.context.message).to.be("Thou shall not pass"); + partialDone(); + }); + request .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") @@ -84,7 +134,7 @@ describe("server", () => { expect(res.status).to.be(403); expect(res.body.code).to.be(4); expect(res.body.message).to.be("Thou shall not pass"); - done(); + partialDone(); }); } ); @@ -302,14 +352,24 @@ describe("server", () => { }); it("should disallow connection that are rejected by `generateId`", done => { - const engine = listen({ allowUpgrades: false }, port => { + const partialDone = createPartialDone(done, 2); + + engine = listen({ allowUpgrades: false }, port => { engine.generateId = () => { return Promise.reject(new Error("nope")); }; + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(3); + expect(err.message).to.be("Bad request"); + expect(err.context.name).to.be("ID_GENERATION_ERROR"); + partialDone(); + }); + const socket = new eioc.Socket("ws://localhost:%d".s(port)); socket.on("error", () => { - done(); + partialDone(); }); }); }); @@ -410,13 +470,25 @@ describe("server", () => { }); it("default to polling when proxy doesn't support websocket", done => { - const engine = listen({ allowUpgrades: false }, port => { + const partialDone = createPartialDone(done, 2); + + engine = listen({ allowUpgrades: false }, port => { engine.on("connection", socket => { socket.on("message", msg => { if ("echo" === msg) socket.send(msg); }); }); + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(3); + expect(err.message).to.be("Bad request"); + expect(err.context.name).to.be("TRANSPORT_MISMATCH"); + expect(err.context.transport).to.be("websocket"); + expect(err.context.previousTransport).to.be("polling"); + partialDone(); + }); + var socket = new eioc.Socket("ws://localhost:%d".s(port)); socket.on("open", () => { request @@ -430,7 +502,7 @@ describe("server", () => { socket.send("echo"); socket.on("message", msg => { expect(msg).to.be("echo"); - done(); + partialDone(); }); }); }); @@ -460,12 +532,24 @@ describe("server", () => { }); }); - it("should disallow bad requests", done => { - listen( + it("should disallow bad requests (handshake error)", done => { + const partialDone = createPartialDone(done, 2); + + engine = listen( { cors: { credentials: true, origin: "http://engine.io" } }, port => { + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(3); + expect(err.message).to.be("Bad request"); + expect(err.context.name).to.be("TRANSPORT_HANDSHAKE_ERROR"); + expect(err.context.error).to.be.an(Error); + expect(err.context.error.name).to.be("TypeError"); + partialDone(); + }); + request .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") @@ -481,18 +565,90 @@ describe("server", () => { expect(res.header["access-control-allow-origin"]).to.be( "http://engine.io" ); - done(); + partialDone(); }); } ); }); + it("should disallow invalid origin header", done => { + const partialDone = createPartialDone(done, 2); + + engine = listen(port => { + // we can't send an invalid header through request.get + // so add an invalid char here + engine.prepare = function(req) { + eio.Server.prototype.prepare.call(engine, req); + req.headers.origin += "\n"; + }; + + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(3); + expect(err.message).to.be("Bad request"); + expect(err.context.name).to.be("INVALID_ORIGIN"); + expect(err.context.origin).to.be("http://engine.io/\n"); + partialDone(); + }); + + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io/") + .query({ transport: "websocket" }) + .end((err, res) => { + expect(err).to.be.an(Error); + expect(res.status).to.be(400); + expect(res.body.code).to.be(3); + expect(res.body.message).to.be("Bad request"); + partialDone(); + }); + }); + }); + + it("should disallow invalid handshake method", done => { + const partialDone = createPartialDone(done, 2); + + engine = listen(port => { + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(2); + expect(err.message).to.be("Bad handshake method"); + expect(err.context.method).to.be("OPTIONS"); + partialDone(); + }); + + request + .options("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling" }) + .end((err, res) => { + expect(err).to.be.an(Error); + expect(res.status).to.be(400); + expect(res.body.code).to.be(2); + expect(res.body.message).to.be("Bad handshake method"); + partialDone(); + }); + }); + }); + it("should disallow unsupported protocol versions", done => { + const partialDone = createPartialDone(done, 2); + const httpServer = http.createServer(); const engine = eio({ allowEIO3: false }); engine.attach(httpServer); httpServer.listen(() => { const port = httpServer.address().port; + + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(5); + expect(err.message).to.be("Unsupported protocol version"); + expect(err.context.protocol).to.be(3); + + httpServer.close(); + partialDone(); + }); + request .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling", EIO: 3 }) @@ -501,8 +657,7 @@ describe("server", () => { expect(res.status).to.be(400); expect(res.body.code).to.be(5); expect(res.body.message).to.be("Unsupported protocol version"); - engine.close(); - done(); + partialDone(); }); }); }); From 252754353a0e88eb036ebb3082e9d6a9a5f497db Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 30 Apr 2021 13:28:11 +0200 Subject: [PATCH 06/58] feat: add the "initial_headers" and "headers" events Those events will be emitted before the response headers are written to the socket: - "initial_headers": on the first request of the connection - "headers": on all requests (HTTP long-polling and WebSocket upgrade) Syntax: ```js server.on("initial_headers", (headers, req) => { headers["test"] = "123"; headers["set-cookie"] = "mycookie=456"; }); server.on("headers", (headers, req) => { headers["test"] = "789"; }); ``` Related: - https://github.com/socketio/engine.io/issues/557 - https://github.com/socketio/socket.io/issues/3630 --- README.md | 13 ++++ lib/server.js | 41 ++++++++--- lib/socket.js | 83 +++++++++++----------- lib/transports/polling.js | 2 +- lib/transports/websocket.js | 3 - test/server.js | 137 ++++++++++++++++++++++++++++++++++++ 6 files changed, 224 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 729fca81c..c55a57455 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,19 @@ The main server/manager. _Inherits from EventEmitter_. - Fired when a new connection is established. - **Arguments** - `Socket`: a Socket object + +- `initial_headers` + - Fired on the first request of the connection, before writing the response headers + - **Arguments** + - `headers` (`Object`): a hash of headers + - `req` (`http.IncomingMessage`): the request + +- `headers` + - Fired on the all requests of the connection, before writing the response headers + - **Arguments** + - `headers` (`Object`): a hash of headers + - `req` (`http.IncomingMessage`): the request + - `connection_error` - Fired when an error occurs when establishing the connection. - **Arguments** diff --git a/lib/server.js b/lib/server.js index b3ef79625..bbb734829 100644 --- a/lib/server.js +++ b/lib/server.js @@ -84,6 +84,25 @@ class Server extends EventEmitter { perMessageDeflate: this.opts.perMessageDeflate, maxPayload: this.opts.maxHttpBufferSize }); + + if (typeof this.ws.on === "function") { + this.ws.on("headers", (headersArray, req) => { + // note: 'ws' uses an array of headers, while Engine.IO uses an object (response.writeHead() accepts both formats) + // we could also try to parse the array and then sync the values, but that will be error-prone + const additionalHeaders = {}; + + const isInitialRequest = !req._query.sid; + if (isInitialRequest) { + this.emit("initial_headers", additionalHeaders, req); + } + + this.emit("headers", additionalHeaders, req); + + Object.keys(additionalHeaders).forEach(key => { + headersArray.push(`${key}: ${additionalHeaders[key]}`); + }); + }); + } } /** @@ -328,15 +347,19 @@ class Server extends EventEmitter { const socket = new Socket(id, this, transport, req, protocol); const self = this; - if (this.opts.cookie) { - transport.on("headers", headers => { - headers["Set-Cookie"] = cookieMod.serialize( - this.opts.cookie.name, - id, - this.opts.cookie - ); - }); - } + transport.on("headers", (headers, req) => { + const isInitialRequest = !req._query.sid; + + if (isInitialRequest) { + if (this.opts.cookie) { + headers["Set-Cookie"] = [ + cookieMod.serialize(this.opts.cookie.name, id, this.opts.cookie) + ]; + } + this.emit("initial_headers", headers, req); + } + this.emit("headers", headers, req); + }); transport.onRequest(req); diff --git a/lib/socket.js b/lib/socket.js index 4920a26ef..3a7123cd4 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -81,49 +81,48 @@ class Socket extends EventEmitter { * @api private */ onPacket(packet) { - if ("open" === this.readyState) { - // export packet event - debug("packet"); - this.emit("packet", packet); - - // Reset ping timeout on any packet, incoming data is a good sign of - // other side's liveness - this.resetPingTimeout( - this.server.opts.pingInterval + this.server.opts.pingTimeout - ); + if ("open" !== this.readyState) { + return debug("packet received with closed socket"); + } + // export packet event + debug(`received packet ${packet.type}`); + this.emit("packet", packet); + + // Reset ping timeout on any packet, incoming data is a good sign of + // other side's liveness + this.resetPingTimeout( + this.server.opts.pingInterval + this.server.opts.pingTimeout + ); - switch (packet.type) { - case "ping": - if (this.transport.protocol !== 3) { - this.onError("invalid heartbeat direction"); - return; - } - debug("got ping"); - this.sendPacket("pong"); - this.emit("heartbeat"); - break; - - case "pong": - if (this.transport.protocol === 3) { - this.onError("invalid heartbeat direction"); - return; - } - debug("got pong"); - this.schedulePing(); - this.emit("heartbeat"); - break; - - case "error": - this.onClose("parse error"); - break; - - case "message": - this.emit("data", packet.data); - this.emit("message", packet.data); - break; - } - } else { - debug("packet received with closed socket"); + switch (packet.type) { + case "ping": + if (this.transport.protocol !== 3) { + this.onError("invalid heartbeat direction"); + return; + } + debug("got ping"); + this.sendPacket("pong"); + this.emit("heartbeat"); + break; + + case "pong": + if (this.transport.protocol === 3) { + this.onError("invalid heartbeat direction"); + return; + } + debug("got pong"); + this.schedulePing(); + this.emit("heartbeat"); + break; + + case "error": + this.onClose("parse error"); + break; + + case "message": + this.emit("data", packet.data); + this.emit("message", packet.data); + break; } } diff --git a/lib/transports/polling.js b/lib/transports/polling.js index e84f2773a..22e7e3c71 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -385,7 +385,7 @@ class Polling extends Transport { headers["X-XSS-Protection"] = "0"; } - this.emit("headers", headers); + this.emit("headers", headers, req); return headers; } } diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 6cd86c546..5c8d335ec 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -14,9 +14,6 @@ class WebSocket extends Transport { this.socket.on("message", this.onData.bind(this)); this.socket.once("close", this.onClose.bind(this)); this.socket.on("error", this.onError.bind(this)); - this.socket.on("headers", headers => { - this.emit("headers", headers); - }); this.writable = true; this.perMessageDeflate = null; } diff --git a/test/server.js b/test/server.js index 53e57f2ef..c72ac0771 100644 --- a/test/server.js +++ b/test/server.js @@ -3137,6 +3137,143 @@ describe("server", () => { }; testForHeaders(headers, done); }); + + it("should emit a 'initial_headers' event (polling)", done => { + const partialDone = createPartialDone(done, 2); + + engine = listen({ cookie: true }, port => { + engine.on("initial_headers", (headers, req) => { + expect(req.method).to.be("GET"); + headers["test"] = "123"; + headers["set-cookie"] = "mycookie=456"; + partialDone(); + }); + + request + .get("http://localhost:%d/engine.io/".s(port)) + .query({ transport: "polling" }) + .end((err, res) => { + expect(err).to.be(null); + expect(res.status).to.be(200); + expect(res.headers["test"]).to.be("123"); + expect(res.headers["set-cookie"].length).to.be(2); + expect(res.headers["set-cookie"][1]).to.be("mycookie=456"); + + const sid = JSON.parse(res.text.substring(4)).sid; + + request + .post("http://localhost:%d/engine.io/".s(port)) + .query({ transport: "polling", sid }) + .send("1:6") + .end((err, res) => { + expect(err).to.be(null); + expect(res.status).to.be(200); + expect(res.headers["test"]).to.be(undefined); + expect(res.headers["set-cookie"]).to.be(undefined); + partialDone(); + }); + }); + }); + }); + + it("should emit a 'headers' event (polling)", done => { + const partialDone = createPartialDone(done, 3); + + engine = listen({ cookie: true }, port => { + engine.on("headers", headers => { + headers["test"] = "123"; + headers["set-cookie"] = "mycookie=456"; + partialDone(); + }); + + request + .get("http://localhost:%d/engine.io/".s(port)) + .query({ transport: "polling" }) + .end((err, res) => { + expect(err).to.be(null); + expect(res.status).to.be(200); + expect(res.headers["test"]).to.be("123"); + expect(res.headers["set-cookie"].length).to.be(2); + expect(res.headers["set-cookie"][1]).to.be("mycookie=456"); + + const sid = JSON.parse(res.text.substring(4)).sid; + + request + .post("http://localhost:%d/engine.io/".s(port)) + .query({ transport: "polling", sid }) + .send("1:6") + .end((err, res) => { + expect(err).to.be(null); + expect(res.status).to.be(200); + expect(res.headers["set-cookie"].length).to.be(1); + expect(res.headers["set-cookie"][0]).to.be("mycookie=456"); + partialDone(); + }); + }); + }); + }); + + it("should emit a 'initial_headers' event (websocket)", function(done) { + if (process.env.EIO_WS_ENGINE === "eiows") { + this.skip(); + } + const partialDone = createPartialDone(done, 2); + + engine = listen({ cookie: true }, port => { + engine.on("initial_headers", (headers, req) => { + expect(req.method).to.be("GET"); + headers["test"] = "123"; + headers["set-cookie"] = "mycookie=456"; + partialDone(); + }); + + client = eioc("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + + client.transport.ws.on("upgrade", res => { + expect(res.headers["test"]).to.be("123"); + expect(res.headers["set-cookie"].length).to.be(1); + expect(res.headers["set-cookie"][0]).to.be("mycookie=456"); + partialDone(); + }); + }); + }); + + it("should emit a single 'initial_headers' event per connection", done => { + const partialDone = createPartialDone(done, 2); + + engine = listen(port => { + engine.on("initial_headers", () => { + partialDone(); + }); + + client = eioc("ws://localhost:%d".s(port)); + + client.on("upgrade", () => { + partialDone(); + }); + }); + }); + + it("should emit several 'headers' events per connection", function(done) { + if (process.env.EIO_WS_ENGINE === "eiows") { + this.skip(); + } + const partialDone = createPartialDone(done, 4); + + engine = listen(port => { + engine.on("headers", () => { + partialDone(); + }); + + client = eioc("ws://localhost:%d".s(port)); + + client.on("upgrade", () => { + partialDone(); + }); + }); + }); }); describe("cors", () => { From 4c0aa73e060f4e2fecdea345fe600f0c29ef575e Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 30 Apr 2021 14:30:47 +0200 Subject: [PATCH 07/58] refactor: remove "self" references --- lib/server.js | 30 +++++------ lib/socket.js | 102 +++++++++++++++++------------------- lib/transports/polling.js | 90 +++++++++++++++---------------- lib/transports/websocket.js | 42 +++++++-------- 4 files changed, 121 insertions(+), 143 deletions(-) diff --git a/lib/server.js b/lib/server.js index bbb734829..bc3cefb75 100644 --- a/lib/server.js +++ b/lib/server.js @@ -345,7 +345,6 @@ class Server extends EventEmitter { return; } const socket = new Socket(id, this, transport, req, protocol); - const self = this; transport.on("headers", (headers, req) => { const isInitialRequest = !req._query.sid; @@ -366,9 +365,9 @@ class Server extends EventEmitter { this.clients[id] = socket; this.clientsCount++; - socket.once("close", function() { - delete self.clients[id]; - self.clientsCount--; + socket.once("close", () => { + delete this.clients[id]; + this.clientsCount--; }); this.emit("connection", socket); @@ -382,7 +381,6 @@ class Server extends EventEmitter { handleUpgrade(req, socket, upgradeHead) { this.prepare(req); - const self = this; this.verify(req, true, (errorCode, errorContext) => { if (errorCode) { this.emit("connection_error", { @@ -399,8 +397,8 @@ class Server extends EventEmitter { upgradeHead = null; // delegate to ws - self.ws.handleUpgrade(req, socket, head, function(conn) { - self.onWebSocket(req, conn); + this.ws.handleUpgrade(req, socket, head, conn => { + this.onWebSocket(req, conn); }); }); } @@ -475,9 +473,7 @@ class Server extends EventEmitter { * @param {Object} options * @api public */ - attach(server, options) { - const self = this; - options = options || {}; + attach(server, options = {}) { let path = (options.path || "/engine.io").replace(/\/$/, ""); const destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000; @@ -492,14 +488,14 @@ class Server extends EventEmitter { // cache and clean up listeners const listeners = server.listeners("request").slice(0); server.removeAllListeners("request"); - server.on("close", self.close.bind(self)); - server.on("listening", self.init.bind(self)); + server.on("close", this.close.bind(this)); + server.on("listening", this.init.bind(this)); // add request handler - server.on("request", function(req, res) { + server.on("request", (req, res) => { if (check(req)) { debug('intercepting request for path "%s"', path); - self.handleRequest(req, res); + this.handleRequest(req, res); } else { let i = 0; const l = listeners.length; @@ -509,10 +505,10 @@ class Server extends EventEmitter { } }); - if (~self.opts.transports.indexOf("websocket")) { - server.on("upgrade", function(req, socket, head) { + if (~this.opts.transports.indexOf("websocket")) { + server.on("upgrade", (req, socket, head) => { if (check(req)) { - self.handleUpgrade(req, socket, head); + this.handleUpgrade(req, socket, head); } else if (false !== options.destroyUpgrade) { // default node behavior is to disconnect when no handlers // but by adding a handler, we prevent that diff --git a/lib/socket.js b/lib/socket.js index 3a7123cd4..733793416 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -211,10 +211,8 @@ class Socket extends EventEmitter { this.upgrading = true; - const self = this; - // set transport upgrade timer - self.upgradeTimeoutTimer = setTimeout(function() { + this.upgradeTimeoutTimer = setTimeout(() => { debug("client did not complete upgrade - closing transport"); cleanup(); if ("open" === transport.readyState) { @@ -222,75 +220,75 @@ class Socket extends EventEmitter { } }, this.server.opts.upgradeTimeout); - function onPacket(packet) { + const onPacket = packet => { if ("ping" === packet.type && "probe" === packet.data) { transport.send([{ type: "pong", data: "probe" }]); - self.emit("upgrading", transport); - clearInterval(self.checkIntervalTimer); - self.checkIntervalTimer = setInterval(check, 100); - } else if ("upgrade" === packet.type && self.readyState !== "closed") { + this.emit("upgrading", transport); + clearInterval(this.checkIntervalTimer); + this.checkIntervalTimer = setInterval(check, 100); + } else if ("upgrade" === packet.type && this.readyState !== "closed") { debug("got upgrade packet - upgrading"); cleanup(); - self.transport.discard(); - self.upgraded = true; - self.clearTransport(); - self.setTransport(transport); - self.emit("upgrade", transport); - self.flush(); - if (self.readyState === "closing") { - transport.close(function() { - self.onClose("forced close"); + this.transport.discard(); + this.upgraded = true; + this.clearTransport(); + this.setTransport(transport); + this.emit("upgrade", transport); + this.flush(); + if (this.readyState === "closing") { + transport.close(() => { + this.onClose("forced close"); }); } } else { cleanup(); transport.close(); } - } + }; // we force a polling cycle to ensure a fast upgrade - function check() { - if ("polling" === self.transport.name && self.transport.writable) { + const check = () => { + if ("polling" === this.transport.name && this.transport.writable) { debug("writing a noop packet to polling for fast upgrade"); - self.transport.send([{ type: "noop" }]); + this.transport.send([{ type: "noop" }]); } - } + }; - function cleanup() { - self.upgrading = false; + const cleanup = () => { + this.upgrading = false; - clearInterval(self.checkIntervalTimer); - self.checkIntervalTimer = null; + clearInterval(this.checkIntervalTimer); + this.checkIntervalTimer = null; - clearTimeout(self.upgradeTimeoutTimer); - self.upgradeTimeoutTimer = null; + clearTimeout(this.upgradeTimeoutTimer); + this.upgradeTimeoutTimer = null; transport.removeListener("packet", onPacket); transport.removeListener("close", onTransportClose); transport.removeListener("error", onError); - self.removeListener("close", onClose); - } + this.removeListener("close", onClose); + }; - function onError(err) { + const onError = err => { debug("client did not complete upgrade - %s", err); cleanup(); transport.close(); transport = null; - } + }; - function onTransportClose() { + const onTransportClose = () => { onError("transport closed"); - } + }; - function onClose() { + const onClose = () => { onError("socket closed"); - } + }; transport.on("packet", onPacket); transport.once("close", onTransportClose); transport.once("error", onError); - self.once("close", onClose); + this.once("close", onClose); } /** @@ -335,11 +333,10 @@ class Socket extends EventEmitter { clearInterval(this.checkIntervalTimer); this.checkIntervalTimer = null; clearTimeout(this.upgradeTimeoutTimer); - const self = this; // clean writeBuffer in next tick, so developers can still // grab the writeBuffer on 'close' event - process.nextTick(function() { - self.writeBuffer = []; + process.nextTick(() => { + this.writeBuffer = []; }); this.packetsFn = []; this.sentCallbackFn = []; @@ -354,32 +351,31 @@ class Socket extends EventEmitter { * @api private */ setupSendCallback() { - const self = this; - this.transport.on("drain", onDrain); - - this.cleanupFn.push(function() { - self.transport.removeListener("drain", onDrain); - }); - // the message was sent successfully, execute the callback - function onDrain() { - if (self.sentCallbackFn.length > 0) { - const seqFn = self.sentCallbackFn.splice(0, 1)[0]; + const onDrain = () => { + if (this.sentCallbackFn.length > 0) { + const seqFn = this.sentCallbackFn.splice(0, 1)[0]; if ("function" === typeof seqFn) { debug("executing send callback"); - seqFn(self.transport); + seqFn(this.transport); } else if (Array.isArray(seqFn)) { debug("executing batch send callback"); const l = seqFn.length; let i = 0; for (; i < l; i++) { if ("function" === typeof seqFn[i]) { - seqFn[i](self.transport); + seqFn[i](this.transport); } } } } - } + }; + + this.transport.on("drain", onDrain); + + this.cleanupFn.push(() => { + this.transport.removeListener("drain", onDrain); + }); } /** diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 22e7e3c71..f0909d070 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -70,16 +70,14 @@ class Polling extends Transport { this.req = req; this.res = res; - const self = this; - - function onClose() { - self.onError("poll connection closed prematurely"); - } + const onClose = () => { + this.onError("poll connection closed prematurely"); + }; - function cleanup() { + const cleanup = () => { req.removeListener("close", onClose); - self.req = self.res = null; - } + this.req = this.res = null; + }; req.cleanup = cleanup; req.on("close", onClose); @@ -118,21 +116,20 @@ class Polling extends Transport { this.dataRes = res; let chunks = isBinary ? Buffer.concat([]) : ""; - const self = this; - function cleanup() { + const cleanup = () => { req.removeListener("data", onData); req.removeListener("end", onEnd); req.removeListener("close", onClose); - self.dataReq = self.dataRes = chunks = null; - } + this.dataReq = this.dataRes = chunks = null; + }; - function onClose() { + const onClose = () => { cleanup(); - self.onError("data request connection closed prematurely"); - } + this.onError("data request connection closed prematurely"); + }; - function onData(data) { + const onData = data => { let contentLength; if (isBinary) { chunks = Buffer.concat([chunks, data]); @@ -142,14 +139,14 @@ class Polling extends Transport { contentLength = Buffer.byteLength(chunks); } - if (contentLength > self.maxHttpBufferSize) { + if (contentLength > this.maxHttpBufferSize) { chunks = isBinary ? Buffer.concat([]) : ""; req.connection.destroy(); } - } + }; - function onEnd() { - self.onData(chunks); + const onEnd = () => { + this.onData(chunks); const headers = { // text/html is required instead of text/plain to avoid an @@ -158,10 +155,10 @@ class Polling extends Transport { "Content-Length": 2 }; - res.writeHead(200, self.headers(req, headers)); + res.writeHead(200, this.headers(req, headers)); res.end("ok"); cleanup(); - } + }; req.on("close", onClose); if (!isBinary) req.setEncoding("utf8"); @@ -177,15 +174,14 @@ class Polling extends Transport { */ onData(data) { debug('received "%s"', data); - const self = this; - const callback = function(packet) { + const callback = packet => { if ("close" === packet.type) { debug("got xhr close packet"); - self.onClose(); + this.onClose(); return false; } - self.onPacket(packet); + this.onPacket(packet); }; if (this.protocol === 3) { @@ -247,9 +243,8 @@ class Polling extends Transport { */ write(data, options) { debug('writing "%s"', data); - const self = this; - this.doWrite(data, options, function() { - self.req.cleanup(); + this.doWrite(data, options, () => { + this.req.cleanup(); }); } @@ -259,8 +254,6 @@ class Polling extends Transport { * @api private */ doWrite(data, options, callback) { - const self = this; - // explicit UTF-8 is required for pages not served under utf const isString = typeof data === "string"; const contentType = isString @@ -271,6 +264,14 @@ class Polling extends Transport { "Content-Type": contentType }; + const respond = data => { + headers["Content-Length"] = + "string" === typeof data ? Buffer.byteLength(data) : data.length; + this.res.writeHead(200, this.headers(this.req, headers)); + this.res.end(data); + callback(); + }; + if (!this.httpCompression || !options.compress) { respond(data); return; @@ -288,10 +289,10 @@ class Polling extends Transport { return; } - this.compress(data, encoding, function(err, data) { + this.compress(data, encoding, (err, data) => { if (err) { - self.res.writeHead(500); - self.res.end(); + this.res.writeHead(500); + this.res.end(); callback(err); return; } @@ -299,14 +300,6 @@ class Polling extends Transport { headers["Content-Encoding"] = encoding; respond(data); }); - - function respond(data) { - headers["Content-Length"] = - "string" === typeof data ? Buffer.byteLength(data) : data.length; - self.res.writeHead(200, self.headers(self.req, headers)); - self.res.end(data); - callback(); - } } /** @@ -340,7 +333,6 @@ class Polling extends Transport { doClose(fn) { debug("closing"); - const self = this; let closeTimeoutTimer; if (this.dataReq) { @@ -348,6 +340,12 @@ class Polling extends Transport { this.dataReq.destroy(); } + const onClose = () => { + clearTimeout(closeTimeoutTimer); + fn(); + this.onClose(); + }; + if (this.writable) { debug("transport writable - closing right away"); this.send([{ type: "close" }]); @@ -360,12 +358,6 @@ class Polling extends Transport { this.shouldClose = onClose; closeTimeoutTimer = setTimeout(onClose, this.closeTimeout); } - - function onClose() { - clearTimeout(closeTimeoutTimer); - fn(); - self.onClose(); - } } /** diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 5c8d335ec..500d7ad44 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -63,38 +63,32 @@ class WebSocket extends Transport { * @api private */ send(packets) { - var self = this; - - for (var i = 0; i < packets.length; i++) { - var packet = packets[i]; - this.parser.encodePacket(packet, self.supportsBinary, send); - } - - function send(data) { - debug('writing "%s"', data); + for (let i = 0; i < packets.length; i++) { + const packet = packets[i]; // always creates a new object since ws modifies it - var opts = {}; + const opts = {}; if (packet.options) { opts.compress = packet.options.compress; } - if (self.perMessageDeflate) { - var len = - "string" === typeof data ? Buffer.byteLength(data) : data.length; - if (len < self.perMessageDeflate.threshold) { - opts.compress = false; + this.parser.encodePacket(packet, this.supportsBinary, data => { + if (this.perMessageDeflate) { + const len = + "string" === typeof data ? Buffer.byteLength(data) : data.length; + if (len < this.perMessageDeflate.threshold) { + opts.compress = false; + } } - } - - self.writable = false; - self.socket.send(data, opts, onEnd); - } + debug('writing "%s"', data); + this.writable = false; - function onEnd(err) { - if (err) return self.onError("write error", err.stack); - self.writable = true; - self.emit("drain"); + this.socket.send(data, opts, err => { + if (err) return this.onError("write error", err.stack); + this.writable = true; + this.emit("drain"); + }); + }); } } From ad5306aeaedf06ac7a49f791e1b76e55c35a564e Mon Sep 17 00:00:00 2001 From: Branislav Katreniak Date: Tue, 4 May 2021 08:35:23 +0200 Subject: [PATCH 08/58] perf(websocket): fix write back-pressure (#618) This change should reduce memory usage when many packets are emitted to many clients in a burst. Co-authored-by: Branislav Katreniak --- lib/transports/websocket.js | 46 +++++++++++++++++++------------------ test/server.js | 4 +++- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 500d7ad44..304f5c641 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -63,33 +63,35 @@ class WebSocket extends Transport { * @api private */ send(packets) { - for (let i = 0; i < packets.length; i++) { - const packet = packets[i]; + const packet = packets.shift(); + if (typeof packet === "undefined") { + this.writable = true; + this.emit("drain"); + return; + } - // always creates a new object since ws modifies it - const opts = {}; - if (packet.options) { - opts.compress = packet.options.compress; - } + // always creates a new object since ws modifies it + const opts = {}; + if (packet.options) { + opts.compress = packet.options.compress; + } - this.parser.encodePacket(packet, this.supportsBinary, data => { - if (this.perMessageDeflate) { - const len = - "string" === typeof data ? Buffer.byteLength(data) : data.length; - if (len < this.perMessageDeflate.threshold) { - opts.compress = false; - } + this.parser.encodePacket(packet, this.supportsBinary, data => { + if (this.perMessageDeflate) { + const len = + "string" === typeof data ? Buffer.byteLength(data) : data.length; + if (len < this.perMessageDeflate.threshold) { + opts.compress = false; } - debug('writing "%s"', data); - this.writable = false; + } + debug('writing "%s"', data); + this.writable = false; - this.socket.send(data, opts, err => { - if (err) return this.onError("write error", err.stack); - this.writable = true; - this.emit("drain"); - }); + this.socket.send(data, opts, err => { + if (err) return this.onError("write error", err.stack); + this.send(packets); }); - } + }); } /** diff --git a/test/server.js b/test/server.js index c72ac0771..758897c49 100644 --- a/test/server.js +++ b/test/server.js @@ -1681,7 +1681,9 @@ describe("server", () => { conn.send("a"); conn.send("b"); conn.send("c"); - conn.close(); + setTimeout(() => { + conn.close(); + }, 50); }); socket.on("open", () => { From 7706b123df914777d19c8179b45ab6932f82916c Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 4 May 2021 08:44:06 +0200 Subject: [PATCH 09/58] perf: add a "wsPreEncoded" writing option This option will be used when broadcasting a packet to multiple clients, in order to only encode the packet once. Usage: ```js socket.write("test", { wsPreEncoded: "4test" }); ``` Note: pre-encoding the content with HTTP long-polling is a bit harder, since the concatenation of the packets is specific to each client. --- lib/transports/websocket.js | 10 ++++++++-- test/server.js | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 304f5c641..1daf6caa9 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -76,7 +76,7 @@ class WebSocket extends Transport { opts.compress = packet.options.compress; } - this.parser.encodePacket(packet, this.supportsBinary, data => { + const send = data => { if (this.perMessageDeflate) { const len = "string" === typeof data ? Buffer.byteLength(data) : data.length; @@ -91,7 +91,13 @@ class WebSocket extends Transport { if (err) return this.onError("write error", err.stack); this.send(packets); }); - }); + }; + + if (packet.options && typeof packet.options.wsPreEncoded === "string") { + send(packet.options.wsPreEncoded); + } else { + this.parser.encodePacket(packet, this.supportsBinary, send); + } } /** diff --git a/test/server.js b/test/server.js index 758897c49..0212e556e 100644 --- a/test/server.js +++ b/test/server.js @@ -2639,6 +2639,27 @@ describe("server", () => { }); }); }); + + describe("pre-encoded content", () => { + it("should use the pre-encoded content", done => { + engine = listen(port => { + client = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + + engine.on("connection", conn => { + conn.send("test", { + wsPreEncoded: "4test pre-encoded" + }); + }); + + client.on("message", msg => { + expect(msg).to.be("test pre-encoded"); + done(); + }); + }); + }); + }); }); describe("packet", () => { From 8c9bd9262f1c664722194c8c321cba947cf0bf54 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 4 May 2021 10:31:23 +0200 Subject: [PATCH 10/58] chore(release): 5.1.0 Diff: https://github.com/socketio/engine.io/compare/5.0.0...5.1.0 --- CHANGELOG.md | 15 +++++++++++++++ package-lock.json | 8 ++++---- package.json | 4 ++-- test/server.js | 18 ++++++++++-------- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7177937db..354115e62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# [5.1.0](https://github.com/socketio/engine.io/compare/5.0.0...5.1.0) (2021-05-04) + + +### Features + +* add a "connection_error" event ([7096e98](https://github.com/socketio/engine.io/commit/7096e98a02295a62c8ea2aa56461d4875887092d)) +* add the "initial_headers" and "headers" events ([2527543](https://github.com/socketio/engine.io/commit/252754353a0e88eb036ebb3082e9d6a9a5f497db)) + + +### Performance Improvements + +* **websocket:** add a "wsPreEncoded" writing option ([7706b12](https://github.com/socketio/engine.io/commit/7706b123df914777d19c8179b45ab6932f82916c)) +* **websocket:** fix write back-pressure ([#618](https://github.com/socketio/engine.io/issues/618)) ([ad5306a](https://github.com/socketio/engine.io/commit/ad5306aeaedf06ac7a49f791e1b76e55c35a564e)) + + # [5.0.0](https://github.com/socketio/engine.io/compare/4.1.1...5.0.0) (2021-03-10) diff --git a/package-lock.json b/package-lock.json index 318d833a9..60176f956 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "5.0.0", + "version": "5.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -526,9 +526,9 @@ "dev": true }, "engine.io-client": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.0.0.tgz", - "integrity": "sha512-e6GK0Fqvq45Nu/j7YdIVqXtDPvlsggAcfml3QiEiGdJ1qeh7IQU6knxSN3+yy9BmbnXtIfjo1hK4MFyHKdc9mQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.0.tgz", + "integrity": "sha512-nXE1q63uBs6GqygguQwE0q+gLVaEo8YN3tAJgADOyBu3gFV5XzAeBLlry9if6KiswzOnNUCUYqk47vC0g7WXRA==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", diff --git a/package.json b/package.json index 48c5b6b9f..cc443580f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "5.0.0", + "version": "5.1.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io.js", "author": "Guillermo Rauch ", @@ -36,7 +36,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.3.0", - "engine.io-client": "5.0.0", + "engine.io-client": "5.1.0", "engine.io-client-v3": "npm:engine.io-client@3.5.0", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", diff --git a/test/server.js b/test/server.js index 0212e556e..9f8e70736 100644 --- a/test/server.js +++ b/test/server.js @@ -740,7 +740,8 @@ describe("server", () => { done(); }); // client abruptly disconnects, no polling request on this tick since we've just connected - socket.sendPacket = socket.onPacket = () => {}; + socket.sendPacket = () => {}; + socket.transport.removeListener("packet"); socket.close(); // then server app tries to close the socket, since client disappeared conn.close(); @@ -754,8 +755,8 @@ describe("server", () => { const socket = new eioc.Socket("ws://localhost:%d".s(port)); socket.on("open", () => { // override onPacket and Transport#onClose to simulate an inactive server after handshake - socket.onPacket = () => {}; - socket.transport.onClose = () => {}; + socket.transport.removeListener("packet"); + socket.transport.removeListener("close"); socket.on("close", (reason, err) => { expect(reason).to.be("ping timeout"); done(); @@ -781,8 +782,9 @@ describe("server", () => { socket.on("open", () => { // override onPacket and Transport#onClose to simulate an inactive server after handshake - socket.onPacket = socket.sendPacket = () => {}; - socket.transport.onClose = () => {}; + socket.sendPacket = () => {}; + socket.transport.removeListener("packet"); + socket.transport.removeListener("close"); socket.on("close", onClose); }); }); @@ -1085,7 +1087,7 @@ describe("server", () => { let clientCloseReason = null; socket.on("handshake", () => { - socket.onPacket = () => {}; + socket.transport.removeListener("packet"); }); socket.on("open", () => { socket.on("close", reason => { @@ -1195,7 +1197,7 @@ describe("server", () => { engine.on("connection", conn => { conn.once("heartbeat", () => { setTimeout(() => { - socket.onPacket = () => {}; + socket.transport.removeListener("packet"); expect(clientCloseReason).to.be(null); }, 150); setTimeout(() => { @@ -1232,7 +1234,7 @@ describe("server", () => { engine.on("connection", conn => { conn.once("heartbeat", () => { - socket.onPacket = () => {}; + socket.transport.removeListener("packet"); setTimeout(() => { expect(clientCloseReason).to.be(null); }, 150); From 29bd4fe800397adf0993c16311dc4614cc0a9c5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 00:21:34 +0200 Subject: [PATCH 11/58] chore(deps): bump lodash from 4.17.20 to 4.17.21 (#619) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 60176f956..7486b833f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1083,9 +1083,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "loose-envify": { From 43606865e5299747cbb31f3ed9baf4567502a879 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sun, 16 May 2021 23:54:02 +0200 Subject: [PATCH 12/58] fix: properly close the websocket connection upon handshake error This should fix the following error: > TypeError: Cannot read property 'writeHead' of undefined This bug was introduced by [1], because the `if (res !== undefined) { ... }` check was removed. But `res` is indeed undefined when the client connects with WebSocket directly, in that case we need to manually write the response content (in the abortUpgrade method). Please note that the previous behavior was invalid too, since the WebSocket connection was left open when an error occurred during the handshake. [1]: https://github.com/socketio/engine.io/commit/7096e98a02295a62c8ea2aa56461d4875887092d --- lib/server.js | 59 +++++++++++++++++++++++++------------------------- test/server.js | 28 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/lib/server.js b/lib/server.js index bc3cefb75..d3087ab8b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -236,7 +236,7 @@ class Server extends EventEmitter { message: Server.errorMessages[errorCode], context: errorContext }); - sendErrorMessage(req, res, errorCode, errorContext); + abortRequest(res, errorCode, errorContext); return; } @@ -244,7 +244,9 @@ class Server extends EventEmitter { debug("setting new request for existing client"); this.clients[req._query.sid].transport.onRequest(req); } else { - this.handshake(req._query.transport, req); + const closeConnection = (errorCode, errorContext) => + abortRequest(res, errorCode, errorContext); + this.handshake(req._query.transport, req, closeConnection); } }; @@ -273,9 +275,11 @@ class Server extends EventEmitter { * * @param {String} transport name * @param {Object} request object + * @param {Function} closeConnection + * * @api private */ - async handshake(transportName, req) { + async handshake(transportName, req, closeConnection) { const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default if (protocol === 3 && !this.opts.allowEIO3) { debug("unsupported protocol version"); @@ -288,11 +292,7 @@ class Server extends EventEmitter { protocol } }); - sendErrorMessage( - req, - req.res, - Server.errors.UNSUPPORTED_PROTOCOL_VERSION - ); + closeConnection(Server.errors.UNSUPPORTED_PROTOCOL_VERSION); return; } @@ -310,7 +310,7 @@ class Server extends EventEmitter { error: e } }); - sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); + closeConnection(Server.errors.BAD_REQUEST); return; } @@ -341,7 +341,7 @@ class Server extends EventEmitter { error: e } }); - sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); + closeConnection(Server.errors.BAD_REQUEST); return; } const socket = new Socket(id, this, transport, req, protocol); @@ -389,7 +389,7 @@ class Server extends EventEmitter { message: Server.errorMessages[errorCode], context: errorContext }); - abortConnection(socket, errorCode, errorContext); + abortUpgrade(socket, errorCode, errorContext); return; } @@ -397,8 +397,8 @@ class Server extends EventEmitter { upgradeHead = null; // delegate to ws - this.ws.handleUpgrade(req, socket, head, conn => { - this.onWebSocket(req, conn); + this.ws.handleUpgrade(req, socket, head, websocket => { + this.onWebSocket(req, socket, websocket); }); }); } @@ -409,15 +409,15 @@ class Server extends EventEmitter { * @param {ws.Socket} websocket * @api private */ - onWebSocket(req, socket) { - socket.on("error", onUpgradeError); + onWebSocket(req, socket, websocket) { + websocket.on("error", onUpgradeError); if ( transports[req._query.transport] !== undefined && !transports[req._query.transport].prototype.handlesUpgrades ) { debug("transport doesnt handle upgraded requests"); - socket.close(); + websocket.close(); return; } @@ -425,24 +425,24 @@ class Server extends EventEmitter { const id = req._query.sid; // keep a reference to the ws.Socket - req.websocket = socket; + req.websocket = websocket; if (id) { const client = this.clients[id]; if (!client) { debug("upgrade attempt for closed client"); - socket.close(); + websocket.close(); } else if (client.upgrading) { debug("transport has already been trying to upgrade"); - socket.close(); + websocket.close(); } else if (client.upgraded) { debug("transport had already been upgraded"); - socket.close(); + websocket.close(); } else { debug("upgrading existing transport"); // transport error handling takes over - socket.removeListener("error", onUpgradeError); + websocket.removeListener("error", onUpgradeError); const transport = new transports[req._query.transport](req); if (req._query && req._query.b64) { @@ -455,14 +455,16 @@ class Server extends EventEmitter { } } else { // transport error handling takes over - socket.removeListener("error", onUpgradeError); + websocket.removeListener("error", onUpgradeError); - this.handshake(req._query.transport, req); + const closeConnection = (errorCode, errorContext) => + abortUpgrade(socket, errorCode, errorContext); + this.handshake(req._query.transport, req, closeConnection); } function onUpgradeError() { debug("websocket error before upgrade"); - // socket.close() not needed + // websocket.close() not needed } } @@ -548,9 +550,8 @@ Server.errorMessages = { }; /** - * Sends an Engine.IO Error Message + * Close the HTTP long-polling request * - * @param req - the request object * @param res - the response object * @param errorCode - the error code * @param errorContext - additional error context @@ -558,7 +559,7 @@ Server.errorMessages = { * @api private */ -function sendErrorMessage(req, res, errorCode, errorContext) { +function abortRequest(res, errorCode, errorContext) { const statusCode = errorCode === Server.errors.FORBIDDEN ? 403 : 400; const message = errorContext && errorContext.message @@ -575,7 +576,7 @@ function sendErrorMessage(req, res, errorCode, errorContext) { } /** - * Closes the connection + * Close the WebSocket connection * * @param {net.Socket} socket * @param {string} errorCode - the error code @@ -584,7 +585,7 @@ function sendErrorMessage(req, res, errorCode, errorContext) { * @api private */ -function abortConnection(socket, errorCode, errorContext) { +function abortUpgrade(socket, errorCode, errorContext = {}) { socket.on("error", () => { debug("ignoring error from closed connection"); }); diff --git a/test/server.js b/test/server.js index 9f8e70736..b8a60b55c 100644 --- a/test/server.js +++ b/test/server.js @@ -374,6 +374,34 @@ describe("server", () => { }); }); + it("should disallow connection that are rejected by `generateId` (websocket only)", function(done) { + if (process.env.EIO_WS_ENGINE === "eiows") { + this.skip(); + } + const partialDone = createPartialDone(done, 2); + + engine = listen({ allowUpgrades: false }, port => { + engine.generateId = () => { + return Promise.reject(new Error("nope")); + }; + + engine.on("connection_error", err => { + expect(err.req).to.be.an(http.IncomingMessage); + expect(err.code).to.be(3); + expect(err.message).to.be("Bad request"); + expect(err.context.name).to.be("ID_GENERATION_ERROR"); + partialDone(); + }); + + const socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + socket.on("error", () => { + partialDone(); + }); + }); + }); + it("should exchange handshake data", done => { listen({ allowUpgrades: false }, port => { const socket = new eioc.Socket("ws://localhost:%d".s(port)); From 733ad4dc4e551ea5319d0efe8895fce4c79a4752 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 17 May 2021 00:29:34 +0200 Subject: [PATCH 13/58] chore(release): 5.1.1 Diff: https://github.com/socketio/engine.io/compare/5.1.0...5.1.1 --- CHANGELOG.md | 8 ++++++++ package-lock.json | 8 ++++---- package.json | 4 ++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 354115e62..681f4d83d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [5.1.1](https://github.com/socketio/engine.io/compare/5.1.0...5.1.1) (2021-05-16) + + +### Bug Fixes + +* properly close the websocket connection upon handshake error ([4360686](https://github.com/socketio/engine.io/commit/43606865e5299747cbb31f3ed9baf4567502a879)) + + # [5.1.0](https://github.com/socketio/engine.io/compare/5.0.0...5.1.0) (2021-05-04) diff --git a/package-lock.json b/package-lock.json index 7486b833f..dd10c2e08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "5.1.0", + "version": "5.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -526,9 +526,9 @@ "dev": true }, "engine.io-client": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.0.tgz", - "integrity": "sha512-nXE1q63uBs6GqygguQwE0q+gLVaEo8YN3tAJgADOyBu3gFV5XzAeBLlry9if6KiswzOnNUCUYqk47vC0g7WXRA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.1.tgz", + "integrity": "sha512-jPFpw2HLL0lhZ2KY0BpZhIJdleQcUO9W1xkIpo0h3d6s+5D6+EV/xgQw9qWOmymszv2WXef/6KUUehyxEKomlQ==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", diff --git a/package.json b/package.json index cc443580f..12e3e2a09 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "5.1.0", + "version": "5.1.1", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io.js", "author": "Guillermo Rauch ", @@ -36,7 +36,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.3.0", - "engine.io-client": "5.1.0", + "engine.io-client": "5.1.1", "engine.io-client-v3": "npm:engine.io-client@3.5.0", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", From 7e9e544a9ac4365ef055c9482cc1e9fed624031c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 30 May 2021 23:05:15 +0200 Subject: [PATCH 14/58] chore(deps): bump ws from 7.4.2 to 7.4.6 (#621) Bumps [ws](https://github.com/websockets/ws) from 7.4.2 to 7.4.6. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.4.2...7.4.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd10c2e08..266176146 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1699,9 +1699,9 @@ } }, "ws": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", - "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" }, "xmlhttprequest-ssl": { "version": "1.5.5", From b9dc43c3e17524ed51646ead20cb27cbf5639ce1 Mon Sep 17 00:00:00 2001 From: Kamil93 Date: Sun, 29 Aug 2021 08:25:06 +0200 Subject: [PATCH 15/58] docs: add documentation about `heartbeat` event (#623) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c55a57455..0f5a9da44 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,8 @@ A representation of a client. _Inherits from EventEmitter_. - **Arguments** - `type`: packet type - `data`: packet data (if type is message) +- `heartbeat` + - Called when `ping` or `pong` packed is received (depends of client version) ##### Properties From 313ca50ce8fdf33f0df1ad3246f1fc349078e44e Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sun, 29 Aug 2021 08:32:20 +0200 Subject: [PATCH 16/58] chore(release): 5.2.0 Diff: https://github.com/socketio/engine.io/compare/5.1.1...5.2.0 --- CHANGELOG.md | 5 +++++ package-lock.json | 21 ++++++++++++++------- package.json | 4 ++-- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 681f4d83d..f82431cc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# [5.2.0](https://github.com/socketio/engine.io/compare/5.1.1...5.2.0) (2021-08-29) + +No change on the server-side, this matches the client release. + + ## [5.1.1](https://github.com/socketio/engine.io/compare/5.1.0...5.1.1) (2021-05-16) diff --git a/package-lock.json b/package-lock.json index 266176146..dafe46078 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "5.1.1", + "version": "5.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -526,9 +526,9 @@ "dev": true }, "engine.io-client": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.1.tgz", - "integrity": "sha512-jPFpw2HLL0lhZ2KY0BpZhIJdleQcUO9W1xkIpo0h3d6s+5D6+EV/xgQw9qWOmymszv2WXef/6KUUehyxEKomlQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.2.0.tgz", + "integrity": "sha512-BcIBXGBkT7wKecwnfrSV79G2X5lSUSgeAGgoo60plXf8UsQEvCQww/KMwXSMhVjb98fFYNq20CC5eo8IOAPqsg==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", @@ -539,17 +539,24 @@ "parseqs": "0.0.6", "parseuri": "0.0.6", "ws": "~7.4.2", + "xmlhttprequest-ssl": "~2.0.0", "yeast": "0.1.2" }, "dependencies": { "engine.io-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", - "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.3.tgz", + "integrity": "sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4" } + }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true } } }, diff --git a/package.json b/package.json index 12e3e2a09..9aaca5c70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "5.1.1", + "version": "5.2.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io.js", "author": "Guillermo Rauch ", @@ -36,7 +36,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.3.0", - "engine.io-client": "5.1.1", + "engine.io-client": "5.2.0", "engine.io-client-v3": "npm:engine.io-client@3.5.0", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", From ebb36f298d9ac2c6a5f2582ac0101dc0aa5a88f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Aug 2021 09:08:48 +0200 Subject: [PATCH 17/58] chore(deps): bump xmlhttprequest-ssl from 1.5.5 to 2.0.0 (#626) Bumps [xmlhttprequest-ssl](https://github.com/mjwwit/node-XMLHttpRequest) from 1.5.5 to 2.0.0. - [Release notes](https://github.com/mjwwit/node-XMLHttpRequest/releases) - [Commits](https://github.com/mjwwit/node-XMLHttpRequest/compare/1.5.5...2.0.0) --- updated-dependencies: - dependency-name: xmlhttprequest-ssl dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index dafe46078..b0082b209 100644 --- a/package-lock.json +++ b/package-lock.json @@ -551,12 +551,6 @@ "requires": { "base64-arraybuffer": "0.1.4" } - }, - "xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", - "dev": true } } }, @@ -606,6 +600,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true } } }, @@ -1711,9 +1711,9 @@ "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" }, "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", "dev": true }, "yallist": { From 18a6eb89fbad618760c6bf81387ad421dfd3353b Mon Sep 17 00:00:00 2001 From: mingkaili Date: Thu, 9 Sep 2021 13:21:04 +0800 Subject: [PATCH 18/58] docs: remove the extra bracket in README (#627) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f5a9da44..163077f94 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Engine.IO: the realtime engine -[![Build Status](https://github.com/socketio/engine.io/workflows/CI/badge.svg?branch=master))](https://github.com/socketio/engine.io/actions) +[![Build Status](https://github.com/socketio/engine.io/workflows/CI/badge.svg?branch=master)](https://github.com/socketio/engine.io/actions) [![NPM version](https://badge.fury.io/js/engine.io.svg)](http://badge.fury.io/js/engine.io) `Engine.IO` is the implementation of transport-based From c0d6eaa1ba1291946dc8425d5f533d5f721862dd Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 8 Oct 2021 14:55:30 +0200 Subject: [PATCH 19/58] chore: migrate to TypeScript Related: https://github.com/socketio/engine.io/issues/510 --- .gitignore | 1 + lib/engine.io.js | 127 --- lib/engine.io.ts | 55 ++ lib/parser-v3/{index.js => index.ts} | 38 +- lib/parser-v3/{utf8.js => utf8.ts} | 2 +- lib/{server.js => server.ts} | 217 +++-- lib/{socket.js => socket.ts} | 72 +- lib/{transport.js => transport.ts} | 54 +- lib/transports/index.js | 31 - lib/transports/index.ts | 24 + .../{polling-jsonp.js => polling-jsonp.ts} | 12 +- lib/transports/{polling.js => polling.ts} | 35 +- lib/transports/{websocket.js => websocket.ts} | 15 +- package-lock.json | 835 +----------------- package.json | 25 +- test/common.js | 4 +- test/engine.io.js | 30 +- test/server.js | 24 +- tsconfig.json | 11 + 19 files changed, 482 insertions(+), 1130 deletions(-) delete mode 100644 lib/engine.io.js create mode 100644 lib/engine.io.ts rename lib/parser-v3/{index.js => index.ts} (89%) rename lib/parser-v3/{utf8.js => utf8.ts} (99%) rename lib/{server.js => server.ts} (77%) rename lib/{socket.js => socket.ts} (89%) rename lib/{transport.js => transport.ts} (62%) delete mode 100644 lib/transports/index.js create mode 100644 lib/transports/index.ts rename lib/transports/{polling-jsonp.js => polling-jsonp.ts} (88%) rename lib/transports/{polling.js => polling.ts} (92%) rename lib/transports/{websocket.js => websocket.ts} (89%) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 93f136199..fef838544 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules npm-debug.log +build/ diff --git a/lib/engine.io.js b/lib/engine.io.js deleted file mode 100644 index 43f7c371b..000000000 --- a/lib/engine.io.js +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Module dependencies. - */ - -const http = require("http"); -const Server = require("./server"); - -/** - * Invoking the library as a function delegates to attach if the first argument - * is an `http.Server`. - * - * If there are no arguments or the first argument is an options object, then - * a new Server instance is returned. - * - * @param {http.Server} server (if specified, will be attached to by the new Server instance) - * @param {Object} options - * @return {Server} engine server - * @api public - */ - -exports = module.exports = function() { - // backwards compatible use as `.attach` - // if first argument is an http server - if (arguments.length && arguments[0] instanceof http.Server) { - return attach.apply(this, arguments); - } - - // if first argument is not an http server, then just make a regular eio server - return new Server(...arguments); -}; - -/** - * Protocol revision number. - * - * @api public - */ - -exports.protocol = 1; - -/** - * Expose Server constructor. - * - * @api public - */ - -exports.Server = Server; - -/** - * Expose Socket constructor. - * - * @api public - */ - -exports.Socket = require("./socket"); - -/** - * Expose Transport constructor. - * - * @api public - */ - -exports.Transport = require("./transport"); - -/** - * Expose mutable list of available transports. - * - * @api public - */ - -exports.transports = require("./transports"); - -/** - * Exports parser. - * - * @api public - */ - -exports.parser = require("engine.io-parser"); - -/** - * Creates an http.Server exclusively used for WS upgrades. - * - * @param {Number} port - * @param {Function} callback - * @param {Object} options - * @return {Server} websocket.io server - * @api public - */ - -exports.listen = listen; - -function listen(port, options, fn) { - if ("function" === typeof options) { - fn = options; - options = {}; - } - - const server = http.createServer(function(req, res) { - res.writeHead(501); - res.end("Not Implemented"); - }); - - // create engine server - const engine = exports.attach(server, options); - engine.httpServer = server; - - server.listen(port, fn); - - return engine; -} - -/** - * Captures upgrade requests for a http.Server. - * - * @param {http.Server} server - * @param {Object} options - * @return {Server} engine server - * @api public - */ - -exports.attach = attach; - -function attach(server, options) { - const engine = new Server(options); - engine.attach(server, options); - return engine; -} diff --git a/lib/engine.io.ts b/lib/engine.io.ts new file mode 100644 index 000000000..71323962f --- /dev/null +++ b/lib/engine.io.ts @@ -0,0 +1,55 @@ +import { createServer } from "http"; +import { Server, AttachOptions, ServerOptions } from "./server"; +import transports from "./transports/index"; +import * as parser from "engine.io-parser"; + +export { Server, transports, listen, attach, parser }; +export { AttachOptions, ServerOptions } from "./server"; +export { Socket } from "./socket"; +export { Transport } from "./transport"; +export const protocol = parser.protocol; + +/** + * Creates an http.Server exclusively used for WS upgrades. + * + * @param {Number} port + * @param {Function} callback + * @param {Object} options + * @return {Server} websocket.io server + * @api public + */ + +function listen(port, options: AttachOptions & ServerOptions, fn) { + if ("function" === typeof options) { + fn = options; + options = {}; + } + + const server = createServer(function(req, res) { + res.writeHead(501); + res.end("Not Implemented"); + }); + + // create engine server + const engine = attach(server, options); + engine.httpServer = server; + + server.listen(port, fn); + + return engine; +} + +/** + * Captures upgrade requests for a http.Server. + * + * @param {http.Server} server + * @param {Object} options + * @return {Server} engine server + * @api public + */ + +function attach(server, options: AttachOptions & ServerOptions) { + const engine = new Server(options); + engine.attach(server, options); + return engine; +} diff --git a/lib/parser-v3/index.js b/lib/parser-v3/index.ts similarity index 89% rename from lib/parser-v3/index.js rename to lib/parser-v3/index.ts index bd5d56baa..6949f1875 100644 --- a/lib/parser-v3/index.js +++ b/lib/parser-v3/index.ts @@ -9,7 +9,7 @@ var utf8 = require('./utf8'); /** * Current protocol version. */ -exports.protocol = 3; +export const protocol = 3; const hasBinary = (packets) => { for (const packet of packets) { @@ -24,7 +24,7 @@ const hasBinary = (packets) => { * Packet types. */ -var packets = exports.packets = { +export const packets = { open: 0 // non-ws , close: 1 // non-ws , ping: 2 @@ -60,7 +60,7 @@ const EMPTY_BUFFER = Buffer.concat([]); * @api private */ -exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) { +export function encodePacket (packet, supportsBinary, utf8encode, callback) { if (typeof supportsBinary === 'function') { callback = supportsBinary; supportsBinary = null; @@ -94,7 +94,7 @@ exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) { function encodeBuffer(packet, supportsBinary, callback) { if (!supportsBinary) { - return exports.encodeBase64Packet(packet, callback); + return encodeBase64Packet(packet, callback); } var data = packet.data; @@ -110,7 +110,7 @@ function encodeBuffer(packet, supportsBinary, callback) { * @return {String} base64 encoded message */ -exports.encodeBase64Packet = function(packet, callback){ +export function encodeBase64Packet (packet, callback){ var data = Buffer.isBuffer(packet.data) ? packet.data : arrayBufferToBuffer(packet.data); var message = 'b' + packets[packet.type]; message += data.toString('base64'); @@ -124,7 +124,7 @@ exports.encodeBase64Packet = function(packet, callback){ * @api private */ -exports.decodePacket = function (data, binaryType, utf8decode) { +export function decodePacket (data, binaryType, utf8decode) { if (data === undefined) { return err; } @@ -137,7 +137,7 @@ exports.decodePacket = function (data, binaryType, utf8decode) { type = data.charAt(0); if (type === 'b') { - return exports.decodeBase64Packet(data.substr(1), binaryType); + return decodeBase64Packet(data.substr(1), binaryType); } if (utf8decode) { @@ -189,7 +189,7 @@ function tryDecode(data) { * @return {Object} with `type` and `data` (if any) */ -exports.decodeBase64Packet = function(msg, binaryType) { +export function decodeBase64Packet (msg, binaryType) { var type = packetslist[msg.charAt(0)]; var data = Buffer.from(msg.substr(1), 'base64'); if (binaryType === 'arraybuffer') { @@ -197,6 +197,7 @@ exports.decodeBase64Packet = function(msg, binaryType) { for (var i = 0; i < abv.length; i++){ abv[i] = data[i]; } + // @ts-ignore data = abv.buffer; } return { type: type, data: data }; @@ -218,14 +219,14 @@ exports.decodeBase64Packet = function(msg, binaryType) { * @api private */ -exports.encodePayload = function (packets, supportsBinary, callback) { +export function encodePayload (packets, supportsBinary, callback) { if (typeof supportsBinary === 'function') { callback = supportsBinary; supportsBinary = null; } if (supportsBinary && hasBinary(packets)) { - return exports.encodePayloadAsBinary(packets, callback); + return encodePayloadAsBinary(packets, callback); } if (!packets.length) { @@ -233,7 +234,7 @@ exports.encodePayload = function (packets, supportsBinary, callback) { } function encodeOne(packet, doneCallback) { - exports.encodePacket(packet, supportsBinary, false, function(message) { + encodePacket(packet, supportsBinary, false, function(message) { doneCallback(null, setLengthHeader(message)); }); } @@ -273,9 +274,9 @@ function map(ary, each, done) { * @api public */ -exports.decodePayload = function (data, binaryType, callback) { +export function decodePayload (data, binaryType, callback) { if (typeof data !== 'string') { - return exports.decodePayloadAsBinary(data, binaryType, callback); + return decodePayloadAsBinary(data, binaryType, callback); } if (typeof binaryType === 'function') { @@ -298,6 +299,7 @@ exports.decodePayload = function (data, binaryType, callback) { continue; } + // @ts-ignore if (length === '' || (length != (n = Number(length)))) { // parser error - ignoring payload return callback(err, 0, 1); @@ -311,7 +313,7 @@ exports.decodePayload = function (data, binaryType, callback) { } if (msg.length) { - packet = exports.decodePacket(msg, binaryType, false); + packet = decodePacket(msg, binaryType, false); if (err.type === packet.type && err.data === packet.data) { // parser error in individual packet - ignoring payload @@ -393,7 +395,7 @@ function arrayBufferToBuffer(data) { * @api private */ -exports.encodePayloadAsBinary = function (packets, callback) { +export function encodePayloadAsBinary (packets, callback) { if (!packets.length) { return callback(EMPTY_BUFFER); } @@ -430,7 +432,7 @@ function encodeOneBinaryPacket(p, doneCallback) { doneCallback(null, Buffer.concat([sizeBuffer, packet])); } - exports.encodePacket(p, true, true, onBinaryPacketEncode); + encodePacket(p, true, true, onBinaryPacketEncode); } @@ -444,7 +446,7 @@ function encodeOneBinaryPacket(p, doneCallback) { * @api public */ -exports.decodePayloadAsBinary = function (data, binaryType, callback) { +export function decodePayloadAsBinary (data, binaryType, callback) { if (typeof binaryType === 'function') { callback = binaryType; binaryType = null; @@ -478,6 +480,6 @@ exports.decodePayloadAsBinary = function (data, binaryType, callback) { var total = buffers.length; for (i = 0; i < total; i++) { var buffer = buffers[i]; - callback(exports.decodePacket(buffer, binaryType, true), i, total); + callback(decodePacket(buffer, binaryType, true), i, total); } }; diff --git a/lib/parser-v3/utf8.js b/lib/parser-v3/utf8.ts similarity index 99% rename from lib/parser-v3/utf8.js rename to lib/parser-v3/utf8.ts index b878740ff..9a9decbd3 100644 --- a/lib/parser-v3/utf8.js +++ b/lib/parser-v3/utf8.ts @@ -203,7 +203,7 @@ function utf8decode(byteString, opts) { return ucs2encode(codePoints); } -module.exports = { +export default { version: '2.1.2', encode: utf8encode, decode: utf8decode diff --git a/lib/server.js b/lib/server.ts similarity index 77% rename from lib/server.js rename to lib/server.ts index d3087ab8b..bd69d6ec0 100644 --- a/lib/server.js +++ b/lib/server.ts @@ -1,22 +1,135 @@ -const qs = require("querystring"); -const parse = require("url").parse; -const base64id = require("base64id"); -const transports = require("./transports"); -const EventEmitter = require("events").EventEmitter; -const Socket = require("./socket"); -const debug = require("debug")("engine"); -const cookieMod = require("cookie"); - -const DEFAULT_WS_ENGINE = require("ws").Server; - -class Server extends EventEmitter { +import * as qs from "querystring"; +import { parse } from "url"; +import * as base64id from "base64id"; +import transports from "./transports"; +import { EventEmitter } from "events"; +import { Socket } from "./socket"; +import debugModule from "debug"; +import { serialize } from "cookie"; +import { Server as DEFAULT_WS_ENGINE } from "ws"; +import { IncomingMessage, Server as HttpServer } from "http"; +import { CookieSerializeOptions } from "cookie"; +import { CorsOptions } from "cors"; + +const debug = debugModule("engine"); + +type Transport = "polling" | "websocket"; + +export interface AttachOptions { + /** + * name of the path to capture + * @default "/engine.io" + */ + path?: string; + /** + * destroy unhandled upgrade requests + * @default true + */ + destroyUpgrade?: boolean; + /** + * milliseconds after which unhandled requests are ended + * @default 1000 + */ + destroyUpgradeTimeout?: number; +} + +export interface ServerOptions { + /** + * how many ms without a pong packet to consider the connection closed + * @default 20000 + */ + pingTimeout?: number; + /** + * how many ms before sending a new ping packet + * @default 25000 + */ + pingInterval?: number; + /** + * how many ms before an uncompleted transport upgrade is cancelled + * @default 10000 + */ + upgradeTimeout?: number; + /** + * how many bytes or characters a message can be, before closing the session (to avoid DoS). + * @default 1e5 (100 KB) + */ + maxHttpBufferSize?: number; + /** + * A function that receives a given handshake or upgrade request as its first parameter, + * and can decide whether to continue or not. The second argument is a function that needs + * to be called with the decided information: fn(err, success), where success is a boolean + * value where false means that the request is rejected, and err is an error code. + */ + allowRequest?: ( + req: IncomingMessage, + fn: (err: string | null | undefined, success: boolean) => void + ) => void; + /** + * the low-level transports that are enabled + * @default ["polling", "websocket"] + */ + transports?: Transport[]; + /** + * whether to allow transport upgrades + * @default true + */ + allowUpgrades?: boolean; + /** + * parameters of the WebSocket permessage-deflate extension (see ws module api docs). Set to false to disable. + * @default false + */ + perMessageDeflate?: boolean | object; + /** + * parameters of the http compression for the polling transports (see zlib api docs). Set to false to disable. + * @default true + */ + httpCompression?: boolean | object; + /** + * what WebSocket server implementation to use. Specified module must + * conform to the ws interface (see ws module api docs). + * An alternative c++ addon is also available by installing eiows module. + * + * @default `require("ws").Server` + */ + wsEngine?: any; + /** + * an optional packet which will be concatenated to the handshake packet emitted by Engine.IO. + */ + initialPacket?: any; + /** + * configuration of the cookie that contains the client sid to send as part of handshake response headers. This cookie + * might be used for sticky-session. Defaults to not sending any cookie. + * @default false + */ + cookie?: (CookieSerializeOptions & { name: string }) | boolean; + /** + * the options that will be forwarded to the cors module + */ + cors?: CorsOptions; + /** + * whether to enable compatibility with Socket.IO v2 clients + * @default false + */ + allowEIO3?: boolean; +} + +export class Server extends EventEmitter { + public opts: ServerOptions; + public httpServer?: HttpServer; + + private clients: any; + private clientsCount: number; + private ws: any; + private corsMiddleware: Function; + private perMessageDeflate: any; + /** * Server constructor. * - * @param {Object} options + * @param {Object} opts - options * @api public */ - constructor(opts = {}) { + constructor(opts: ServerOptions = {}) { super(); this.clients = {}; @@ -45,6 +158,7 @@ class Server extends EventEmitter { { name: "io", path: "/", + // @ts-ignore httpOnly: opts.cookie.path !== false, sameSite: "lax" }, @@ -73,7 +187,7 @@ class Server extends EventEmitter { * * @api private */ - init() { + private init() { if (!~this.opts.transports.indexOf("websocket")) return; if (this.ws) this.ws.close(); @@ -111,7 +225,7 @@ class Server extends EventEmitter { * @return {Array} * @api public */ - upgrades(transport) { + public upgrades(transport) { if (!this.opts.allowUpgrades) return []; return transports[transport].upgradesTo || []; } @@ -123,7 +237,7 @@ class Server extends EventEmitter { * @return {Boolean} whether the request is valid * @api private */ - verify(req, upgrade, fn) { + private verify(req, upgrade, fn) { // transport check const transport = req._query.transport; if (!~this.opts.transports.indexOf(transport)) { @@ -189,7 +303,7 @@ class Server extends EventEmitter { * * @api private */ - prepare(req) { + private prepare(req) { // try to leverage pre-existing `req._query` (e.g: from connect) if (!req._query) { req._query = ~req.url.indexOf("?") ? qs.parse(parse(req.url).query) : {}; @@ -201,7 +315,7 @@ class Server extends EventEmitter { * * @api public */ - close() { + public close() { debug("closing all open clients"); for (let i in this.clients) { if (this.clients.hasOwnProperty(i)) { @@ -223,7 +337,7 @@ class Server extends EventEmitter { * @param {http.ServerResponse|http.OutgoingMessage} response * @api public */ - handleRequest(req, res) { + public handleRequest(req, res) { debug('handling "%s" http request "%s"', req.method, req.url); this.prepare(req); req.res = res; @@ -266,7 +380,7 @@ class Server extends EventEmitter { * @param {Object} request object * @api public */ - generateId(req) { + public generateId(req) { return base64id.generateId(); } @@ -279,7 +393,7 @@ class Server extends EventEmitter { * * @api private */ - async handshake(transportName, req, closeConnection) { + private async handshake(transportName, req, closeConnection) { const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default if (protocol === 3 && !this.opts.allowEIO3) { debug("unsupported protocol version"); @@ -352,7 +466,8 @@ class Server extends EventEmitter { if (isInitialRequest) { if (this.opts.cookie) { headers["Set-Cookie"] = [ - cookieMod.serialize(this.opts.cookie.name, id, this.opts.cookie) + // @ts-ignore + serialize(this.opts.cookie.name, id, this.opts.cookie) ]; } this.emit("initial_headers", headers, req); @@ -378,7 +493,7 @@ class Server extends EventEmitter { * * @api public */ - handleUpgrade(req, socket, upgradeHead) { + public handleUpgrade(req, socket, upgradeHead) { this.prepare(req); this.verify(req, true, (errorCode, errorContext) => { @@ -409,7 +524,7 @@ class Server extends EventEmitter { * @param {ws.Socket} websocket * @api private */ - onWebSocket(req, socket, websocket) { + private onWebSocket(req, socket, websocket) { websocket.on("error", onUpgradeError); if ( @@ -475,7 +590,7 @@ class Server extends EventEmitter { * @param {Object} options * @api public */ - attach(server, options = {}) { + public attach(server, options: AttachOptions = {}) { let path = (options.path || "/engine.io").replace(/\/$/, ""); const destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000; @@ -525,29 +640,29 @@ class Server extends EventEmitter { }); } } -} -/** - * Protocol errors mappings. - */ + /** + * Protocol errors mappings. + */ -Server.errors = { - UNKNOWN_TRANSPORT: 0, - UNKNOWN_SID: 1, - BAD_HANDSHAKE_METHOD: 2, - BAD_REQUEST: 3, - FORBIDDEN: 4, - UNSUPPORTED_PROTOCOL_VERSION: 5 -}; - -Server.errorMessages = { - 0: "Transport unknown", - 1: "Session ID unknown", - 2: "Bad handshake method", - 3: "Bad request", - 4: "Forbidden", - 5: "Unsupported protocol version" -}; + static errors = { + UNKNOWN_TRANSPORT: 0, + UNKNOWN_SID: 1, + BAD_HANDSHAKE_METHOD: 2, + BAD_REQUEST: 3, + FORBIDDEN: 4, + UNSUPPORTED_PROTOCOL_VERSION: 5 + }; + + static errorMessages = { + 0: "Transport unknown", + 1: "Session ID unknown", + 2: "Bad handshake method", + 3: "Bad request", + 4: "Forbidden", + 5: "Unsupported protocol version" + }; +} /** * Close the HTTP long-polling request @@ -585,7 +700,11 @@ function abortRequest(res, errorCode, errorContext) { * @api private */ -function abortUpgrade(socket, errorCode, errorContext = {}) { +function abortUpgrade( + socket, + errorCode, + errorContext: { message?: string } = {} +) { socket.on("error", () => { debug("ignoring error from closed connection"); }); @@ -606,8 +725,6 @@ function abortUpgrade(socket, errorCode, errorContext = {}) { socket.destroy(); } -module.exports = Server; - /* eslint-disable */ /** diff --git a/lib/socket.js b/lib/socket.ts similarity index 89% rename from lib/socket.js rename to lib/socket.ts index 733793416..f9293213c 100644 --- a/lib/socket.js +++ b/lib/socket.ts @@ -1,7 +1,33 @@ -const EventEmitter = require("events"); -const debug = require("debug")("engine:socket"); +import { EventEmitter } from "events"; +import debugModule from "debug"; +import { IncomingMessage } from "http"; +import { Transport } from "./transport"; +import { Server } from "./server"; + +const debug = debugModule("engine:socket"); + +export class Socket extends EventEmitter { + public readonly protocol: number; + public readonly request: IncomingMessage; + public readonly remoteAddress: string; + + public readyState: string; + public transport: Transport; + + private server: Server; + private upgrading: boolean; + private upgraded: boolean; + private writeBuffer: any[]; + private packetsFn: any[]; + private sentCallbackFn: any[]; + private cleanupFn: any[]; + private checkIntervalTimer; + private upgradeTimeoutTimer; + private pingTimeoutTimer; + private pingIntervalTimer; + + private readonly id: string; -class Socket extends EventEmitter { /** * Client class (abstract). * @@ -42,7 +68,7 @@ class Socket extends EventEmitter { * * @api private */ - onOpen() { + private onOpen() { this.readyState = "open"; // sends an `open` packet @@ -80,7 +106,7 @@ class Socket extends EventEmitter { * @param {Object} packet * @api private */ - onPacket(packet) { + private onPacket(packet) { if ("open" !== this.readyState) { return debug("packet received with closed socket"); } @@ -132,7 +158,7 @@ class Socket extends EventEmitter { * @param {Error} error object * @api private */ - onError(err) { + private onError(err) { debug("transport error"); this.onClose("transport error", err); } @@ -143,7 +169,7 @@ class Socket extends EventEmitter { * * @api private */ - schedulePing() { + private schedulePing() { clearTimeout(this.pingIntervalTimer); this.pingIntervalTimer = setTimeout(() => { debug( @@ -160,7 +186,7 @@ class Socket extends EventEmitter { * * @api private */ - resetPingTimeout(timeout) { + private resetPingTimeout(timeout) { clearTimeout(this.pingTimeoutTimer); this.pingTimeoutTimer = setTimeout(() => { if (this.readyState === "closed") return; @@ -174,7 +200,7 @@ class Socket extends EventEmitter { * @param {Transport} transport * @api private */ - setTransport(transport) { + private setTransport(transport) { const onError = this.onError.bind(this); const onPacket = this.onPacket.bind(this); const flush = this.flush.bind(this); @@ -202,7 +228,7 @@ class Socket extends EventEmitter { * @param {Transport} transport * @api private */ - maybeUpgrade(transport) { + private maybeUpgrade(transport) { debug( 'might upgrade socket transport from "%s" to "%s"', this.transport.name, @@ -296,7 +322,7 @@ class Socket extends EventEmitter { * * @api private */ - clearTransport() { + private clearTransport() { let cleanup; const toCleanUp = this.cleanupFn.length; @@ -322,7 +348,7 @@ class Socket extends EventEmitter { * Possible reasons: `ping timeout`, `client error`, `parse error`, * `transport error`, `server close`, `transport close` */ - onClose(reason, description) { + private onClose(reason: string, description?) { if ("closed" !== this.readyState) { this.readyState = "closed"; @@ -350,7 +376,7 @@ class Socket extends EventEmitter { * * @api private */ - setupSendCallback() { + private setupSendCallback() { // the message was sent successfully, execute the callback const onDrain = () => { if (this.sentCallbackFn.length > 0) { @@ -387,12 +413,12 @@ class Socket extends EventEmitter { * @return {Socket} for chaining * @api public */ - send(data, options, callback) { + public send(data, options, callback?) { this.sendPacket("message", data, options, callback); return this; } - write(data, options, callback) { + public write(data, options, callback?) { this.sendPacket("message", data, options, callback); return this; } @@ -405,7 +431,7 @@ class Socket extends EventEmitter { * @param {Object} options * @api private */ - sendPacket(type, data, options, callback) { + private sendPacket(type, data?, options?, callback?) { if ("function" === typeof options) { callback = options; options = null; @@ -417,7 +443,7 @@ class Socket extends EventEmitter { if ("closing" !== this.readyState && "closed" !== this.readyState) { debug('sending packet "%s" (%s)', type, data); - const packet = { + const packet: any = { type: type, options: options }; @@ -440,7 +466,7 @@ class Socket extends EventEmitter { * * @api private */ - flush() { + private flush() { if ( "closed" !== this.readyState && this.transport.writable && @@ -468,7 +494,7 @@ class Socket extends EventEmitter { * * @api private */ - getAvailableUpgrades() { + private getAvailableUpgrades() { const availableUpgrades = []; const allUpgrades = this.server.upgrades(this.transport.name); let i = 0; @@ -485,11 +511,11 @@ class Socket extends EventEmitter { /** * Closes the socket and underlying transport. * - * @param {Boolean} optional, discard + * @param {Boolean} discard - optional, discard the transport * @return {Socket} for chaining * @api public */ - close(discard) { + public close(discard?: boolean) { if ("open" !== this.readyState) return; this.readyState = "closing"; @@ -508,10 +534,8 @@ class Socket extends EventEmitter { * @param {Boolean} discard * @api private */ - closeTransport(discard) { + private closeTransport(discard) { if (discard) this.transport.discard(); this.transport.close(this.onClose.bind(this, "forced close")); } } - -module.exports = Socket; diff --git a/lib/transport.js b/lib/transport.ts similarity index 62% rename from lib/transport.js rename to lib/transport.ts index 0da3993cb..a82e1b846 100644 --- a/lib/transport.js +++ b/lib/transport.ts @@ -1,7 +1,10 @@ -const EventEmitter = require("events"); -const parser_v4 = require("engine.io-parser"); -const parser_v3 = require("./parser-v3/index"); -const debug = require("debug")("engine:transport"); +import { EventEmitter } from "events"; +import * as parser_v4 from "engine.io-parser"; +import * as parser_v3 from "./parser-v3/index"; +import debugModule from "debug"; +import { IncomingMessage } from "http"; + +const debug = debugModule("engine:transport"); /** * Noop function. @@ -11,7 +14,17 @@ const debug = require("debug")("engine:transport"); function noop() {} -class Transport extends EventEmitter { +export abstract class Transport extends EventEmitter { + public sid: string; + public writable: boolean; + public protocol: number; + + protected readyState: string; + protected discarded: boolean; + protected parser: any; + protected req: IncomingMessage & { cleanup: Function }; + protected supportsBinary: boolean; + /** * Transport constructor. * @@ -39,9 +52,9 @@ class Transport extends EventEmitter { * Called with an incoming HTTP request. * * @param {http.IncomingMessage} request - * @api private + * @api protected */ - onRequest(req) { + protected onRequest(req) { debug("setting request"); this.req = req; } @@ -51,7 +64,7 @@ class Transport extends EventEmitter { * * @api private */ - close(fn) { + close(fn?) { if ("closed" === this.readyState || "closing" === this.readyState) return; this.readyState = "closing"; @@ -63,12 +76,14 @@ class Transport extends EventEmitter { * * @param {String} message error * @param {Object} error description - * @api private + * @api protected */ - onError(msg, desc) { + protected onError(msg: string, desc?) { if (this.listeners("error").length) { const err = new Error(msg); + // @ts-ignore err.type = "TransportError"; + // @ts-ignore err.description = desc; this.emit("error", err); } else { @@ -80,9 +95,9 @@ class Transport extends EventEmitter { * Called with parsed out a packets from the data stream. * * @param {Object} packet - * @api private + * @api protected */ - onPacket(packet) { + protected onPacket(packet) { this.emit("packet", packet); } @@ -90,21 +105,24 @@ class Transport extends EventEmitter { * Called with the encoded packet data. * * @param {String} data - * @api private + * @api protected */ - onData(data) { + protected onData(data) { this.onPacket(this.parser.decodePacket(data)); } /** * Called upon transport close. * - * @api private + * @api protected */ - onClose() { + protected onClose() { this.readyState = "closed"; this.emit("close"); } -} -module.exports = Transport; + abstract get supportsFraming(); + abstract get name(); + abstract send(packets); + abstract doClose(fn?); +} diff --git a/lib/transports/index.js b/lib/transports/index.js deleted file mode 100644 index 98e0ab537..000000000 --- a/lib/transports/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const XHR = require("./polling"); -const JSONP = require("./polling-jsonp"); - -/** - * Export transports. - */ - -module.exports = exports = { - polling: polling, - websocket: require("./websocket") -}; - -/** - * Export upgrades map. - */ - -exports.polling.upgradesTo = ["websocket"]; - -/** - * Polling polymorphic constructor. - * - * @api private - */ - -function polling(req) { - if ("string" === typeof req._query.j) { - return new JSONP(req); - } else { - return new XHR(req); - } -} diff --git a/lib/transports/index.ts b/lib/transports/index.ts new file mode 100644 index 000000000..55065bd66 --- /dev/null +++ b/lib/transports/index.ts @@ -0,0 +1,24 @@ +import { Polling as XHR } from "./polling"; +import { JSONP } from "./polling-jsonp"; +import { WebSocket } from "./websocket"; + +export default { + polling: polling, + websocket: WebSocket +}; + +/** + * Polling polymorphic constructor. + * + * @api private + */ + +function polling(req) { + if ("string" === typeof req._query.j) { + return new JSONP(req); + } else { + return new XHR(req); + } +} + +polling.upgradesTo = ["websocket"]; diff --git a/lib/transports/polling-jsonp.js b/lib/transports/polling-jsonp.ts similarity index 88% rename from lib/transports/polling-jsonp.js rename to lib/transports/polling-jsonp.ts index bb07ce857..b40f935e9 100644 --- a/lib/transports/polling-jsonp.js +++ b/lib/transports/polling-jsonp.ts @@ -1,9 +1,13 @@ -const Polling = require("./polling"); -const qs = require("querystring"); +import { Polling } from "./polling"; +import * as qs from "querystring"; + const rDoubleSlashes = /\\\\n/g; const rSlashes = /(\\)?\\n/g; -class JSONP extends Polling { +export class JSONP extends Polling { + private readonly head: string; + private readonly foot: string; + /** * JSON-P polling transport. * @@ -54,5 +58,3 @@ class JSONP extends Polling { super.doWrite(data, options, callback); } } - -module.exports = JSONP; diff --git a/lib/transports/polling.js b/lib/transports/polling.ts similarity index 92% rename from lib/transports/polling.js rename to lib/transports/polling.ts index f0909d070..c96010f1d 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.ts @@ -1,14 +1,27 @@ -const Transport = require("../transport"); -const zlib = require("zlib"); -const accepts = require("accepts"); -const debug = require("debug")("engine:polling"); +import { Transport } from "../transport"; +import { createGzip, createDeflate } from "zlib"; +import * as accepts from "accepts"; +import debugModule from "debug"; +import { IncomingMessage, ServerResponse } from "http"; + +const debug = debugModule("engine:polling"); const compressionMethods = { - gzip: zlib.createGzip, - deflate: zlib.createDeflate + gzip: createGzip, + deflate: createDeflate }; -class Polling extends Transport { +export class Polling extends Transport { + public maxHttpBufferSize: number; + public httpCompression: any; + + private res: ServerResponse; + private dataReq: IncomingMessage; + private dataRes: ServerResponse; + private shouldClose: Function; + + private readonly closeTimeout: number; + /** * HTTP polling constructor. * @@ -18,8 +31,6 @@ class Polling extends Transport { super(req); this.closeTimeout = 30 * 1000; - this.maxHttpBufferSize = null; - this.httpCompression = null; } /** @@ -31,6 +42,10 @@ class Polling extends Transport { return "polling"; } + get supportsFraming() { + return false; + } + /** * Overrides onRequest. * @@ -381,5 +396,3 @@ class Polling extends Transport { return headers; } } - -module.exports = Polling; diff --git a/lib/transports/websocket.js b/lib/transports/websocket.ts similarity index 89% rename from lib/transports/websocket.js rename to lib/transports/websocket.ts index 1daf6caa9..55c286134 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.ts @@ -1,7 +1,12 @@ -const Transport = require("../transport"); -const debug = require("debug")("engine:ws"); +import { Transport } from "../transport"; +import debugModule from "debug"; + +const debug = debugModule("engine:ws"); + +export class WebSocket extends Transport { + protected perMessageDeflate: any; + private socket: any; -class WebSocket extends Transport { /** * WebSocket transport * @@ -71,7 +76,7 @@ class WebSocket extends Transport { } // always creates a new object since ws modifies it - const opts = {}; + const opts: { compress?: boolean } = {}; if (packet.options) { opts.compress = packet.options.compress; } @@ -111,5 +116,3 @@ class WebSocket extends Transport { fn && fn(); } } - -module.exports = WebSocket; diff --git a/package-lock.json b/package-lock.json index b0082b209..580d081d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -118,6 +118,21 @@ "to-fast-properties": "^2.0.0" } }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "@types/node": { + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==" + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -127,65 +142,12 @@ "negotiator": "0.6.2" } }, - "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, "after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", "dev": true }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -195,15 +157,6 @@ "color-convert": "^1.9.0" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", @@ -216,53 +169,6 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "babel-eslint": { "version": "8.2.6", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.6.tgz", @@ -322,27 +228,6 @@ "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", "dev": true }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -354,39 +239,6 @@ "supports-color": "^5.3.0" } }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -435,18 +287,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", @@ -473,17 +313,6 @@ "vary": "^1" } }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -492,12 +321,6 @@ "ms": "2.1.2" } }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -510,15 +333,6 @@ "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", "dev": true }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "eiows": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.3.2.tgz", @@ -610,81 +424,25 @@ } }, "engine.io-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.0.tgz", - "integrity": "sha512-dfV5CyDe0Z5I6L5gyZZXIuWhqtaIrKhHLTfsQpTvDkoQfPPuQe94wkUBCsBe2HOlbNojiDxQRi9RvMNNZRyarw==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.0.tgz", + "integrity": "sha512-wn6QavHEqXoM+cg+x8uUG7GhxLBCfKEKNEsCNc7V2ugj3gB3lK91l1MuZiy6xFB2V9D1eew0aWkmpiT/aBb/KA==", "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" + "base64-arraybuffer": "~1.0.1" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } + "base64-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", + "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==" } } }, - "eslint-config-prettier": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz", - "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==", - "dev": true, - "requires": { - "get-stdin": "^6.0.0" - } + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "eslint-scope": { "version": "3.7.1", @@ -702,39 +460,6 @@ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -776,66 +501,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" - } - }, "form-data": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", @@ -859,18 +524,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -891,27 +544,12 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "growl": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", "dev": true }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", @@ -947,27 +585,6 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", @@ -990,28 +607,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -1021,74 +616,24 @@ "loose-envify": "^1.0.0" } }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -1104,16 +649,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -1139,12 +674,6 @@ "mime-db": "1.44.0" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1154,21 +683,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "mocha": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", @@ -1253,18 +767,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -1284,35 +786,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "parseqs": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", @@ -1331,24 +804,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, "prettier": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", @@ -1361,18 +816,6 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "qs": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", @@ -1394,68 +837,15 @@ "util-deprecate": "~1.0.1" } }, - "regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true, - "requires": { - "rx-lite": "*" - } - }, "s": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/s/-/s-0.1.1.tgz", @@ -1468,70 +858,12 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -1541,29 +873,6 @@ "safe-buffer": "~5.1.0" } }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, "superagent": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", @@ -1602,41 +911,6 @@ "has-flag": "^3.0.0" } }, - "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", - "dev": true, - "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -1649,19 +923,10 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "typescript": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, "util-deprecate": { @@ -1675,36 +940,12 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "ws": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", @@ -1716,12 +957,6 @@ "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", "dev": true }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", diff --git a/package.json b/package.json index 9aaca5c70..9f2e2fa15 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "engine.io", "version": "5.2.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", - "main": "lib/engine.io.js", + "main": "./build/engine.io.js", + "types": "./build/engine.io.d.ts", "author": "Guillermo Rauch ", "homepage": "https://github.com/socketio/engine.io", "contributors": [ @@ -25,12 +26,15 @@ ], "license": "MIT", "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~4.0.0", + "engine.io-parser": "~5.0.0", "ws": "~7.4.2" }, "devDependencies": { @@ -38,26 +42,27 @@ "eiows": "^3.3.0", "engine.io-client": "5.2.0", "engine.io-client-v3": "npm:engine.io-client@3.5.0", - "eslint": "^4.19.1", - "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", "mocha": "^4.0.1", "prettier": "^1.19.1", + "rimraf": "^3.0.2", "s": "0.1.1", - "superagent": "^3.8.1" + "superagent": "^3.8.1", + "typescript": "^4.4.3" }, "scripts": { - "lint": "eslint lib/ test/ *.js", - "test": "npm run lint && npm run format:check && mocha && EIO_CLIENT=3 mocha && EIO_WS_ENGINE=eiows mocha", - "format:check": "prettier --check 'lib/**/*.js' 'test/**/*.js'", - "format:fix": "prettier --write 'lib/**/*.js' 'test/**/*.js'" + "compile": "rimraf ./build && tsc", + "test": "npm run compile && npm run format:check && mocha --bail && EIO_CLIENT=3 mocha && EIO_WS_ENGINE=eiows mocha", + "format:check": "prettier --check 'lib/**/*.ts' 'test/**/*.js'", + "format:fix": "prettier --write 'lib/**/*.ts' 'test/**/*.js'", + "prepack": "npm run compile" }, "repository": { "type": "git", "url": "git@github.com:socketio/engine.io.git" }, "files": [ - "lib/" + "build/" ], "engines": { "node": ">=10.0.0" diff --git a/test/common.js b/test/common.js index 1e747e412..8cea5afc8 100644 --- a/test/common.js +++ b/test/common.js @@ -1,4 +1,4 @@ -const eio = require(".."); +const { listen } = require(".."); const eioc = process.env.EIO_CLIENT === "3" ? require("engine.io-client-v3") @@ -20,7 +20,7 @@ exports.listen = (opts, fn) => { opts.wsEngine = require(process.env.EIO_WS_ENGINE).Server; } - const e = eio.listen(0, opts, () => { + const e = listen(0, opts, () => { fn(e.httpServer.address().port); }); diff --git a/test/engine.io.js b/test/engine.io.js index 23f7034e6..5b76c9819 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -1,5 +1,5 @@ const net = require("net"); -const eio = require(".."); +const { Server, protocol, attach } = require(".."); const listen = require("./common").listen; const expect = require("expect.js"); const request = require("superagent"); @@ -11,7 +11,7 @@ const http = require("http"); describe("engine", () => { it("should expose protocol number", () => { - expect(eio.protocol).to.be.a("number"); + expect(protocol).to.be.a("number"); }); it("should be the same version as client", () => { @@ -21,13 +21,13 @@ describe("engine", () => { describe("engine()", () => { it("should create a Server when require called with no arguments", () => { - const engine = eio(); - expect(engine).to.be.an(eio.Server); + const engine = new Server(); + expect(engine).to.be.an(Server); expect(engine.ws).to.be.ok(); }); it("should pass options correctly to the Server", () => { - const engine = eio({ cors: true }); + const engine = new Server({ cors: true }); expect(engine.opts).to.have.property("cors", true); }); }); @@ -47,21 +47,21 @@ describe("engine", () => { describe("attach()", () => { it("should work from require()", () => { const server = http.createServer(); - const engine = eio(server); + const engine = new Server(server); - expect(engine).to.be.an(eio.Server); + expect(engine).to.be.an(Server); }); it("should return an engine.Server", () => { const server = http.createServer(); - const engine = eio.attach(server); + const engine = attach(server); - expect(engine).to.be.an(eio.Server); + expect(engine).to.be.an(Server); }); it("should attach engine to an http server", done => { const server = http.createServer(); - eio.attach(server); + attach(server); server.listen(() => { const uri = "http://localhost:%d/engine.io/default/".s( @@ -80,7 +80,7 @@ describe("engine", () => { it("should destroy upgrades not handled by engine", done => { const server = http.createServer(); - eio.attach(server, { destroyUpgradeTimeout: 50 }); + attach(server, { destroyUpgradeTimeout: 50 }); server.listen(() => { const client = net.createConnection(server.address().port); @@ -108,7 +108,7 @@ describe("engine", () => { it("should not destroy unhandled upgrades with destroyUpgrade:false", done => { const server = http.createServer(); - eio.attach(server, { destroyUpgrade: false, destroyUpgradeTimeout: 50 }); + attach(server, { destroyUpgrade: false, destroyUpgradeTimeout: 50 }); server.listen(() => { const client = net.createConnection(server.address().port); @@ -140,7 +140,7 @@ describe("engine", () => { it("should destroy unhandled upgrades with after a timeout", done => { const server = http.createServer(); - eio.attach(server, { destroyUpgradeTimeout: 200 }); + attach(server, { destroyUpgradeTimeout: 200 }); server.listen(() => { const client = net.createConnection(server.address().port); @@ -174,7 +174,7 @@ describe("engine", () => { it("should not destroy handled upgrades with after a timeout", done => { const server = http.createServer(); - eio.attach(server, { destroyUpgradeTimeout: 100 }); + attach(server, { destroyUpgradeTimeout: 100 }); // write to the socket to keep engine.io from closing it by writing before the timeout server.on("upgrade", (req, socket) => { @@ -226,7 +226,7 @@ describe("engine", () => { listeners++; }); - eio.attach(server); + attach(server); server.listen(() => { const port = server.address().port; diff --git a/test/server.js b/test/server.js index b8a60b55c..3b72c564b 100644 --- a/test/server.js +++ b/test/server.js @@ -6,7 +6,7 @@ const fs = require("fs"); const path = require("path"); const exec = require("child_process").exec; const zlib = require("zlib"); -const eio = require(".."); +const { Server, Socket, attach } = require(".."); const { eioc, listen, createPartialDone } = require("./common"); const expect = require("expect.js"); const request = require("superagent"); @@ -163,8 +163,8 @@ describe("server", () => { it("should send the io cookie", done => { listen({ cookie: true }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) - .query({ transport: "polling", b64: 1 }) + .get("http://localhost:%d/engine.io/".s(port)) + .query({ transport: "polling", EIO: 4 }) .end((err, res) => { expect(err).to.be(null); // hack-obtain sid @@ -428,7 +428,7 @@ describe("server", () => { const engine = listen({ allowUpgrades: false }, port => { eioc("ws://localhost:%d".s(port)); engine.on("connection", socket => { - expect(socket).to.be.an(eio.Socket); + expect(socket).to.be.an(Socket); done(); }); }); @@ -606,7 +606,7 @@ describe("server", () => { // we can't send an invalid header through request.get // so add an invalid char here engine.prepare = function(req) { - eio.Server.prototype.prepare.call(engine, req); + Server.prototype.prepare.call(engine, req); req.headers.origin += "\n"; }; @@ -662,7 +662,7 @@ describe("server", () => { const partialDone = createPartialDone(done, 2); const httpServer = http.createServer(); - const engine = eio({ allowEIO3: false }); + const engine = new Server({ allowEIO3: false }); engine.attach(httpServer); httpServer.listen(() => { const port = httpServer.address().port; @@ -2061,7 +2061,7 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ + const engine = new Server({ transports: ["polling"], allowUpgrades: false, allowEIO3: true @@ -2103,7 +2103,7 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ + const engine = new Server({ transports: ["polling"], allowUpgrades: false, allowEIO3: true @@ -2147,7 +2147,7 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ + const engine = new Server({ transports: ["websocket"], allowUpgrades: false, allowEIO3: true @@ -2190,7 +2190,7 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ + const engine = new Server({ transports: ["polling"], allowUpgrades: false, allowEIO3: true @@ -2233,7 +2233,7 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ + const engine = new Server({ transports: ["websocket"], allowUpgrades: false, allowEIO3: true @@ -2856,7 +2856,7 @@ describe("server", () => { }); // attach another engine to make sure it doesn't break upgrades - eio.attach(engine.httpServer, { path: "/foo" }); + attach(engine.httpServer, { path: "/foo" }); }); }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..72ed6e2d5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "outDir": "build/", + "target": "es2018", // Node.js 10 (https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping) + "module": "commonjs", + "declaration": true + }, + "include": [ + "./lib/**/*" + ] +} From 64d57545116c7a7d9eaf0a823d866946d6fedf4a Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 8 Oct 2021 15:05:51 +0200 Subject: [PATCH 20/58] chore: bump ws Release notes: https://github.com/websockets/ws/releases/tag/8.0.0 --- lib/transports/websocket.ts | 17 +++++------------ package-lock.json | 18 +++++++++++++++--- package.json | 2 +- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/transports/websocket.ts b/lib/transports/websocket.ts index 55c286134..4e868707e 100644 --- a/lib/transports/websocket.ts +++ b/lib/transports/websocket.ts @@ -16,7 +16,11 @@ export class WebSocket extends Transport { constructor(req) { super(req); this.socket = req.websocket; - this.socket.on("message", this.onData.bind(this)); + this.socket.on("message", (data, isBinary) => { + const message = isBinary ? data : data.toString(); + debug('received "%s"', message); + super.onData(message); + }); this.socket.once("close", this.onClose.bind(this)); this.socket.on("error", this.onError.bind(this)); this.writable = true; @@ -50,17 +54,6 @@ export class WebSocket extends Transport { return true; } - /** - * Processes the incoming data. - * - * @param {String} encoded packet - * @api private - */ - onData(data) { - debug('received "%s"', data); - super.onData(data); - } - /** * Writes a packet payload. * diff --git a/package-lock.json b/package-lock.json index 580d081d3..6c03991dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -365,6 +365,12 @@ "requires": { "base64-arraybuffer": "0.1.4" } + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true } } }, @@ -415,6 +421,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true + }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", @@ -947,9 +959,9 @@ "dev": true }, "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" }, "xmlhttprequest-ssl": { "version": "2.0.0", diff --git a/package.json b/package.json index 9f2e2fa15..72aff9994 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.0.0", - "ws": "~7.4.2" + "ws": "~8.2.3" }, "devDependencies": { "babel-eslint": "^8.0.2", From 401f4b60693fb6702c942692ce42e5bb701d81d7 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 8 Oct 2021 15:23:57 +0200 Subject: [PATCH 21/58] chore: add an ES module wrapper --- package.json | 8 +++++++- wrapper.mjs | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 wrapper.mjs diff --git a/package.json b/package.json index 72aff9994..0b0d914db 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,13 @@ "name": "engine.io", "version": "5.2.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", + "type": "commonjs", "main": "./build/engine.io.js", "types": "./build/engine.io.d.ts", + "exports": { + "import": "./wrapper.mjs", + "require": "./build/engine.io.js" + }, "author": "Guillermo Rauch ", "homepage": "https://github.com/socketio/engine.io", "contributors": [ @@ -62,7 +67,8 @@ "url": "git@github.com:socketio/engine.io.git" }, "files": [ - "build/" + "build/", + "wrapper.mjs" ], "engines": { "node": ">=10.0.0" diff --git a/wrapper.mjs b/wrapper.mjs new file mode 100644 index 000000000..6b0005b7c --- /dev/null +++ b/wrapper.mjs @@ -0,0 +1,3 @@ +import lib from "./build/engine.io.js"; + +export const { Server, Socket, Transport, transports, listen, attach, parser, protocol } = lib; From fe5d97fc3d7a26d34bce786a97962fae3d7ce17f Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 8 Oct 2021 16:12:32 +0200 Subject: [PATCH 22/58] chore(release): 6.0.0 Diff: https://github.com/socketio/engine.io/compare/5.2.0...6.0.0 --- CHANGELOG.md | 29 +++ package-lock.json | 38 ++-- package.json | 4 +- test/common.js | 4 +- test/engine.io.js | 2 +- test/fixtures/server-close-upgraded.js | 5 +- test/fixtures/server-close-upgrading.js | 5 +- test/fixtures/server-close.js | 5 +- test/jsonp.js | 248 ------------------------ test/server.js | 220 +++++++++++---------- 10 files changed, 168 insertions(+), 392 deletions(-) delete mode 100644 test/jsonp.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f82431cc5..876651fc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# [6.0.0](https://github.com/socketio/engine.io/compare/5.2.0...6.0.0) (2021-10-08) + +The codebase was migrated to TypeScript ([c0d6eaa](https://github.com/socketio/engine.io/commit/c0d6eaa1ba1291946dc8425d5f533d5f721862dd)) + +An ES module wrapper was also added ([401f4b6](https://github.com/socketio/engine.io/commit/401f4b60693fb6702c942692ce42e5bb701d81d7)). + +Please note that the communication protocol was not updated, so a v5 client will be able to reach a v6 server (and vice-versa). + +Reference: https://github.com/socketio/engine.io-protocol + +### BREAKING CHANGES + +- the default export was removed, so the following code won't work anymore: + +```js +const eioServer = require("engine.io")(httpServer); +``` + +Please use this instead: + +```js +const { Server } = require("engine.io"); +const eioServer = new Server(httpServer); +``` + +### Dependencies + +`ws` version: `~8.2.3` (bumped from `~7.4.2`) + # [5.2.0](https://github.com/socketio/engine.io/compare/5.1.1...5.2.0) (2021-08-29) No change on the server-side, this matches the client release. diff --git a/package-lock.json b/package-lock.json index 6c03991dc..e808f386b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "5.2.0", + "version": "6.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -118,6 +118,12 @@ "to-fast-properties": "^2.0.0" } }, + "@socket.io/component-emitter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-2.0.0.tgz", + "integrity": "sha512-tfCc1aFpZQtnVXQhZDlwefCmT03U75O/NNd65X37U20r6vfERhwRBcZYANnFt0/GEU/Acb3Z1ZVeK+qbV32VJw==", + "dev": true + }, "@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -340,38 +346,20 @@ "dev": true }, "engine.io-client": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.2.0.tgz", - "integrity": "sha512-BcIBXGBkT7wKecwnfrSV79G2X5lSUSgeAGgoo60plXf8UsQEvCQww/KMwXSMhVjb98fFYNq20CC5eo8IOAPqsg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.0.0.tgz", + "integrity": "sha512-74Nr22GETU/hD3t/WZX2exOlqX1HdxL3mqYsxd3dvgzK6jIJ+IcVvJRRUzH96P4ZEmjJV9uHiQykP1KyRZ0yDw==", "dev": true, "requires": { - "base64-arraybuffer": "0.1.4", - "component-emitter": "~1.3.0", + "@socket.io/component-emitter": "~2.0.0", "debug": "~4.3.1", - "engine.io-parser": "~4.0.1", + "engine.io-parser": "~5.0.0", "has-cors": "1.1.0", "parseqs": "0.0.6", "parseuri": "0.0.6", - "ws": "~7.4.2", + "ws": "~8.2.3", "xmlhttprequest-ssl": "~2.0.0", "yeast": "0.1.2" - }, - "dependencies": { - "engine.io-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.3.tgz", - "integrity": "sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==", - "dev": true, - "requires": { - "base64-arraybuffer": "0.1.4" - } - }, - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true - } } }, "engine.io-client-v3": { diff --git a/package.json b/package.json index 0b0d914db..56bbb2fe7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "5.2.0", + "version": "6.0.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "type": "commonjs", "main": "./build/engine.io.js", @@ -45,7 +45,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.3.0", - "engine.io-client": "5.2.0", + "engine.io-client": "6.0.0", "engine.io-client-v3": "npm:engine.io-client@3.5.0", "expect.js": "^0.3.1", "mocha": "^4.0.1", diff --git a/test/common.js b/test/common.js index 8cea5afc8..014681af3 100644 --- a/test/common.js +++ b/test/common.js @@ -1,5 +1,5 @@ const { listen } = require(".."); -const eioc = +const { Socket } = process.env.EIO_CLIENT === "3" ? require("engine.io-client-v3") : require("engine.io-client"); @@ -27,7 +27,7 @@ exports.listen = (opts, fn) => { return e; }; -exports.eioc = eioc; +exports.ClientSocket = Socket; /** * Sprintf util. diff --git a/test/engine.io.js b/test/engine.io.js index 5b76c9819..4c74b7d20 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -14,7 +14,7 @@ describe("engine", () => { expect(protocol).to.be.a("number"); }); - it("should be the same version as client", () => { + it.skip("should be the same version as client", () => { const version = require("../package").version; expect(version).to.be(require("engine.io-client/package").version); }); diff --git a/test/fixtures/server-close-upgraded.js b/test/fixtures/server-close-upgraded.js index 46d8983e1..774a21edb 100644 --- a/test/fixtures/server-close-upgraded.js +++ b/test/fixtures/server-close-upgraded.js @@ -1,8 +1,7 @@ -const eioc = require("../common").eioc; -const listen = require("../common").listen; +const { ClientSocket, listen } = require("../common"); const engine = listen(port => { - const socket = new eioc.Socket("ws://localhost:" + port); + const socket = new ClientSocket("ws://localhost:" + port); socket.on("upgrade", () => { engine.httpServer.close(); engine.close(); diff --git a/test/fixtures/server-close-upgrading.js b/test/fixtures/server-close-upgrading.js index 7938658e1..dcf2861d6 100644 --- a/test/fixtures/server-close-upgrading.js +++ b/test/fixtures/server-close-upgrading.js @@ -1,8 +1,7 @@ -const eioc = require("../common").eioc; -const listen = require("../common").listen; +const { ClientSocket, listen } = require("../common"); const engine = listen(port => { - const socket = new eioc.Socket("ws://localhost:" + port); + const socket = new ClientSocket("ws://localhost:" + port); socket.on("upgrading", () => { engine.httpServer.close(); engine.close(); diff --git a/test/fixtures/server-close.js b/test/fixtures/server-close.js index 532ee92c3..d46b81d56 100644 --- a/test/fixtures/server-close.js +++ b/test/fixtures/server-close.js @@ -1,8 +1,7 @@ -const eioc = require("../common").eioc; -const listen = require("../common").listen; +const { ClientSocket, listen } = require("../common"); const engine = listen(port => { - const socket = new eioc.Socket("ws://localhost:" + port); + const socket = new ClientSocket("ws://localhost:" + port); socket.on("open", () => { engine.httpServer.close(); engine.close(); diff --git a/test/jsonp.js b/test/jsonp.js deleted file mode 100644 index d1edf4284..000000000 --- a/test/jsonp.js +++ /dev/null @@ -1,248 +0,0 @@ -const eioc = require("./common").eioc; -const listen = require("./common").listen; -const expect = require("expect.js"); -const request = require("superagent"); - -describe("JSONP", () => { - before(() => { - // we have to override the browser's functionality for JSONP - document = { - // eslint-disable-line no-global-assign - body: { - appendChild: () => {}, - removeChild: () => {} - } - }; - - document.createElement = function(name) { - const self = this; - - if ("script" === name) { - const script = {}; - - script.__defineGetter__("parentNode", () => document.body); - - script.__defineSetter__("src", uri => { - request.get(uri).end((err, res) => { - expect(err).to.be(null); - eval(res.text); // eslint-disable-line no-eval - }); - }); - return script; - } else if ("form" === name) { - const form = { - style: {}, - action: "", - parentNode: { - removeChild: () => {} - }, - removeChild: () => {}, - setAttribute: () => {}, - appendChild: () => {}, - submit: function() { - request - .post(this.action) - .type("form") - .send({ d: self.areaValue }) - .end(() => {}); - } - }; - return form; - } else if ("textarea" === name) { - const textarea = {}; - - // a hack to be able to access the area data when form is sent - textarea.__defineSetter__("value", data => { - self.areaValue = data; - }); - return textarea; - } else if (~name.indexOf("iframe")) { - const iframe = {}; - setTimeout(() => { - if (iframe.onload) iframe.onload(); - }, 0); - - return iframe; - } else { - return {}; - } - }; - - document.getElementsByTagName = name => [ - { - parentNode: { - insertBefore: () => {} - } - } - ]; - }); - - after(() => { - delete document.getElementsByTagName; - delete document.createElement; - delete global.document; - }); - - describe("handshake", () => { - it("should open with polling JSONP when requested", done => { - const engine = listen( - { allowUpgrades: false, transports: ["polling"] }, - port => { - eioc("ws://localhost:" + port, { - transports: ["polling"], - forceJSONP: true, - upgrade: false - }); - engine.on("connection", socket => { - expect(socket.transport.name).to.be("polling"); - expect(socket.transport.head).to.be("___eio[0]("); - done(); - }); - } - ); - }); - }); - - describe("messages", () => { - let engine, port, socket; - - beforeEach(done => { - engine = listen({ allowUpgrades: false, transports: ["polling"] }, p => { - port = p; - - socket = new eioc.Socket("ws://localhost:" + port, { - transports: ["polling"], - forceJSONP: true, - upgrade: false - }); - - done(); - }); - }); - - it("should arrive from client to server and back (pollingJSONP)", done => { - engine.on("connection", conn => { - conn.on("message", msg => { - conn.send("a"); - }); - }); - - socket.on("open", () => { - socket.send("a"); - socket.on("message", msg => { - expect(socket.transport.query.j).to.not.be(undefined); - expect(msg).to.be("a"); - done(); - }); - }); - }); - - it("should not fail JSON.parse for stringified messages", done => { - engine.on("connection", conn => { - conn.on("message", message => { - expect(JSON.parse(message)).to.be.eql({ test: "a\r\nb\n\n\n\nc" }); - done(); - }); - }); - socket.on("open", () => { - socket.send(JSON.stringify({ test: "a\r\nb\n\n\n\nc" })); - }); - }); - - it("should parse newlines in message correctly", done => { - engine.on("connection", conn => { - conn.on("message", message => { - expect(message).to.be.equal("a\r\nb\n\n\n\nc"); - done(); - }); - }); - socket.on("open", () => { - socket.send("a\r\nb\n\n\n\nc"); - }); - }); - - it("should arrive from server to client and back with binary data (pollingJSONP)", done => { - const binaryData = Buffer.allocUnsafe(5); - for (var i = 0; i < 5; i++) binaryData[i] = i; - engine.on("connection", conn => { - conn.on("message", msg => { - conn.send(msg); - }); - }); - - socket.on("open", () => { - socket.send(binaryData); - socket.on("message", msg => { - for (let i = 0; i < msg.length; i++) expect(msg[i]).to.be(i); - done(); - }); - }); - }); - }); - - describe("close", () => { - it("should trigger when server closes a client", done => { - const engine = listen( - { allowUpgrades: false, transports: ["polling"] }, - port => { - const socket = new eioc.Socket("ws://localhost:" + port, { - transports: ["polling"], - forceJSONP: true, - upgrade: false - }); - let total = 2; - - engine.on("connection", conn => { - conn.on("close", reason => { - expect(reason).to.be("forced close"); - --total || done(); - }); - setTimeout(() => { - conn.close(); - }, 10); - }); - - socket.on("open", () => { - socket.on("close", reason => { - expect(reason).to.be("transport close"); - --total || done(); - }); - }); - } - ); - }); - - it("should trigger when client closes", done => { - const engine = listen( - { allowUpgrades: false, transports: ["polling"] }, - port => { - const socket = new eioc.Socket("ws://localhost:" + port, { - transports: ["polling"], - forceJSONP: true, - upgrade: false - }); - let total = 2; - - engine.on("connection", conn => { - conn.on("close", reason => { - expect(reason).to.be("transport close"); - --total || done(); - }); - }); - - socket.on("open", () => { - socket.send("a"); - socket.on("close", reason => { - expect(reason).to.be("forced close"); - --total || done(); - }); - - setTimeout(() => { - socket.close(); - }, 10); - }); - } - ); - }); - }); -}); diff --git a/test/server.js b/test/server.js index 3b72c564b..60e00536c 100644 --- a/test/server.js +++ b/test/server.js @@ -7,7 +7,7 @@ const path = require("path"); const exec = require("child_process").exec; const zlib = require("zlib"); const { Server, Socket, attach } = require(".."); -const { eioc, listen, createPartialDone } = require("./common"); +const { ClientSocket, listen, createPartialDone } = require("./common"); const expect = require("expect.js"); const request = require("superagent"); const cookieMod = require("cookie"); @@ -148,7 +148,7 @@ describe("server", () => { } }, port => { - const client = eioc("ws://localhost:%d".s(port), { + const client = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); client.on("error", () => { @@ -307,7 +307,7 @@ describe("server", () => { expect(Object.keys(engine.clients)).to.have.length(0); expect(engine.clientsCount).to.be(0); - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { expect(Object.keys(engine.clients)).to.have.length(1); expect(engine.clientsCount).to.be(1); @@ -325,7 +325,7 @@ describe("server", () => { engine.generateId = req => customId; - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.once("open", () => { expect(Object.keys(engine.clients)).to.have.length(1); expect(engine.clientsCount).to.be(1); @@ -342,7 +342,7 @@ describe("server", () => { engine.generateId = () => Promise.resolve(customId); - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.once("open", () => { expect(socket.id).to.be(customId); expect(engine.clients[customId].id).to.be(customId); @@ -367,7 +367,7 @@ describe("server", () => { partialDone(); }); - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("error", () => { partialDone(); }); @@ -393,7 +393,7 @@ describe("server", () => { partialDone(); }); - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); socket.on("error", () => { @@ -404,7 +404,7 @@ describe("server", () => { it("should exchange handshake data", done => { listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("handshake", obj => { expect(obj.sid).to.be.a("string"); expect(obj.pingTimeout).to.be.a("number"); @@ -416,7 +416,7 @@ describe("server", () => { it("should allow custom ping timeouts", done => { listen({ allowUpgrades: false, pingTimeout: 123 }, port => { - const socket = new eioc.Socket("http://localhost:%d".s(port)); + const socket = new ClientSocket("http://localhost:%d".s(port)); socket.on("handshake", obj => { expect(obj.pingTimeout).to.be(123); done(); @@ -426,7 +426,7 @@ describe("server", () => { it("should trigger a connection event with a Socket", done => { const engine = listen({ allowUpgrades: false }, port => { - eioc("ws://localhost:%d".s(port)); + new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", socket => { expect(socket).to.be.an(Socket); done(); @@ -436,7 +436,7 @@ describe("server", () => { it("should open with polling by default", done => { const engine = listen({ allowUpgrades: false }, port => { - eioc("ws://localhost:%d".s(port)); + new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", socket => { expect(socket.transport.name).to.be("polling"); done(); @@ -446,7 +446,9 @@ describe("server", () => { it("should be able to open with ws directly", done => { const engine = listen({ transports: ["websocket"] }, port => { - eioc("ws://localhost:%d".s(port), { transports: ["websocket"] }); + new ClientSocket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); engine.on("connection", socket => { expect(socket.transport.name).to.be("websocket"); done(); @@ -456,7 +458,7 @@ describe("server", () => { it("should not suggest any upgrades for websocket", done => { listen({ transports: ["websocket"] }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); socket.on("handshake", obj => { @@ -468,7 +470,7 @@ describe("server", () => { it("should not suggest upgrades when none are availble", done => { listen({ transports: ["polling"] }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + const socket = new ClientSocket("ws://localhost:%d".s(port), {}); socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(0); done(); @@ -478,7 +480,7 @@ describe("server", () => { it("should only suggest available upgrades", done => { listen({ transports: ["polling"] }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + const socket = new ClientSocket("ws://localhost:%d".s(port), {}); socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(0); done(); @@ -488,7 +490,7 @@ describe("server", () => { it("should suggest all upgrades when no transports are disabled", done => { listen({}, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + const socket = new ClientSocket("ws://localhost:%d".s(port), {}); socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(1); expect(obj.upgrades).to.have.contain("websocket"); @@ -517,7 +519,7 @@ describe("server", () => { partialDone(); }); - var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { request .get("http://localhost:%d/engine.io/".s(port)) @@ -539,7 +541,7 @@ describe("server", () => { it("should allow arbitrary data through query string", done => { const engine = listen({ allowUpgrades: false }, port => { - eioc("ws://localhost:%d".s(port), { query: { a: "b" } }); + new ClientSocket("ws://localhost:%d".s(port), { query: { a: "b" } }); engine.on("connection", conn => { expect(conn.request._query).to.have.keys("transport", "a"); expect(conn.request._query.a).to.be("b"); @@ -550,7 +552,7 @@ describe("server", () => { it("should allow data through query string in uri", done => { const engine = listen({ allowUpgrades: false }, port => { - eioc("ws://localhost:%d?a=b&c=d".s(port)); + new ClientSocket("ws://localhost:%d?a=b&c=d".s(port)); engine.on("connection", conn => { expect(conn.request._query.EIO).to.be.a("string"); expect(conn.request._query.a).to.be("b"); @@ -692,7 +694,7 @@ describe("server", () => { it("should send a packet along with the handshake", done => { listen({ initialPacket: "faster!" }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { socket.on("message", msg => { expect(msg).to.be("faster!"); @@ -707,7 +709,7 @@ describe("server", () => { it("should be able to access non-empty writeBuffer at closing (server)", done => { const opts = { allowUpgrades: false }; const engine = listen(opts, port => { - eioc("http://localhost:%d".s(port)); + new ClientSocket("http://localhost:%d".s(port)); engine.on("connection", conn => { conn.on("close", reason => { expect(conn.writeBuffer.length).to.be(1); @@ -725,7 +727,7 @@ describe("server", () => { it("should be able to access non-empty writeBuffer at closing (client)", done => { const opts = { allowUpgrades: false }; listen(opts, port => { - const socket = new eioc.Socket("http://localhost:%d".s(port)); + const socket = new ClientSocket("http://localhost:%d".s(port)); socket.on("open", () => { socket.on("close", reason => { expect(socket.writeBuffer.length).to.be(1); @@ -743,7 +745,7 @@ describe("server", () => { it("should trigger on server if the client does not pong", done => { const opts = { allowUpgrades: false, pingInterval: 5, pingTimeout: 5 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("http://localhost:%d".s(port)); + const socket = new ClientSocket("http://localhost:%d".s(port)); socket.sendPacket = () => {}; engine.on("connection", conn => { conn.on("close", reason => { @@ -761,7 +763,7 @@ describe("server", () => { pingTimeout: 500 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("http://localhost:%d".s(port)); + const socket = new ClientSocket("http://localhost:%d".s(port)); engine.on("connection", conn => { conn.on("close", reason => { expect(reason).to.be("ping timeout"); @@ -780,7 +782,7 @@ describe("server", () => { it("should trigger on client if server does not meet ping timeout", done => { const opts = { allowUpgrades: false, pingInterval: 50, pingTimeout: 30 }; listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { // override onPacket and Transport#onClose to simulate an inactive server after handshake socket.transport.removeListener("packet"); @@ -796,7 +798,7 @@ describe("server", () => { it("should trigger on both ends upon ping timeout", done => { const opts = { allowUpgrades: false, pingTimeout: 50, pingInterval: 50 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let total = 2; function onClose(reason, err) { @@ -820,7 +822,7 @@ describe("server", () => { it("should trigger when server closes a client", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let total = 2; engine.on("connection", conn => { @@ -845,7 +847,7 @@ describe("server", () => { it("should trigger when server closes a client (ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); let total = 2; @@ -875,7 +877,7 @@ describe("server", () => { engine.httpServer.close(); engine.httpServer.listen(port); - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); @@ -894,7 +896,7 @@ describe("server", () => { it("should trigger when client closes", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let total = 2; engine.on("connection", conn => { @@ -920,7 +922,7 @@ describe("server", () => { it("should trigger when client closes (ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); let total = 2; @@ -947,7 +949,7 @@ describe("server", () => { it("should trigger when calling socket.close() in payload", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.send(null, () => { @@ -975,7 +977,7 @@ describe("server", () => { it("should abort upgrade if socket is closed (GH-35)", done => { listen({ allowUpgrades: true }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { socket.close(); // we wait until complete to see if we get an uncaught EPIPE @@ -1029,7 +1031,7 @@ describe("server", () => { $done(); } - var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var socket = new ClientSocket("ws://localhost:%d".s(port)); let serverSocket; engine.on("connection", s => { @@ -1088,7 +1090,7 @@ describe("server", () => { }); }); - var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { socket.send("test"); }); @@ -1111,7 +1113,7 @@ describe("server", () => { pingTimeout: 100 }; listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let clientCloseReason = null; socket.on("handshake", () => { @@ -1143,7 +1145,7 @@ describe("server", () => { pingTimeout: 50 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let clientCloseReason = null; engine.on("connection", conn => { @@ -1178,7 +1180,7 @@ describe("server", () => { pingTimeout: 50 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let clientCloseReason = null; socket.on("open", () => { @@ -1213,7 +1215,7 @@ describe("server", () => { pingTimeout: 100 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let clientCloseReason = null; socket.on("open", () => { @@ -1251,7 +1253,7 @@ describe("server", () => { pingTimeout: 100 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let clientCloseReason = null; socket.on("open", () => { @@ -1284,7 +1286,7 @@ describe("server", () => { "should abort the polling data request if it is " + "in progress", done => { const engine = listen({ transports: ["polling"] }, port => { - const socket = new eioc.Socket("http://localhost:%d".s(port)); + const socket = new ClientSocket("http://localhost:%d".s(port)); engine.on("connection", conn => { const onDataRequest = conn.transport.onDataRequest; @@ -1309,7 +1311,7 @@ describe("server", () => { const opts = { transports: ["websocket"] }; listen(opts, port => { const url = "ws://%s:%d".s("0.0.0.0", port); - const socket = new eioc.Socket(url); + const socket = new ClientSocket(url); socket.on("open", () => { done(new Error("Test invalidation")); }); @@ -1325,7 +1327,7 @@ describe("server", () => { it("should trigger transport close before open for xhr", done => { const opts = { transports: ["polling"] }; listen(opts, port => { - const socket = new eioc.Socket("http://invalidserver:%d".s(port)); + const socket = new ClientSocket("http://invalidserver:%d".s(port)); socket.on("open", () => { done(new Error("Test invalidation")); }); @@ -1341,7 +1343,7 @@ describe("server", () => { it("should trigger force close before open for ws", done => { const opts = { transports: ["websocket"] }; listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { done(new Error("Test invalidation")); }); @@ -1358,7 +1360,7 @@ describe("server", () => { it("should trigger force close before open for xhr", done => { const opts = { transports: ["polling"] }; listen(opts, port => { - const socket = new eioc.Socket("http://localhost:%d".s(port)); + const socket = new ClientSocket("http://localhost:%d".s(port)); socket.on("open", () => { done(new Error("Test invalidation")); }); @@ -1381,7 +1383,7 @@ describe("server", () => { engine.on("connection", conn => { conn.transport.on("close", done); }); - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); // override to simulate an inactive client @@ -1400,7 +1402,7 @@ describe("server", () => { engine.on("connection", conn => { conn.transport.on("close", done); }); - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); // override to simulate an inactive client @@ -1414,7 +1416,7 @@ describe("server", () => { engine.on("connection", conn => { conn.transport.on("close", done); }); - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); socket.on("open", () => { @@ -1430,7 +1432,7 @@ describe("server", () => { conn.transport.closeTimeout = 100; conn.transport.on("close", done); }); - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); socket.on("open", () => { @@ -1447,7 +1449,7 @@ describe("server", () => { conn.close(); }); }); - eioc("ws://localhost:%d".s(port)); + new ClientSocket("ws://localhost:%d".s(port)); }); }); @@ -1459,7 +1461,7 @@ describe("server", () => { transport.on("close", done); }); }); - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("upgrading", transport => { // override not to complete upgrading transport.send = () => {}; @@ -1470,7 +1472,7 @@ describe("server", () => { it("should not timeout after an upgrade", done => { const opts = { pingInterval: 200, pingTimeout: 20 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { setTimeout(() => { socket.removeListener("close"); @@ -1488,7 +1490,7 @@ describe("server", () => { it("should not crash when messing with Object prototype", done => { Object.prototype.foo = "bar"; // eslint-disable-line no-extend-native const engine = listen({ allowUpgrades: true }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { engine.close(); setTimeout(() => { @@ -1524,7 +1526,7 @@ describe("server", () => { it("should arrive from server to client", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.send("a"); }); @@ -1539,7 +1541,7 @@ describe("server", () => { it("should arrive from server to client (multiple)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); const expected = ["a", "b", "c"]; let i = 0; @@ -1581,7 +1583,7 @@ describe("server", () => { maxHttpBufferSize: 5 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.on("message", msg => { done( @@ -1601,7 +1603,7 @@ describe("server", () => { it("should not be receiving data when getting a message longer than maxHttpBufferSize (websocket)", done => { const opts = { maxHttpBufferSize: 5 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); engine.on("connection", conn => { @@ -1627,7 +1629,7 @@ describe("server", () => { maxHttpBufferSize: 5 }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.on("message", msg => { expect(msg).to.be("a"); @@ -1643,7 +1645,7 @@ describe("server", () => { it("should arrive from server to client (ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); engine.on("connection", conn => { @@ -1661,7 +1663,7 @@ describe("server", () => { it("should arrive from server to client (multiple, ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); const expected = ["a", "b", "c"]; @@ -1695,7 +1697,7 @@ describe("server", () => { it("should arrive from server to client (multiple, no delay, ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); const expected = ["a", "b", "c"]; @@ -1732,7 +1734,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); @@ -1760,7 +1762,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); @@ -1790,7 +1792,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); @@ -1820,7 +1822,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); @@ -1848,7 +1850,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["polling"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); @@ -1877,7 +1879,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); socket.binaryType = "arraybuffer"; @@ -1908,7 +1910,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["polling"] }; const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); socket.binaryType = "arraybuffer"; @@ -1959,7 +1961,7 @@ describe("server", () => { socket.send("aaaa"); }); - eioc("ws://localhost:%d".s(port)); + new ClientSocket("ws://localhost:%d".s(port)); }); }); @@ -1983,7 +1985,7 @@ describe("server", () => { engine.on("connection", conn => { connection = conn; }); - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); socket.on("open", () => { @@ -2005,7 +2007,7 @@ describe("server", () => { it("should support chinese", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); const shi = "石室詩士施氏,嗜獅,誓食十獅。"; const shi2 = "氏時時適市視獅。"; engine.on("connection", conn => { @@ -2069,7 +2071,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new eioc.Socket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket("https://localhost:%d".s(port), opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2111,7 +2113,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new eioc.Socket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket("https://localhost:%d".s(port), opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2155,7 +2157,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new eioc.Socket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket("https://localhost:%d".s(port), opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2198,7 +2200,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new eioc.Socket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket("https://localhost:%d".s(port), opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2241,7 +2243,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new eioc.Socket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket("https://localhost:%d".s(port), opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2261,7 +2263,7 @@ describe("server", () => { describe("writeBuffer", () => { it("should not empty until `drain` event (polling)", done => { listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); let totalEvents = 2; @@ -2280,7 +2282,7 @@ describe("server", () => { it("should not empty until `drain` event (websocket)", done => { listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); let totalEvents = 2; @@ -2301,7 +2303,7 @@ describe("server", () => { describe("callback", () => { it("should execute in order when message sent (client) (polling)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); let i = 0; @@ -2340,7 +2342,7 @@ describe("server", () => { it("should execute in order when message sent (client) (websocket)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); let i = 0; @@ -2379,7 +2381,7 @@ describe("server", () => { it("should execute in order with payloads (client) (polling)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); let i = 0; @@ -2424,7 +2426,7 @@ describe("server", () => { it("should execute in order with payloads (client) (websocket)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); let i = 0; @@ -2469,7 +2471,7 @@ describe("server", () => { it("should execute when message sent (polling)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); let i = 0; @@ -2495,7 +2497,7 @@ describe("server", () => { it("should execute when message sent (websocket)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); let i = 0; @@ -2522,7 +2524,7 @@ describe("server", () => { it("should execute once for each send", done => { const engine = listen(port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let a = 0; let b = 0; let c = 0; @@ -2553,7 +2555,7 @@ describe("server", () => { it("should execute in multipart packet", done => { const engine = listen(port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); let i = 0; let j = 0; @@ -2581,7 +2583,7 @@ describe("server", () => { it("should execute in multipart packet (polling)", done => { const engine = listen(port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); let i = 0; @@ -2619,7 +2621,7 @@ describe("server", () => { it("should clean callback references when socket gets closed with pending callbacks", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); @@ -2648,7 +2650,7 @@ describe("server", () => { it("should not execute when it is not actually sent (polling)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { transports: ["polling"] }); @@ -2673,7 +2675,7 @@ describe("server", () => { describe("pre-encoded content", () => { it("should use the pre-encoded content", done => { engine = listen(port => { - client = new eioc.Socket("ws://localhost:%d".s(port), { + client = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); @@ -2695,7 +2697,7 @@ describe("server", () => { describe("packet", () => { it("should emit when socket receives packet", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.on("packet", packet => { expect(packet.type).to.be("message"); @@ -2711,7 +2713,7 @@ describe("server", () => { it("should emit when receives pong", done => { const engine = listen({ allowUpgrades: false, pingInterval: 4 }, port => { - eioc("ws://localhost:%d".s(port)); + new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.on("packet", packet => { conn.close(); @@ -2730,7 +2732,7 @@ describe("server", () => { describe("packetCreate", () => { it("should emit before socket send message", done => { const engine = listen({ allowUpgrades: false }, port => { - eioc("ws://localhost:%d".s(port)); + new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.on("packetCreate", packet => { expect(packet.type).to.be("message"); @@ -2744,7 +2746,7 @@ describe("server", () => { it("should emit before send pong", done => { const engine = listen({ allowUpgrades: false, pingInterval: 4 }, port => { - eioc("ws://localhost:%d".s(port)); + new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.on("packetCreate", packet => { conn.close(); @@ -2812,7 +2814,7 @@ describe("server", () => { }); // client - var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var socket = new ClientSocket("ws://localhost:%d".s(port)); socket.on("open", () => { let lastSent = 0; let lastReceived = 0; @@ -3092,7 +3094,9 @@ describe("server", () => { for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf, { compress: true }); }); - eioc("http://localhost:%d".s(port), { transports: ["websocket"] }); + new ClientSocket("http://localhost:%d".s(port), { + transports: ["websocket"] + }); } ); }); @@ -3117,7 +3121,9 @@ describe("server", () => { for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf, { compress: true }); }); - eioc("http://localhost:%d".s(port), { transports: ["websocket"] }); + new ClientSocket("http://localhost:%d".s(port), { + transports: ["websocket"] + }); } ); }); @@ -3134,7 +3140,7 @@ describe("server", () => { function testForTransport(transport, done) { const engine = listen(port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new ClientSocket("ws://localhost:%d".s(port), { extraHeaders: headers, transports: [transport] }); @@ -3168,7 +3174,7 @@ describe("server", () => { }); conn.send("hi"); }); - eioc("ws://localhost:%d".s(port), { + new ClientSocket("ws://localhost:%d".s(port), { extraHeaders: headers, transports: ["polling"] }); @@ -3280,7 +3286,7 @@ describe("server", () => { partialDone(); }); - client = eioc("ws://localhost:%d".s(port), { + client = new ClientSocket("ws://localhost:%d".s(port), { transports: ["websocket"] }); @@ -3301,7 +3307,7 @@ describe("server", () => { partialDone(); }); - client = eioc("ws://localhost:%d".s(port)); + client = new ClientSocket("ws://localhost:%d".s(port)); client.on("upgrade", () => { partialDone(); @@ -3320,7 +3326,7 @@ describe("server", () => { partialDone(); }); - client = eioc("ws://localhost:%d".s(port)); + client = new ClientSocket("ws://localhost:%d".s(port)); client.on("upgrade", () => { partialDone(); @@ -3469,7 +3475,7 @@ describe("server", () => { { allowUpgrades: false, wsEngine: require("eiows").Server }, port => { expect(engine.ws instanceof require("eiows").Server).to.be.ok(); - const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const socket = new ClientSocket("ws://localhost:%d".s(port)); engine.on("connection", conn => { conn.send("a"); }); @@ -3487,7 +3493,9 @@ describe("server", () => { describe("remoteAddress", () => { it("should be defined (polling)", done => { const engine = listen({ transports: ["polling"] }, port => { - eioc("ws://localhost:%d".s(port), { transports: ["polling"] }); + new ClientSocket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); engine.on("connection", socket => { expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); done(); @@ -3497,7 +3505,9 @@ describe("server", () => { it("should be defined (ws)", done => { const engine = listen({ transports: ["websocket"] }, port => { - eioc("ws://localhost:%d".s(port), { transports: ["websocket"] }); + new ClientSocket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); engine.on("connection", socket => { expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); done(); From 37474c7e67be7c5f25f9ca2d4ea99f3a256bd2de Mon Sep 17 00:00:00 2001 From: Tom Atkinson Date: Mon, 1 Nov 2021 21:33:15 +0100 Subject: [PATCH 23/58] perf: refresh ping timer (#628) Reference: https://nodejs.org/docs/latest/api/timers.html#timeoutrefresh --- lib/socket.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/socket.ts b/lib/socket.ts index f9293213c..c7936d31a 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -137,7 +137,7 @@ export class Socket extends EventEmitter { return; } debug("got pong"); - this.schedulePing(); + this.pingIntervalTimer.refresh(); this.emit("heartbeat"); break; @@ -170,7 +170,6 @@ export class Socket extends EventEmitter { * @api private */ private schedulePing() { - clearTimeout(this.pingIntervalTimer); this.pingIntervalTimer = setTimeout(() => { debug( "writing ping packet - expecting pong within %sms", From 271e2df94d39bbd13c33cab98cdd5915f9d28536 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 1 Nov 2021 23:34:23 +0100 Subject: [PATCH 24/58] feat: add an implementation based on uWebSockets.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ```js const { App } = require("uWebSockets.js"); const { uServer } = require("engine.io"); const app = new App(); const server = new uServer(); server.attach(app); app.listen(3000); ``` Reference: https://github.com/uNetworking/uWebSockets.js Related: https://github.com/socketio/engine.io/issues/578 --- .github/workflows/ci.yml | 2 +- lib/engine.io.ts | 1 + lib/server.ts | 279 ++++++++++++----------- lib/socket.ts | 12 +- lib/transport.ts | 16 +- lib/transports-uws/index.ts | 7 + lib/transports-uws/polling.ts | 393 ++++++++++++++++++++++++++++++++ lib/transports-uws/websocket.ts | 100 ++++++++ lib/userver.ts | 254 +++++++++++++++++++++ package-lock.json | 5 + package.json | 9 +- test/common.js | 22 +- test/engine.io.js | 5 +- test/server.js | 196 ++++++++++------ 14 files changed, 1098 insertions(+), 203 deletions(-) create mode 100644 lib/transports-uws/index.ts create mode 100644 lib/transports-uws/polling.ts create mode 100644 lib/transports-uws/websocket.ts create mode 100644 lib/userver.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e1fe7b25..c51ab3f52 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [12.x, 14.x] steps: - uses: actions/checkout@v2 diff --git a/lib/engine.io.ts b/lib/engine.io.ts index 71323962f..b57ed6390 100644 --- a/lib/engine.io.ts +++ b/lib/engine.io.ts @@ -5,6 +5,7 @@ import * as parser from "engine.io-parser"; export { Server, transports, listen, attach, parser }; export { AttachOptions, ServerOptions } from "./server"; +export { uServer } from "./userver"; export { Socket } from "./socket"; export { Transport } from "./transport"; export const protocol = parser.protocol; diff --git a/lib/server.ts b/lib/server.ts index bd69d6ec0..78648dfc7 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -113,15 +113,12 @@ export interface ServerOptions { allowEIO3?: boolean; } -export class Server extends EventEmitter { +export abstract class BaseServer extends EventEmitter { public opts: ServerOptions; - public httpServer?: HttpServer; - private clients: any; + protected clients: any; private clientsCount: number; - private ws: any; - private corsMiddleware: Function; - private perMessageDeflate: any; + protected corsMiddleware: Function; /** * Server constructor. @@ -182,42 +179,7 @@ export class Server extends EventEmitter { this.init(); } - /** - * Initialize websocket server - * - * @api private - */ - private init() { - if (!~this.opts.transports.indexOf("websocket")) return; - - if (this.ws) this.ws.close(); - - this.ws = new this.opts.wsEngine({ - noServer: true, - clientTracking: false, - perMessageDeflate: this.opts.perMessageDeflate, - maxPayload: this.opts.maxHttpBufferSize - }); - - if (typeof this.ws.on === "function") { - this.ws.on("headers", (headersArray, req) => { - // note: 'ws' uses an array of headers, while Engine.IO uses an object (response.writeHead() accepts both formats) - // we could also try to parse the array and then sync the values, but that will be error-prone - const additionalHeaders = {}; - - const isInitialRequest = !req._query.sid; - if (isInitialRequest) { - this.emit("initial_headers", additionalHeaders, req); - } - - this.emit("headers", additionalHeaders, req); - - Object.keys(additionalHeaders).forEach(key => { - headersArray.push(`${key}: ${additionalHeaders[key]}`); - }); - }); - } - } + protected abstract init(); /** * Returns a list of available transports for upgrade given a certain transport. @@ -237,7 +199,7 @@ export class Server extends EventEmitter { * @return {Boolean} whether the request is valid * @api private */ - private verify(req, upgrade, fn) { + protected verify(req, upgrade, fn) { // transport check const transport = req._query.transport; if (!~this.opts.transports.indexOf(transport)) { @@ -298,18 +260,6 @@ export class Server extends EventEmitter { fn(); } - /** - * Prepares a request by processing the query string. - * - * @api private - */ - private prepare(req) { - // try to leverage pre-existing `req._query` (e.g: from connect) - if (!req._query) { - req._query = ~req.url.indexOf("?") ? qs.parse(parse(req.url).query) : {}; - } - } - /** * Closes all clients. * @@ -322,56 +272,11 @@ export class Server extends EventEmitter { this.clients[i].close(true); } } - if (this.ws) { - debug("closing webSocketServer"); - this.ws.close(); - // don't delete this.ws because it can be used again if the http server starts listening again - } + this.cleanup(); return this; } - /** - * Handles an Engine.IO HTTP request. - * - * @param {http.IncomingMessage} request - * @param {http.ServerResponse|http.OutgoingMessage} response - * @api public - */ - public handleRequest(req, res) { - debug('handling "%s" http request "%s"', req.method, req.url); - this.prepare(req); - req.res = res; - - const callback = (errorCode, errorContext) => { - if (errorCode !== undefined) { - this.emit("connection_error", { - req, - code: errorCode, - message: Server.errorMessages[errorCode], - context: errorContext - }); - abortRequest(res, errorCode, errorContext); - return; - } - - if (req._query.sid) { - debug("setting new request for existing client"); - this.clients[req._query.sid].transport.onRequest(req); - } else { - const closeConnection = (errorCode, errorContext) => - abortRequest(res, errorCode, errorContext); - this.handshake(req._query.transport, req, closeConnection); - } - }; - - if (this.corsMiddleware) { - this.corsMiddleware.call(null, req, res, () => { - this.verify(req, false, callback); - }); - } else { - this.verify(req, false, callback); - } - } + protected abstract cleanup(); /** * generate a socket id. @@ -391,9 +296,9 @@ export class Server extends EventEmitter { * @param {Object} request object * @param {Function} closeConnection * - * @api private + * @api protected */ - private async handshake(transportName, req, closeConnection) { + protected async handshake(transportName, req, closeConnection) { const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default if (protocol === 3 && !this.opts.allowEIO3) { debug("unsupported protocol version"); @@ -431,7 +336,7 @@ export class Server extends EventEmitter { debug('handshaking client "%s"', id); try { - var transport = new transports[transportName](req); + var transport = this.createTransport(transportName, req); if ("polling" === transportName) { transport.maxHttpBufferSize = this.opts.maxHttpBufferSize; transport.httpCompression = this.opts.httpCompression; @@ -486,6 +391,141 @@ export class Server extends EventEmitter { }); this.emit("connection", socket); + + return transport; + } + + protected abstract createTransport(transportName, req); + + /** + * Protocol errors mappings. + */ + + static errors = { + UNKNOWN_TRANSPORT: 0, + UNKNOWN_SID: 1, + BAD_HANDSHAKE_METHOD: 2, + BAD_REQUEST: 3, + FORBIDDEN: 4, + UNSUPPORTED_PROTOCOL_VERSION: 5 + }; + + static errorMessages = { + 0: "Transport unknown", + 1: "Session ID unknown", + 2: "Bad handshake method", + 3: "Bad request", + 4: "Forbidden", + 5: "Unsupported protocol version" + }; +} + +export class Server extends BaseServer { + public httpServer?: HttpServer; + private ws: any; + + /** + * Initialize websocket server + * + * @api protected + */ + protected init() { + if (!~this.opts.transports.indexOf("websocket")) return; + + if (this.ws) this.ws.close(); + + this.ws = new this.opts.wsEngine({ + noServer: true, + clientTracking: false, + perMessageDeflate: this.opts.perMessageDeflate, + maxPayload: this.opts.maxHttpBufferSize + }); + + if (typeof this.ws.on === "function") { + this.ws.on("headers", (headersArray, req) => { + // note: 'ws' uses an array of headers, while Engine.IO uses an object (response.writeHead() accepts both formats) + // we could also try to parse the array and then sync the values, but that will be error-prone + const additionalHeaders = {}; + + const isInitialRequest = !req._query.sid; + if (isInitialRequest) { + this.emit("initial_headers", additionalHeaders, req); + } + + this.emit("headers", additionalHeaders, req); + + Object.keys(additionalHeaders).forEach(key => { + headersArray.push(`${key}: ${additionalHeaders[key]}`); + }); + }); + } + } + + protected cleanup() { + if (this.ws) { + debug("closing webSocketServer"); + this.ws.close(); + // don't delete this.ws because it can be used again if the http server starts listening again + } + } + + /** + * Prepares a request by processing the query string. + * + * @api private + */ + private prepare(req) { + // try to leverage pre-existing `req._query` (e.g: from connect) + if (!req._query) { + req._query = ~req.url.indexOf("?") ? qs.parse(parse(req.url).query) : {}; + } + } + + protected createTransport(transportName, req) { + return new transports[transportName](req); + } + + /** + * Handles an Engine.IO HTTP request. + * + * @param {http.IncomingMessage} request + * @param {http.ServerResponse|http.OutgoingMessage} response + * @api public + */ + public handleRequest(req, res) { + debug('handling "%s" http request "%s"', req.method, req.url); + this.prepare(req); + req.res = res; + + const callback = (errorCode, errorContext) => { + if (errorCode !== undefined) { + this.emit("connection_error", { + req, + code: errorCode, + message: Server.errorMessages[errorCode], + context: errorContext + }); + abortRequest(res, errorCode, errorContext); + return; + } + + if (req._query.sid) { + debug("setting new request for existing client"); + this.clients[req._query.sid].transport.onRequest(req); + } else { + const closeConnection = (errorCode, errorContext) => + abortRequest(res, errorCode, errorContext); + this.handshake(req._query.transport, req, closeConnection); + } + }; + + if (this.corsMiddleware) { + this.corsMiddleware.call(null, req, res, () => { + this.verify(req, false, callback); + }); + } else { + this.verify(req, false, callback); + } } /** @@ -559,13 +599,13 @@ export class Server extends EventEmitter { // transport error handling takes over websocket.removeListener("error", onUpgradeError); - const transport = new transports[req._query.transport](req); + const transport = this.createTransport(req._query.transport, req); if (req._query && req._query.b64) { transport.supportsBinary = false; } else { transport.supportsBinary = true; } - transport.perMessageDeflate = this.perMessageDeflate; + transport.perMessageDeflate = this.opts.perMessageDeflate; client.maybeUpgrade(transport); } } else { @@ -590,7 +630,7 @@ export class Server extends EventEmitter { * @param {Object} options * @api public */ - public attach(server, options: AttachOptions = {}) { + public attach(server: HttpServer, options: AttachOptions = {}) { let path = (options.path || "/engine.io").replace(/\/$/, ""); const destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000; @@ -632,6 +672,7 @@ export class Server extends EventEmitter { // and if no eio thing handles the upgrade // then the socket needs to die! setTimeout(function() { + // @ts-ignore if (socket.writable && socket.bytesWritten <= 0) { return socket.end(); } @@ -640,28 +681,6 @@ export class Server extends EventEmitter { }); } } - - /** - * Protocol errors mappings. - */ - - static errors = { - UNKNOWN_TRANSPORT: 0, - UNKNOWN_SID: 1, - BAD_HANDSHAKE_METHOD: 2, - BAD_REQUEST: 3, - FORBIDDEN: 4, - UNSUPPORTED_PROTOCOL_VERSION: 5 - }; - - static errorMessages = { - 0: "Transport unknown", - 1: "Session ID unknown", - 2: "Bad handshake method", - 3: "Bad request", - 4: "Forbidden", - 5: "Unsupported protocol version" - }; } /** diff --git a/lib/socket.ts b/lib/socket.ts index c7936d31a..6697ed45a 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -11,7 +11,7 @@ export class Socket extends EventEmitter { public readonly request: IncomingMessage; public readonly remoteAddress: string; - public readyState: string; + public _readyState: string; public transport: Transport; private server: Server; @@ -28,6 +28,15 @@ export class Socket extends EventEmitter { private readonly id: string; + get readyState() { + return this._readyState; + } + + set readyState(state) { + debug("readyState updated from %s to %s", this._readyState, state); + this._readyState = state; + } + /** * Client class (abstract). * @@ -247,6 +256,7 @@ export class Socket extends EventEmitter { const onPacket = packet => { if ("ping" === packet.type && "probe" === packet.data) { + debug("got probe ping packet, sending pong"); transport.send([{ type: "pong", data: "probe" }]); this.emit("upgrading", transport); clearInterval(this.checkIntervalTimer); diff --git a/lib/transport.ts b/lib/transport.ts index a82e1b846..53d8ad023 100644 --- a/lib/transport.ts +++ b/lib/transport.ts @@ -19,12 +19,26 @@ export abstract class Transport extends EventEmitter { public writable: boolean; public protocol: number; - protected readyState: string; + protected _readyState: string; protected discarded: boolean; protected parser: any; protected req: IncomingMessage & { cleanup: Function }; protected supportsBinary: boolean; + get readyState() { + return this._readyState; + } + + set readyState(state) { + debug( + "readyState updated from %s to %s (%s)", + this._readyState, + state, + this.name + ); + this._readyState = state; + } + /** * Transport constructor. * diff --git a/lib/transports-uws/index.ts b/lib/transports-uws/index.ts new file mode 100644 index 000000000..d015645e7 --- /dev/null +++ b/lib/transports-uws/index.ts @@ -0,0 +1,7 @@ +import { Polling } from "./polling"; +import { WebSocket } from "./websocket"; + +export default { + polling: Polling, + websocket: WebSocket +}; diff --git a/lib/transports-uws/polling.ts b/lib/transports-uws/polling.ts new file mode 100644 index 000000000..8d6b98279 --- /dev/null +++ b/lib/transports-uws/polling.ts @@ -0,0 +1,393 @@ +import { Transport } from "../transport"; +import { createGzip, createDeflate } from "zlib"; +import * as accepts from "accepts"; +import debugModule from "debug"; +import { HttpRequest, HttpResponse } from "uWebSockets.js"; + +const debug = debugModule("engine:polling"); + +const compressionMethods = { + gzip: createGzip, + deflate: createDeflate +}; + +export class Polling extends Transport { + public maxHttpBufferSize: number; + public httpCompression: any; + + private res: HttpResponse; + private dataReq: HttpRequest; + private dataRes: HttpResponse; + private shouldClose: Function; + + private readonly closeTimeout: number; + + /** + * HTTP polling constructor. + * + * @api public. + */ + constructor(req) { + super(req); + + this.closeTimeout = 30 * 1000; + } + + /** + * Transport name + * + * @api public + */ + get name() { + return "polling"; + } + + get supportsFraming() { + return false; + } + + /** + * Overrides onRequest. + * + * @param req + * + * @api private + */ + onRequest(req) { + const res = req.res; + + if (req.getMethod() === "get") { + this.onPollRequest(req, res); + } else if (req.getMethod() === "post") { + this.onDataRequest(req, res); + } else { + res.writeStatus("500 Internal Server Error"); + res.end(); + } + } + + /** + * The client sends a request awaiting for us to send data. + * + * @api private + */ + onPollRequest(req, res) { + if (this.req) { + debug("request overlap"); + // assert: this.res, '.req and .res should be (un)set together' + this.onError("overlap from client"); + res.writeStatus("500 Internal Server Error"); + res.end(); + return; + } + + debug("setting request"); + + this.req = req; + this.res = res; + + const onClose = () => { + this.writable = false; + this.onError("poll connection closed prematurely"); + }; + + const cleanup = () => { + this.req = this.res = null; + }; + + req.cleanup = cleanup; + res.onAborted(onClose); + + this.writable = true; + this.emit("drain"); + + // if we're still writable but had a pending close, trigger an empty send + if (this.writable && this.shouldClose) { + debug("triggering empty send to append close packet"); + this.send([{ type: "noop" }]); + } + } + + /** + * The client sends a request with data. + * + * @api private + */ + onDataRequest(req, res) { + if (this.dataReq) { + // assert: this.dataRes, '.dataReq and .dataRes should be (un)set together' + this.onError("data request overlap from client"); + res.writeStatus("500 Internal Server Error"); + res.end(); + return; + } + + const isBinary = "application/octet-stream" === req.headers["content-type"]; + + if (isBinary && this.protocol === 4) { + return this.onError("invalid content"); + } + + this.dataReq = req; + this.dataRes = res; + + let chunks = []; + let contentLength = 0; + + const cleanup = () => { + this.dataReq = this.dataRes = chunks = null; + }; + + const onClose = () => { + cleanup(); + this.onError("data request connection closed prematurely"); + }; + + const headers = { + // text/html is required instead of text/plain to avoid an + // unwanted download dialog on certain user-agents (GH-43) + "Content-Type": "text/html" + }; + + this.headers(req, headers); + Object.keys(headers).forEach(key => { + res.writeHeader(key, String(headers[key])); + }); + + const onEnd = () => { + this.onData(Buffer.concat(chunks).toString()); + + if (this.readyState !== "closing") { + res.end("ok"); + } + cleanup(); + }; + + res.onAborted(onClose); + + res.onData((chunk, isLast) => { + chunks.push(Buffer.from(chunk)); + contentLength += Buffer.byteLength(chunk); + if (contentLength > this.maxHttpBufferSize) { + this.onError("payload too large"); + res.writeStatus("413 Payload Too Large"); + res.end(); + return; + } + if (isLast) { + onEnd(); + } + }); + } + + /** + * Processes the incoming data payload. + * + * @param {String} encoded payload + * @api private + */ + onData(data) { + debug('received "%s"', data); + const callback = packet => { + if ("close" === packet.type) { + debug("got xhr close packet"); + this.onClose(); + return false; + } + + this.onPacket(packet); + }; + + if (this.protocol === 3) { + this.parser.decodePayload(data, callback); + } else { + this.parser.decodePayload(data).forEach(callback); + } + } + + /** + * Overrides onClose. + * + * @api private + */ + onClose() { + if (this.writable) { + // close pending poll request + this.send([{ type: "noop" }]); + } + super.onClose(); + } + + /** + * Writes a packet payload. + * + * @param {Object} packet + * @api private + */ + send(packets) { + this.writable = false; + + if (this.shouldClose) { + debug("appending close packet to payload"); + packets.push({ type: "close" }); + this.shouldClose(); + this.shouldClose = null; + } + + const doWrite = data => { + const compress = packets.some(packet => { + return packet.options && packet.options.compress; + }); + this.write(data, { compress }); + }; + + if (this.protocol === 3) { + this.parser.encodePayload(packets, this.supportsBinary, doWrite); + } else { + this.parser.encodePayload(packets, doWrite); + } + } + + /** + * Writes data as response to poll request. + * + * @param {String} data + * @param {Object} options + * @api private + */ + write(data, options) { + debug('writing "%s"', data); + this.doWrite(data, options, () => { + this.req.cleanup(); + }); + } + + /** + * Performs the write. + * + * @api private + */ + doWrite(data, options, callback) { + // explicit UTF-8 is required for pages not served under utf + const isString = typeof data === "string"; + const contentType = isString + ? "text/plain; charset=UTF-8" + : "application/octet-stream"; + + const headers = { + "Content-Type": contentType + }; + + const respond = data => { + this.headers(this.req, headers); + Object.keys(headers).forEach(key => { + this.res.writeHeader(key, String(headers[key])); + }); + this.res.end(data); + callback(); + }; + + if (!this.httpCompression || !options.compress) { + respond(data); + return; + } + + const len = isString ? Buffer.byteLength(data) : data.length; + if (len < this.httpCompression.threshold) { + respond(data); + return; + } + + const encoding = accepts(this.req).encodings(["gzip", "deflate"]); + if (!encoding) { + respond(data); + return; + } + + this.compress(data, encoding, (err, data) => { + if (err) { + this.res.writeStatus("500 Internal Server Error"); + this.res.end(); + callback(err); + return; + } + + headers["Content-Encoding"] = encoding; + respond(data); + }); + } + + /** + * Compresses data. + * + * @api private + */ + compress(data, encoding, callback) { + debug("compressing"); + + const buffers = []; + let nread = 0; + + compressionMethods[encoding](this.httpCompression) + .on("error", callback) + .on("data", function(chunk) { + buffers.push(chunk); + nread += chunk.length; + }) + .on("end", function() { + callback(null, Buffer.concat(buffers, nread)); + }) + .end(data); + } + + /** + * Closes the transport. + * + * @api private + */ + doClose(fn) { + debug("closing"); + + let closeTimeoutTimer; + + const onClose = () => { + clearTimeout(closeTimeoutTimer); + fn(); + this.onClose(); + }; + + if (this.writable) { + debug("transport writable - closing right away"); + this.send([{ type: "close" }]); + onClose(); + } else if (this.discarded) { + debug("transport discarded - closing right away"); + onClose(); + } else { + debug("transport not writable - buffering orderly close"); + this.shouldClose = onClose; + closeTimeoutTimer = setTimeout(onClose, this.closeTimeout); + } + } + + /** + * Returns headers for a response. + * + * @param req - request + * @param {Object} extra headers + * @api private + */ + headers(req, headers) { + headers = headers || {}; + + // prevent XSS warnings on IE + // https://github.com/LearnBoost/socket.io/pull/1333 + const ua = req.headers["user-agent"]; + if (ua && (~ua.indexOf(";MSIE") || ~ua.indexOf("Trident/"))) { + headers["X-XSS-Protection"] = "0"; + } + + this.emit("headers", headers, req); + return headers; + } +} diff --git a/lib/transports-uws/websocket.ts b/lib/transports-uws/websocket.ts new file mode 100644 index 000000000..3add4baab --- /dev/null +++ b/lib/transports-uws/websocket.ts @@ -0,0 +1,100 @@ +import { Transport } from "../transport"; +import debugModule from "debug"; + +const debug = debugModule("engine:ws"); + +export class WebSocket extends Transport { + protected perMessageDeflate: any; + private socket: any; + + /** + * WebSocket transport + * + * @param req + * @api public + */ + constructor(req) { + super(req); + this.writable = false; + this.perMessageDeflate = null; + } + + /** + * Transport name + * + * @api public + */ + get name() { + return "websocket"; + } + + /** + * Advertise upgrade support. + * + * @api public + */ + get handlesUpgrades() { + return true; + } + + /** + * Advertise framing support. + * + * @api public + */ + get supportsFraming() { + return true; + } + + /** + * Writes a packet payload. + * + * @param {Array} packets + * @api private + */ + send(packets) { + const packet = packets.shift(); + if (typeof packet === "undefined") { + this.writable = true; + this.emit("drain"); + return; + } + + // always creates a new object since ws modifies it + const opts: { compress?: boolean } = {}; + if (packet.options) { + opts.compress = packet.options.compress; + } + + const send = data => { + const isBinary = typeof data !== "string"; + const compress = + this.perMessageDeflate && + Buffer.byteLength(data) > this.perMessageDeflate.threshold; + + debug('writing "%s"', data); + this.writable = false; + + this.socket.send(data, isBinary, compress); + this.send(packets); + }; + + if (packet.options && typeof packet.options.wsPreEncoded === "string") { + send(packet.options.wsPreEncoded); + } else { + this.parser.encodePacket(packet, this.supportsBinary, send); + } + } + + /** + * Closes the transport. + * + * @api private + */ + doClose(fn) { + debug("closing"); + fn && fn(); + // call fn first since socket.close() immediately emits a "close" event + this.socket.close(); + } +} diff --git a/lib/userver.ts b/lib/userver.ts new file mode 100644 index 000000000..76c9a41d1 --- /dev/null +++ b/lib/userver.ts @@ -0,0 +1,254 @@ +import debugModule from "debug"; +import { AttachOptions, BaseServer, Server } from "./server"; +import { HttpRequest, HttpResponse } from "uWebSockets.js"; +import transports from "./transports-uws"; + +const debug = debugModule("engine:uws"); + +export class uServer extends BaseServer { + protected init() {} + protected cleanup() {} + + /** + * Prepares a request by processing the query string. + * + * @api private + */ + private prepare(req, res: HttpResponse) { + req.method = req.getMethod().toUpperCase(); + + const params = new URLSearchParams(req.getQuery()); + req._query = Object.fromEntries(params.entries()); + + req.headers = {}; + req.forEach((key, value) => { + req.headers[key] = value; + }); + + req.connection = { + remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString() + }; + } + + protected createTransport(transportName, req) { + return new transports[transportName](req); + } + + /** + * Attach the engine to a µWebSockets.js server + * @param app + * @param options + */ + public attach(app /* : TemplatedApp */, options: AttachOptions = {}) { + const path = (options.path || "/engine.io").replace(/\/$/, "") + "/"; + + app + .any(path, this.handleRequest.bind(this)) + // + .ws(path, { + maxPayloadLength: this.opts.maxHttpBufferSize, + upgrade: this.handleUpgrade.bind(this), + open: ws => { + ws.transport.socket = ws; + ws.transport.writable = true; + ws.transport.emit("drain"); + }, + message: (ws, message, isBinary) => { + ws.transport.onData( + isBinary ? message : Buffer.from(message).toString() + ); + }, + close: (ws, code, message) => { + ws.transport.onClose(code, message); + } + }); + } + + private handleRequest( + res: HttpResponse, + req: HttpRequest & { res: any; _query: any } + ) { + debug('handling "%s" http request "%s"', req.getMethod(), req.getUrl()); + this.prepare(req, res); + + req.res = res; + + const callback = (errorCode, errorContext) => { + if (errorCode !== undefined) { + this.emit("connection_error", { + req, + code: errorCode, + message: Server.errorMessages[errorCode], + context: errorContext + }); + this.abortRequest(req.res, errorCode, errorContext); + return; + } + + if (req._query.sid) { + debug("setting new request for existing client"); + this.clients[req._query.sid].transport.onRequest(req); + } else { + const closeConnection = (errorCode, errorContext) => + this.abortRequest(res, errorCode, errorContext); + this.handshake(req._query.transport, req, closeConnection); + } + }; + + if (this.corsMiddleware) { + // needed to buffer headers until the status is computed + req.res = new ResponseWrapper(res); + + this.corsMiddleware.call(null, req, req.res, () => { + this.verify(req, false, callback); + }); + } else { + this.verify(req, false, callback); + } + } + + private handleUpgrade( + res: HttpResponse, + req: HttpRequest & { _query: any }, + context + ) { + debug("on upgrade"); + + this.prepare(req, res); + + // @ts-ignore + req.res = res; + + this.verify(req, true, async (errorCode, errorContext) => { + if (errorCode) { + this.emit("connection_error", { + req, + code: errorCode, + message: Server.errorMessages[errorCode], + context: errorContext + }); + this.abortRequest(res, errorCode, errorContext); + return; + } + + const id = req._query.sid; + let transport; + + if (id) { + const client = this.clients[id]; + if (!client) { + debug("upgrade attempt for closed client"); + res.close(); + } else if (client.upgrading) { + debug("transport has already been trying to upgrade"); + res.close(); + } else if (client.upgraded) { + debug("transport had already been upgraded"); + res.close(); + } else { + debug("upgrading existing transport"); + transport = this.createTransport(req._query.transport, req); + client.maybeUpgrade(transport); + } + } else { + transport = await this.handshake( + req._query.transport, + req, + (errorCode, errorContext) => + this.abortRequest(res, errorCode, errorContext) + ); + if (!transport) { + return; + } + } + + res.upgrade( + { + transport + }, + req.getHeader("sec-websocket-key"), + req.getHeader("sec-websocket-protocol"), + req.getHeader("sec-websocket-extensions"), + context + ); + }); + } + + private abortRequest( + res: HttpResponse | ResponseWrapper, + errorCode, + errorContext + ) { + const statusCode = + errorCode === Server.errors.FORBIDDEN + ? "403 Forbidden" + : "400 Bad Request"; + const message = + errorContext && errorContext.message + ? errorContext.message + : Server.errorMessages[errorCode]; + + res.writeStatus(statusCode); + res.writeHeader("Content-Type", "application/json"); + res.end( + JSON.stringify({ + code: errorCode, + message + }) + ); + } +} + +class ResponseWrapper { + private statusWritten: boolean = false; + private headers = []; + + constructor(readonly res: HttpResponse) {} + + public set statusCode(status: number) { + this.writeStatus(status === 200 ? "200 OK" : "204 No Content"); + } + + public setHeader(key, value) { + this.writeHeader(key, value); + } + + // needed by vary: https://github.com/jshttp/vary/blob/5d725d059b3871025cf753e9dfa08924d0bcfa8f/index.js#L134 + public getHeader() {} + + public writeStatus(status: string) { + this.res.writeStatus(status); + this.statusWritten = true; + this.writeBufferedHeaders(); + } + + public writeHeader(key: string, value: string) { + if (key === "Content-Length") { + // the content length is automatically added by uWebSockets.js + return; + } + if (this.statusWritten) { + this.res.writeHeader(key, value); + } else { + this.headers.push([key, value]); + } + } + + private writeBufferedHeaders() { + this.headers.forEach(([key, value]) => { + this.res.writeHeader(key, value); + }); + } + + public end(data) { + if (!this.statusWritten) { + // status will be inferred as "200 OK" + this.writeBufferedHeaders(); + } + this.res.end(data); + } + + public onAborted(fn) { + this.res.onAborted(fn); + } +} diff --git a/package-lock.json b/package-lock.json index e808f386b..da54a98b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -929,6 +929,11 @@ "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, + "uWebSockets.js": { + "version": "github:uNetworking/uWebSockets.js#4558ee00f9f1f686fffe1accbfc2e85b1af9c50f", + "from": "github:uNetworking/uWebSockets.js#v20.0.0", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 56bbb2fe7..5b013f066 100644 --- a/package.json +++ b/package.json @@ -53,11 +53,16 @@ "rimraf": "^3.0.2", "s": "0.1.1", "superagent": "^3.8.1", - "typescript": "^4.4.3" + "typescript": "^4.4.3", + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.0.0" }, "scripts": { "compile": "rimraf ./build && tsc", - "test": "npm run compile && npm run format:check && mocha --bail && EIO_CLIENT=3 mocha && EIO_WS_ENGINE=eiows mocha", + "test": "npm run compile && npm run format:check && npm run test:default && npm run test:compat-v3 && npm run test:eiows && npm run test:uws", + "test:default": "mocha --bail", + "test:compat-v3": "EIO_CLIENT=3 mocha", + "test:eiows": "EIO_WS_ENGINE=eiows mocha", + "test:uws": "EIO_WS_ENGINE=uws mocha", "format:check": "prettier --check 'lib/**/*.ts' 'test/**/*.js'", "format:fix": "prettier --write 'lib/**/*.ts' 'test/**/*.js'", "prepack": "npm run compile" diff --git a/test/common.js b/test/common.js index 014681af3..5f9807b0f 100644 --- a/test/common.js +++ b/test/common.js @@ -1,4 +1,5 @@ -const { listen } = require(".."); +const { listen, uServer } = require(".."); +const { App, us_socket_local_port } = require("uWebSockets.js"); const { Socket } = process.env.EIO_CLIENT === "3" ? require("engine.io-client-v3") @@ -16,8 +17,23 @@ exports.listen = (opts, fn) => { opts.allowEIO3 = true; - if (process.env.EIO_WS_ENGINE) { - opts.wsEngine = require(process.env.EIO_WS_ENGINE).Server; + if (process.env.EIO_WS_ENGINE === "uws") { + const engine = new uServer(opts); + const app = App(); + engine.attach(app, opts); + + app.listen(0, listenSocket => { + const port = us_socket_local_port(listenSocket); + process.nextTick(() => { + fn(port); + }); + }); + + return engine; + } + + if (process.env.EIO_WS_ENGINE === "eiows") { + opts.wsEngine = require("eiows").Server; } const e = listen(0, opts, () => { diff --git a/test/engine.io.js b/test/engine.io.js index 4c74b7d20..ffe7edddd 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -33,7 +33,10 @@ describe("engine", () => { }); describe("listen", () => { - it("should open a http server that returns 501", done => { + it("should open a http server that returns 501", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } listen(port => { request.get("http://localhost:%d/".s(port), (err, res) => { expect(err).to.be.an(Error); diff --git a/test/server.js b/test/server.js index 60e00536c..bc2f6a058 100644 --- a/test/server.js +++ b/test/server.js @@ -20,7 +20,7 @@ describe("server", () => { let engine, client; afterEach(() => { - if (engine) { + if (engine && engine.httpServer) { engine.httpServer.close(); } if (client) { @@ -34,7 +34,7 @@ describe("server", () => { engine = listen(port => { engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(0); expect(err.message).to.be("Transport unknown"); expect(err.context.transport).to.be("tobi"); @@ -42,7 +42,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "tobi" }) // no tobi transport - outrageous .end((err, res) => { expect(err).to.be.an(Error); @@ -60,7 +60,7 @@ describe("server", () => { // make sure we check for actual properties - not those present on every {} engine = listen(port => { engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(0); expect(err.message).to.be("Transport unknown"); expect(err.context.transport).to.be("constructor"); @@ -68,7 +68,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "constructor" }) .end((err, res) => { @@ -86,7 +86,7 @@ describe("server", () => { engine = listen(port => { engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(1); expect(err.message).to.be("Session ID unknown"); expect(err.context.sid).to.be("test"); @@ -94,7 +94,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "polling", sid: "test" }) .end((err, res) => { @@ -118,7 +118,7 @@ describe("server", () => { }, port => { engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(4); expect(err.message).to.be("Forbidden"); expect(err.context.message).to.be("Thou shall not pass"); @@ -126,7 +126,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "polling" }) .end((err, res) => { @@ -140,7 +140,7 @@ describe("server", () => { ); }); - it("should disallow connection that are rejected by `allowRequest`", done => { + it("should disallow connection that are rejected by `allowRequest` (ws)", done => { listen( { allowRequest: (req, fn) => { @@ -180,7 +180,7 @@ describe("server", () => { it("should send the io cookie custom name", done => { listen({ cookie: { name: "woot" } }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -196,7 +196,7 @@ describe("server", () => { it("should send the cookie with custom path", done => { listen({ cookie: { path: "/custom" } }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -212,7 +212,7 @@ describe("server", () => { it("should send the cookie with path=false", done => { listen({ cookie: { path: false } }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -228,7 +228,7 @@ describe("server", () => { it("should send the io cookie with httpOnly=true", done => { listen({ cookie: { httpOnly: true } }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -244,7 +244,7 @@ describe("server", () => { it("should send the io cookie with sameSite=strict", done => { listen({ cookie: { sameSite: "strict" } }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -260,7 +260,7 @@ describe("server", () => { it("should send the io cookie with httpOnly=false", done => { listen({ cookie: { httpOnly: false } }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -276,7 +276,7 @@ describe("server", () => { it("should send the io cookie with httpOnly not boolean", done => { listen({ cookie: { httpOnly: "no" } }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -292,7 +292,7 @@ describe("server", () => { it("should not send the io cookie", done => { listen({ cookie: false }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling" }) .end((err, res) => { expect(err).to.be(null); @@ -360,7 +360,7 @@ describe("server", () => { }; engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(3); expect(err.message).to.be("Bad request"); expect(err.context.name).to.be("ID_GENERATION_ERROR"); @@ -376,7 +376,7 @@ describe("server", () => { it("should disallow connection that are rejected by `generateId` (websocket only)", function(done) { if (process.env.EIO_WS_ENGINE === "eiows") { - this.skip(); + return this.skip(); } const partialDone = createPartialDone(done, 2); @@ -386,7 +386,7 @@ describe("server", () => { }; engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(3); expect(err.message).to.be("Bad request"); expect(err.context.name).to.be("ID_GENERATION_ERROR"); @@ -510,7 +510,7 @@ describe("server", () => { }); engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(3); expect(err.message).to.be("Bad request"); expect(err.context.name).to.be("TRANSPORT_MISMATCH"); @@ -562,7 +562,10 @@ describe("server", () => { }); }); - it("should disallow bad requests (handshake error)", done => { + it("should disallow bad requests (handshake error)", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const partialDone = createPartialDone(done, 2); engine = listen( @@ -571,7 +574,7 @@ describe("server", () => { }, port => { engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(3); expect(err.message).to.be("Bad request"); expect(err.context.name).to.be("TRANSPORT_HANDSHAKE_ERROR"); @@ -581,7 +584,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "websocket" }) .end((err, res) => { @@ -601,7 +604,10 @@ describe("server", () => { ); }); - it("should disallow invalid origin header", done => { + it("should disallow invalid origin header", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const partialDone = createPartialDone(done, 2); engine = listen(port => { @@ -613,7 +619,7 @@ describe("server", () => { }; engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(3); expect(err.message).to.be("Bad request"); expect(err.context.name).to.be("INVALID_ORIGIN"); @@ -622,7 +628,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://engine.io/") .query({ transport: "websocket" }) .end((err, res) => { @@ -640,7 +646,7 @@ describe("server", () => { engine = listen(port => { engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(2); expect(err.message).to.be("Bad handshake method"); expect(err.context.method).to.be("OPTIONS"); @@ -648,7 +654,7 @@ describe("server", () => { }); request - .options("http://localhost:%d/engine.io/default/".s(port)) + .options("http://localhost:%d/engine.io/".s(port)) .query({ transport: "polling" }) .end((err, res) => { expect(err).to.be.an(Error); @@ -670,7 +676,7 @@ describe("server", () => { const port = httpServer.address().port; engine.on("connection_error", err => { - expect(err.req).to.be.an(http.IncomingMessage); + expect(err.req).to.be.ok(); expect(err.code).to.be(5); expect(err.message).to.be("Unsupported protocol version"); expect(err.context.protocol).to.be(3); @@ -871,7 +877,10 @@ describe("server", () => { }); }); - it("should allow client reconnect after restarting (ws)", done => { + it("should allow client reconnect after restarting (ws)", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const opts = { transports: ["websocket"] }; const engine = listen(opts, port => { engine.httpServer.close(); @@ -1284,7 +1293,10 @@ describe("server", () => { it( "should abort the polling data request if it is " + "in progress", - done => { + function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const engine = listen({ transports: ["polling"] }, port => { const socket = new ClientSocket("http://localhost:%d".s(port)); @@ -1501,6 +1513,12 @@ describe("server", () => { }); describe("graceful close", () => { + before(function() { + if (process.env.EIO_WS_ENGINE === "uws") { + this.skip(); + } + }); + function fixture(filename) { return ( process.execPath + " " + path.join(__dirname, "fixtures", filename) @@ -1969,6 +1987,9 @@ describe("server", () => { "should interleave with pongs if many messages buffered " + "after connection open", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } this.slow(4000); this.timeout(8000); @@ -2042,7 +2063,10 @@ describe("server", () => { }); }); - it("should send and receive data with key and cert (polling)", done => { + it("should send and receive data with key and cert (polling)", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -2086,7 +2110,10 @@ describe("server", () => { }); }); - it("should send and receive data with ca when not requiring auth (polling)", done => { + it("should send and receive data with ca when not requiring auth (polling)", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -2128,7 +2155,10 @@ describe("server", () => { }); }); - it("should send and receive data with key and cert (ws)", done => { + it("should send and receive data with key and cert (ws)", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -2172,7 +2202,10 @@ describe("server", () => { }); }); - it("should send and receive data with pfx (polling)", done => { + it("should send and receive data with pfx (polling)", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -2215,7 +2248,10 @@ describe("server", () => { }); }); - it("should send and receive data with pfx (ws)", done => { + it("should send and receive data with pfx (ws)", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -2857,8 +2893,10 @@ describe("server", () => { }); }); - // attach another engine to make sure it doesn't break upgrades - attach(engine.httpServer, { path: "/foo" }); + if (engine.httpServer) { + // attach another engine to make sure it doesn't break upgrades + attach(engine.httpServer, { path: "/foo" }); + } }); }); @@ -2879,14 +2917,14 @@ describe("server", () => { http.get( { port: port, - path: "/engine.io/default/?transport=polling" + path: "/engine.io/?transport=polling" }, res => { const sid = getSidFromResponse(res); http.get( { port: port, - path: "/engine.io/default/?transport=polling&sid=" + sid, + path: "/engine.io/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, res => { @@ -2914,14 +2952,14 @@ describe("server", () => { http.get( { port: port, - path: "/engine.io/default/?transport=polling" + path: "/engine.io/?transport=polling" }, res => { const sid = getSidFromResponse(res); http.get( { port: port, - path: "/engine.io/default/?transport=polling&sid=" + sid, + path: "/engine.io/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "deflate" } }, res => { @@ -2955,14 +2993,14 @@ describe("server", () => { http.get( { port: port, - path: "/engine.io/default/?transport=polling" + path: "/engine.io/?transport=polling" }, res => { const sid = getSidFromResponse(res); http.get( { port: port, - path: "/engine.io/default/?transport=polling&sid=" + sid, + path: "/engine.io/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, res => { @@ -2989,14 +3027,14 @@ describe("server", () => { http.get( { port: port, - path: "/engine.io/default/?transport=polling" + path: "/engine.io/?transport=polling" }, res => { const sid = getSidFromResponse(res); http.get( { port: port, - path: "/engine.io/default/?transport=polling&sid=" + sid, + path: "/engine.io/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, res => { @@ -3021,14 +3059,14 @@ describe("server", () => { http.get( { port: port, - path: "/engine.io/default/?transport=polling" + path: "/engine.io/?transport=polling" }, res => { const sid = getSidFromResponse(res); http.get( { port: port, - path: "/engine.io/default/?transport=polling&sid=" + sid, + path: "/engine.io/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, res => { @@ -3052,14 +3090,14 @@ describe("server", () => { http.get( { port: port, - path: "/engine.io/default/?transport=polling" + path: "/engine.io/?transport=polling" }, res => { const sid = getSidFromResponse(res); http.get( { port: port, - path: "/engine.io/default/?transport=polling&sid=" + sid, + path: "/engine.io/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, res => { @@ -3074,7 +3112,10 @@ describe("server", () => { }); describe("permessage-deflate", () => { - it("should set threshold", done => { + it("should set threshold", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const engine = listen( { transports: ["websocket"], perMessageDeflate: { threshold: 0 } }, port => { @@ -3101,7 +3142,10 @@ describe("server", () => { ); }); - it("should not compress when the byte size is below threshold", done => { + it("should not compress when the byte size is below threshold", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + return this.skip(); + } const engine = listen( { transports: ["websocket"], perMessageDeflate: true }, port => { @@ -3273,8 +3317,11 @@ describe("server", () => { }); it("should emit a 'initial_headers' event (websocket)", function(done) { - if (process.env.EIO_WS_ENGINE === "eiows") { - this.skip(); + if ( + process.env.EIO_WS_ENGINE === "eiows" || + process.env.EIO_WS_ENGINE === "uws" + ) { + return this.skip(); } const partialDone = createPartialDone(done, 2); @@ -3316,8 +3363,11 @@ describe("server", () => { }); it("should emit several 'headers' events per connection", function(done) { - if (process.env.EIO_WS_ENGINE === "eiows") { - this.skip(); + if ( + process.env.EIO_WS_ENGINE === "eiows" || + process.env.EIO_WS_ENGINE === "uws" + ) { + return this.skip(); } const partialDone = createPartialDone(done, 4); @@ -3341,7 +3391,7 @@ describe("server", () => { { cors: { origin: true, headers: ["my-header"], credentials: true } }, port => { request - .options("http://localhost:%d/engine.io/default/".s(port)) + .options("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "polling" }) .end((err, res) => { @@ -3371,7 +3421,7 @@ describe("server", () => { { cors: { origin: true, headers: ["my-header"], credentials: true } }, port => { request - .get("http://localhost:%d/engine.io/default/".s(port)) + .get("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "polling" }) .end((err, res) => { @@ -3405,7 +3455,7 @@ describe("server", () => { }, port => { request - .options("http://localhost:%d/engine.io/default/".s(port)) + .options("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://bad-domain.com") .query({ transport: "polling" }) .end((err, res) => { @@ -3439,7 +3489,7 @@ describe("server", () => { }, port => { request - .options("http://localhost:%d/engine.io/default/".s(port)) + .options("http://localhost:%d/engine.io/".s(port)) .set("Origin", "http://good-domain.com") .query({ transport: "polling" }) .end((err, res) => { @@ -3470,6 +3520,12 @@ describe("server", () => { }); describe("wsEngine option", () => { + before(function() { + if (process.env.EIO_WS_ENGINE === "uws") { + this.skip(); + } + }); + it("should allow loading of other websocket server implementation like eiows", done => { const engine = listen( { allowUpgrades: false, wsEngine: require("eiows").Server }, @@ -3497,7 +3553,13 @@ describe("server", () => { transports: ["polling"] }); engine.on("connection", socket => { - expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); + if (process.env.EIO_WS_ENGINE === "uws") { + expect(socket.remoteAddress).to.be( + "0000:0000:0000:0000:0000:ffff:7f00:0001" + ); + } else { + expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); + } done(); }); }); @@ -3509,7 +3571,13 @@ describe("server", () => { transports: ["websocket"] }); engine.on("connection", socket => { - expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); + if (process.env.EIO_WS_ENGINE === "uws") { + expect(socket.remoteAddress).to.be( + "0000:0000:0000:0000:0000:ffff:7f00:0001" + ); + } else { + expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); + } done(); }); }); From ed50fc346b9c58459bf4e6fe5c45e8d34faac8da Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sat, 6 Nov 2021 07:43:40 +0100 Subject: [PATCH 25/58] fix: fix payload encoding for v3 clients The v3 parser (used for compatibility with older clients) was broken during the migration to TypeScript ([1]). This was not caught in the test suite because the Node.js client does not support binary packet in polling mode (packets are base64-encoded). [1]: https://github.com/socketio/engine.io/commit/c0d6eaa1ba1291946dc8425d5f533d5f721862dd Backported from 6.0.x branch: https://github.com/socketio/engine.io/commit/3f42262fd27a77a7383cdbb44ede7c6211a9782b --- lib/parser-v3/utf8.ts | 2 +- test/parser.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/parser.js diff --git a/lib/parser-v3/utf8.ts b/lib/parser-v3/utf8.ts index 9a9decbd3..b878740ff 100644 --- a/lib/parser-v3/utf8.ts +++ b/lib/parser-v3/utf8.ts @@ -203,7 +203,7 @@ function utf8decode(byteString, opts) { return ucs2encode(codePoints); } -export default { +module.exports = { version: '2.1.2', encode: utf8encode, decode: utf8decode diff --git a/test/parser.js b/test/parser.js new file mode 100644 index 000000000..3f967e1b7 --- /dev/null +++ b/test/parser.js @@ -0,0 +1,22 @@ +const expect = require("expect.js"); +const parser = require("../build/parser-v3/index.js"); + +describe("parser", () => { + it("properly encodes a mixed payload", done => { + parser.encodePayload( + [ + { type: "message", data: "€€€€" }, + { type: "message", data: Buffer.from([1, 2, 3]) } + ], + true, + encoded => { + expect(encoded).to.be.a(Buffer); + + parser.decodePayload(encoded, decoded => { + expect(decoded.data).to.eql("€€€€"); + done(); + }); + } + ); + }); +}); From 35b6b505df7688a33ff29790283f85e324dfb9e6 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 8 Nov 2021 07:07:34 +0100 Subject: [PATCH 26/58] test: replace "s" package by string interpolation --- package-lock.json | 6 -- package.json | 1 - test/common.js | 6 -- test/engine.io.js | 10 +- test/server.js | 260 +++++++++++++++++++++++----------------------- 5 files changed, 134 insertions(+), 149 deletions(-) diff --git a/package-lock.json b/package-lock.json index da54a98b0..00d95efb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -846,12 +846,6 @@ "glob": "^7.1.3" } }, - "s": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/s/-/s-0.1.1.tgz", - "integrity": "sha1-bfd3XSqyF2/xMZE/Qn7dtDAvrAw=", - "dev": true - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", diff --git a/package.json b/package.json index 5b013f066..1229d8ca0 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "mocha": "^4.0.1", "prettier": "^1.19.1", "rimraf": "^3.0.2", - "s": "0.1.1", "superagent": "^3.8.1", "typescript": "^4.4.3", "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.0.0" diff --git a/test/common.js b/test/common.js index 5f9807b0f..c02d6a429 100644 --- a/test/common.js +++ b/test/common.js @@ -45,12 +45,6 @@ exports.listen = (opts, fn) => { exports.ClientSocket = Socket; -/** - * Sprintf util. - */ - -require("s").extend(); - exports.createPartialDone = (done, count) => { let i = 0; return () => { diff --git a/test/engine.io.js b/test/engine.io.js index ffe7edddd..503462468 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -38,7 +38,7 @@ describe("engine", () => { return this.skip(); } listen(port => { - request.get("http://localhost:%d/".s(port), (err, res) => { + request.get(`http://localhost:${port}`, (err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(501); done(); @@ -67,9 +67,7 @@ describe("engine", () => { attach(server); server.listen(() => { - const uri = "http://localhost:%d/engine.io/default/".s( - server.address().port - ); + const uri = `http://localhost:${server.address().port}/engine.io/`; request.get(uri, (err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(400); @@ -234,13 +232,13 @@ describe("engine", () => { server.listen(() => { const port = server.address().port; request.get( - "http://localhost:%d/engine.io/default/".s(port), + `http://localhost:${port}/engine.io/default/`, (err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(0); expect(res.body.message).to.be("Transport unknown"); - request.get("http://localhost:%d/test".s(port), (err, res) => { + request.get(`http://localhost:${port}/test`, (err, res) => { expect(err).to.be(null); expect(res.status).to.be(200); expect(listeners).to.eql(2); diff --git a/test/server.js b/test/server.js index bc2f6a058..80086c84d 100644 --- a/test/server.js +++ b/test/server.js @@ -42,7 +42,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "tobi" }) // no tobi transport - outrageous .end((err, res) => { expect(err).to.be.an(Error); @@ -68,7 +68,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .set("Origin", "http://engine.io") .query({ transport: "constructor" }) .end((err, res) => { @@ -94,7 +94,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .set("Origin", "http://engine.io") .query({ transport: "polling", sid: "test" }) .end((err, res) => { @@ -126,7 +126,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .set("Origin", "http://engine.io") .query({ transport: "polling" }) .end((err, res) => { @@ -148,7 +148,7 @@ describe("server", () => { } }, port => { - const client = new ClientSocket("ws://localhost:%d".s(port), { + const client = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); client.on("error", () => { @@ -163,7 +163,7 @@ describe("server", () => { it("should send the io cookie", done => { listen({ cookie: true }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", EIO: 4 }) .end((err, res) => { expect(err).to.be(null); @@ -180,7 +180,7 @@ describe("server", () => { it("should send the io cookie custom name", done => { listen({ cookie: { name: "woot" } }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -196,7 +196,7 @@ describe("server", () => { it("should send the cookie with custom path", done => { listen({ cookie: { path: "/custom" } }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -212,7 +212,7 @@ describe("server", () => { it("should send the cookie with path=false", done => { listen({ cookie: { path: false } }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -228,7 +228,7 @@ describe("server", () => { it("should send the io cookie with httpOnly=true", done => { listen({ cookie: { httpOnly: true } }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -244,7 +244,7 @@ describe("server", () => { it("should send the io cookie with sameSite=strict", done => { listen({ cookie: { sameSite: "strict" } }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -260,7 +260,7 @@ describe("server", () => { it("should send the io cookie with httpOnly=false", done => { listen({ cookie: { httpOnly: false } }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -276,7 +276,7 @@ describe("server", () => { it("should send the io cookie with httpOnly not boolean", done => { listen({ cookie: { httpOnly: "no" } }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", b64: 1 }) .end((err, res) => { expect(err).to.be(null); @@ -292,7 +292,7 @@ describe("server", () => { it("should not send the io cookie", done => { listen({ cookie: false }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling" }) .end((err, res) => { expect(err).to.be(null); @@ -307,7 +307,7 @@ describe("server", () => { expect(Object.keys(engine.clients)).to.have.length(0); expect(engine.clientsCount).to.be(0); - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { expect(Object.keys(engine.clients)).to.have.length(1); expect(engine.clientsCount).to.be(1); @@ -325,7 +325,7 @@ describe("server", () => { engine.generateId = req => customId; - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.once("open", () => { expect(Object.keys(engine.clients)).to.have.length(1); expect(engine.clientsCount).to.be(1); @@ -342,7 +342,7 @@ describe("server", () => { engine.generateId = () => Promise.resolve(customId); - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.once("open", () => { expect(socket.id).to.be(customId); expect(engine.clients[customId].id).to.be(customId); @@ -367,7 +367,7 @@ describe("server", () => { partialDone(); }); - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("error", () => { partialDone(); }); @@ -393,7 +393,7 @@ describe("server", () => { partialDone(); }); - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); socket.on("error", () => { @@ -404,7 +404,7 @@ describe("server", () => { it("should exchange handshake data", done => { listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("handshake", obj => { expect(obj.sid).to.be.a("string"); expect(obj.pingTimeout).to.be.a("number"); @@ -416,7 +416,7 @@ describe("server", () => { it("should allow custom ping timeouts", done => { listen({ allowUpgrades: false, pingTimeout: 123 }, port => { - const socket = new ClientSocket("http://localhost:%d".s(port)); + const socket = new ClientSocket(`http://localhost:${port}`); socket.on("handshake", obj => { expect(obj.pingTimeout).to.be(123); done(); @@ -426,7 +426,7 @@ describe("server", () => { it("should trigger a connection event with a Socket", done => { const engine = listen({ allowUpgrades: false }, port => { - new ClientSocket("ws://localhost:%d".s(port)); + new ClientSocket(`ws://localhost:${port}`); engine.on("connection", socket => { expect(socket).to.be.an(Socket); done(); @@ -436,7 +436,7 @@ describe("server", () => { it("should open with polling by default", done => { const engine = listen({ allowUpgrades: false }, port => { - new ClientSocket("ws://localhost:%d".s(port)); + new ClientSocket(`ws://localhost:${port}`); engine.on("connection", socket => { expect(socket.transport.name).to.be("polling"); done(); @@ -446,7 +446,7 @@ describe("server", () => { it("should be able to open with ws directly", done => { const engine = listen({ transports: ["websocket"] }, port => { - new ClientSocket("ws://localhost:%d".s(port), { + new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); engine.on("connection", socket => { @@ -458,7 +458,7 @@ describe("server", () => { it("should not suggest any upgrades for websocket", done => { listen({ transports: ["websocket"] }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); socket.on("handshake", obj => { @@ -470,7 +470,7 @@ describe("server", () => { it("should not suggest upgrades when none are availble", done => { listen({ transports: ["polling"] }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), {}); + const socket = new ClientSocket(`ws://localhost:${port}`, {}); socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(0); done(); @@ -480,7 +480,7 @@ describe("server", () => { it("should only suggest available upgrades", done => { listen({ transports: ["polling"] }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), {}); + const socket = new ClientSocket(`ws://localhost:${port}`, {}); socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(0); done(); @@ -490,7 +490,7 @@ describe("server", () => { it("should suggest all upgrades when no transports are disabled", done => { listen({}, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), {}); + const socket = new ClientSocket(`ws://localhost:${port}`, {}); socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(1); expect(obj.upgrades).to.have.contain("websocket"); @@ -519,10 +519,10 @@ describe("server", () => { partialDone(); }); - var socket = new ClientSocket("ws://localhost:%d".s(port)); + var socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .set({ connection: "close" }) .query({ transport: "websocket", sid: socket.id }) .end((err, res) => { @@ -541,7 +541,7 @@ describe("server", () => { it("should allow arbitrary data through query string", done => { const engine = listen({ allowUpgrades: false }, port => { - new ClientSocket("ws://localhost:%d".s(port), { query: { a: "b" } }); + new ClientSocket(`ws://localhost:${port}`, { query: { a: "b" } }); engine.on("connection", conn => { expect(conn.request._query).to.have.keys("transport", "a"); expect(conn.request._query.a).to.be("b"); @@ -552,7 +552,7 @@ describe("server", () => { it("should allow data through query string in uri", done => { const engine = listen({ allowUpgrades: false }, port => { - new ClientSocket("ws://localhost:%d?a=b&c=d".s(port)); + new ClientSocket(`ws://localhost:${port}?a=b&c=d`); engine.on("connection", conn => { expect(conn.request._query.EIO).to.be.a("string"); expect(conn.request._query.a).to.be("b"); @@ -584,7 +584,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .set("Origin", "http://engine.io") .query({ transport: "websocket" }) .end((err, res) => { @@ -628,7 +628,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .set("Origin", "http://engine.io/") .query({ transport: "websocket" }) .end((err, res) => { @@ -654,7 +654,7 @@ describe("server", () => { }); request - .options("http://localhost:%d/engine.io/".s(port)) + .options(`http://localhost:${port}/engine.io/`) .query({ transport: "polling" }) .end((err, res) => { expect(err).to.be.an(Error); @@ -686,7 +686,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", EIO: 3 }) .end((err, res) => { expect(err).to.be.an(Error); @@ -700,7 +700,7 @@ describe("server", () => { it("should send a packet along with the handshake", done => { listen({ initialPacket: "faster!" }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { socket.on("message", msg => { expect(msg).to.be("faster!"); @@ -715,7 +715,7 @@ describe("server", () => { it("should be able to access non-empty writeBuffer at closing (server)", done => { const opts = { allowUpgrades: false }; const engine = listen(opts, port => { - new ClientSocket("http://localhost:%d".s(port)); + new ClientSocket(`http://localhost:${port}`); engine.on("connection", conn => { conn.on("close", reason => { expect(conn.writeBuffer.length).to.be(1); @@ -733,7 +733,7 @@ describe("server", () => { it("should be able to access non-empty writeBuffer at closing (client)", done => { const opts = { allowUpgrades: false }; listen(opts, port => { - const socket = new ClientSocket("http://localhost:%d".s(port)); + const socket = new ClientSocket(`http://localhost:${port}`); socket.on("open", () => { socket.on("close", reason => { expect(socket.writeBuffer.length).to.be(1); @@ -751,7 +751,7 @@ describe("server", () => { it("should trigger on server if the client does not pong", done => { const opts = { allowUpgrades: false, pingInterval: 5, pingTimeout: 5 }; const engine = listen(opts, port => { - const socket = new ClientSocket("http://localhost:%d".s(port)); + const socket = new ClientSocket(`http://localhost:${port}`); socket.sendPacket = () => {}; engine.on("connection", conn => { conn.on("close", reason => { @@ -769,7 +769,7 @@ describe("server", () => { pingTimeout: 500 }; const engine = listen(opts, port => { - const socket = new ClientSocket("http://localhost:%d".s(port)); + const socket = new ClientSocket(`http://localhost:${port}`); engine.on("connection", conn => { conn.on("close", reason => { expect(reason).to.be("ping timeout"); @@ -788,7 +788,7 @@ describe("server", () => { it("should trigger on client if server does not meet ping timeout", done => { const opts = { allowUpgrades: false, pingInterval: 50, pingTimeout: 30 }; listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { // override onPacket and Transport#onClose to simulate an inactive server after handshake socket.transport.removeListener("packet"); @@ -804,7 +804,7 @@ describe("server", () => { it("should trigger on both ends upon ping timeout", done => { const opts = { allowUpgrades: false, pingTimeout: 50, pingInterval: 50 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let total = 2; function onClose(reason, err) { @@ -828,7 +828,7 @@ describe("server", () => { it("should trigger when server closes a client", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let total = 2; engine.on("connection", conn => { @@ -853,7 +853,7 @@ describe("server", () => { it("should trigger when server closes a client (ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); let total = 2; @@ -886,7 +886,7 @@ describe("server", () => { engine.httpServer.close(); engine.httpServer.listen(port); - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); @@ -905,7 +905,7 @@ describe("server", () => { it("should trigger when client closes", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let total = 2; engine.on("connection", conn => { @@ -931,7 +931,7 @@ describe("server", () => { it("should trigger when client closes (ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); let total = 2; @@ -958,7 +958,7 @@ describe("server", () => { it("should trigger when calling socket.close() in payload", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.send(null, () => { @@ -986,7 +986,7 @@ describe("server", () => { it("should abort upgrade if socket is closed (GH-35)", done => { listen({ allowUpgrades: true }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { socket.close(); // we wait until complete to see if we get an uncaught EPIPE @@ -1040,7 +1040,7 @@ describe("server", () => { $done(); } - var socket = new ClientSocket("ws://localhost:%d".s(port)); + var socket = new ClientSocket(`ws://localhost:${port}`); let serverSocket; engine.on("connection", s => { @@ -1099,7 +1099,7 @@ describe("server", () => { }); }); - var socket = new ClientSocket("ws://localhost:%d".s(port)); + var socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { socket.send("test"); }); @@ -1122,7 +1122,7 @@ describe("server", () => { pingTimeout: 100 }; listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let clientCloseReason = null; socket.on("handshake", () => { @@ -1154,7 +1154,7 @@ describe("server", () => { pingTimeout: 50 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let clientCloseReason = null; engine.on("connection", conn => { @@ -1189,7 +1189,7 @@ describe("server", () => { pingTimeout: 50 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let clientCloseReason = null; socket.on("open", () => { @@ -1224,7 +1224,7 @@ describe("server", () => { pingTimeout: 100 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let clientCloseReason = null; socket.on("open", () => { @@ -1262,7 +1262,7 @@ describe("server", () => { pingTimeout: 100 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let clientCloseReason = null; socket.on("open", () => { @@ -1298,7 +1298,7 @@ describe("server", () => { return this.skip(); } const engine = listen({ transports: ["polling"] }, port => { - const socket = new ClientSocket("http://localhost:%d".s(port)); + const socket = new ClientSocket(`http://localhost:${port}`); engine.on("connection", conn => { const onDataRequest = conn.transport.onDataRequest; @@ -1322,7 +1322,7 @@ describe("server", () => { it("should trigger transport close before open for ws", done => { const opts = { transports: ["websocket"] }; listen(opts, port => { - const url = "ws://%s:%d".s("0.0.0.0", port); + const url = `ws://0.0.0.0:${port}`; const socket = new ClientSocket(url); socket.on("open", () => { done(new Error("Test invalidation")); @@ -1339,7 +1339,7 @@ describe("server", () => { it("should trigger transport close before open for xhr", done => { const opts = { transports: ["polling"] }; listen(opts, port => { - const socket = new ClientSocket("http://invalidserver:%d".s(port)); + const socket = new ClientSocket(`http://invalidserver:${port}`); socket.on("open", () => { done(new Error("Test invalidation")); }); @@ -1355,7 +1355,7 @@ describe("server", () => { it("should trigger force close before open for ws", done => { const opts = { transports: ["websocket"] }; listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { done(new Error("Test invalidation")); }); @@ -1372,7 +1372,7 @@ describe("server", () => { it("should trigger force close before open for xhr", done => { const opts = { transports: ["polling"] }; listen(opts, port => { - const socket = new ClientSocket("http://localhost:%d".s(port)); + const socket = new ClientSocket(`http://localhost:${port}`); socket.on("open", () => { done(new Error("Test invalidation")); }); @@ -1395,7 +1395,7 @@ describe("server", () => { engine.on("connection", conn => { conn.transport.on("close", done); }); - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); // override to simulate an inactive client @@ -1414,7 +1414,7 @@ describe("server", () => { engine.on("connection", conn => { conn.transport.on("close", done); }); - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); // override to simulate an inactive client @@ -1428,7 +1428,7 @@ describe("server", () => { engine.on("connection", conn => { conn.transport.on("close", done); }); - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); socket.on("open", () => { @@ -1444,7 +1444,7 @@ describe("server", () => { conn.transport.closeTimeout = 100; conn.transport.on("close", done); }); - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); socket.on("open", () => { @@ -1461,7 +1461,7 @@ describe("server", () => { conn.close(); }); }); - new ClientSocket("ws://localhost:%d".s(port)); + new ClientSocket(`ws://localhost:${port}`); }); }); @@ -1473,7 +1473,7 @@ describe("server", () => { transport.on("close", done); }); }); - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("upgrading", transport => { // override not to complete upgrading transport.send = () => {}; @@ -1484,7 +1484,7 @@ describe("server", () => { it("should not timeout after an upgrade", done => { const opts = { pingInterval: 200, pingTimeout: 20 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { setTimeout(() => { socket.removeListener("close"); @@ -1502,7 +1502,7 @@ describe("server", () => { it("should not crash when messing with Object prototype", done => { Object.prototype.foo = "bar"; // eslint-disable-line no-extend-native const engine = listen({ allowUpgrades: true }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { engine.close(); setTimeout(() => { @@ -1544,7 +1544,7 @@ describe("server", () => { it("should arrive from server to client", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.send("a"); }); @@ -1559,7 +1559,7 @@ describe("server", () => { it("should arrive from server to client (multiple)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); const expected = ["a", "b", "c"]; let i = 0; @@ -1601,7 +1601,7 @@ describe("server", () => { maxHttpBufferSize: 5 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.on("message", msg => { done( @@ -1621,7 +1621,7 @@ describe("server", () => { it("should not be receiving data when getting a message longer than maxHttpBufferSize (websocket)", done => { const opts = { maxHttpBufferSize: 5 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); engine.on("connection", conn => { @@ -1647,7 +1647,7 @@ describe("server", () => { maxHttpBufferSize: 5 }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.on("message", msg => { expect(msg).to.be("a"); @@ -1663,7 +1663,7 @@ describe("server", () => { it("should arrive from server to client (ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); engine.on("connection", conn => { @@ -1681,7 +1681,7 @@ describe("server", () => { it("should arrive from server to client (multiple, ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); const expected = ["a", "b", "c"]; @@ -1715,7 +1715,7 @@ describe("server", () => { it("should arrive from server to client (multiple, no delay, ws)", done => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); const expected = ["a", "b", "c"]; @@ -1752,7 +1752,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); @@ -1780,7 +1780,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); @@ -1810,7 +1810,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); @@ -1840,7 +1840,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); @@ -1868,7 +1868,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["polling"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); @@ -1897,7 +1897,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["websocket"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); socket.binaryType = "arraybuffer"; @@ -1928,7 +1928,7 @@ describe("server", () => { const opts = { allowUpgrades: false, transports: ["polling"] }; const engine = listen(opts, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); socket.binaryType = "arraybuffer"; @@ -1979,7 +1979,7 @@ describe("server", () => { socket.send("aaaa"); }); - new ClientSocket("ws://localhost:%d".s(port)); + new ClientSocket(`ws://localhost:${port}`); }); }); @@ -2006,7 +2006,7 @@ describe("server", () => { engine.on("connection", conn => { connection = conn; }); - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); socket.on("open", () => { @@ -2028,7 +2028,7 @@ describe("server", () => { it("should support chinese", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); const shi = "石室詩士施氏,嗜獅,誓食十獅。"; const shi2 = "氏時時適市視獅。"; engine.on("connection", conn => { @@ -2095,7 +2095,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new ClientSocket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket(`https://localhost:${port}`, opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2140,7 +2140,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new ClientSocket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket(`https://localhost:${port}`, opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2187,7 +2187,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new ClientSocket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket(`https://localhost:${port}`, opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2233,7 +2233,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new ClientSocket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket(`https://localhost:${port}`, opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2279,7 +2279,7 @@ describe("server", () => { engine.attach(srv); srv.listen(() => { const port = srv.address().port; - const socket = new ClientSocket("https://localhost:%d".s(port), opts); + const socket = new ClientSocket(`https://localhost:${port}`, opts); engine.on("connection", conn => { conn.on("message", msg => { @@ -2299,7 +2299,7 @@ describe("server", () => { describe("writeBuffer", () => { it("should not empty until `drain` event (polling)", done => { listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); let totalEvents = 2; @@ -2318,7 +2318,7 @@ describe("server", () => { it("should not empty until `drain` event (websocket)", done => { listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); let totalEvents = 2; @@ -2339,7 +2339,7 @@ describe("server", () => { describe("callback", () => { it("should execute in order when message sent (client) (polling)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); let i = 0; @@ -2378,7 +2378,7 @@ describe("server", () => { it("should execute in order when message sent (client) (websocket)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); let i = 0; @@ -2417,7 +2417,7 @@ describe("server", () => { it("should execute in order with payloads (client) (polling)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); let i = 0; @@ -2462,7 +2462,7 @@ describe("server", () => { it("should execute in order with payloads (client) (websocket)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); let i = 0; @@ -2507,7 +2507,7 @@ describe("server", () => { it("should execute when message sent (polling)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); let i = 0; @@ -2533,7 +2533,7 @@ describe("server", () => { it("should execute when message sent (websocket)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); let i = 0; @@ -2560,7 +2560,7 @@ describe("server", () => { it("should execute once for each send", done => { const engine = listen(port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let a = 0; let b = 0; let c = 0; @@ -2591,7 +2591,7 @@ describe("server", () => { it("should execute in multipart packet", done => { const engine = listen(port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); let i = 0; let j = 0; @@ -2619,7 +2619,7 @@ describe("server", () => { it("should execute in multipart packet (polling)", done => { const engine = listen(port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); let i = 0; @@ -2657,7 +2657,7 @@ describe("server", () => { it("should clean callback references when socket gets closed with pending callbacks", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); @@ -2686,7 +2686,7 @@ describe("server", () => { it("should not execute when it is not actually sent (polling)", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); @@ -2711,7 +2711,7 @@ describe("server", () => { describe("pre-encoded content", () => { it("should use the pre-encoded content", done => { engine = listen(port => { - client = new ClientSocket("ws://localhost:%d".s(port), { + client = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); @@ -2733,7 +2733,7 @@ describe("server", () => { describe("packet", () => { it("should emit when socket receives packet", done => { const engine = listen({ allowUpgrades: false }, port => { - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.on("packet", packet => { expect(packet.type).to.be("message"); @@ -2749,7 +2749,7 @@ describe("server", () => { it("should emit when receives pong", done => { const engine = listen({ allowUpgrades: false, pingInterval: 4 }, port => { - new ClientSocket("ws://localhost:%d".s(port)); + new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.on("packet", packet => { conn.close(); @@ -2768,7 +2768,7 @@ describe("server", () => { describe("packetCreate", () => { it("should emit before socket send message", done => { const engine = listen({ allowUpgrades: false }, port => { - new ClientSocket("ws://localhost:%d".s(port)); + new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.on("packetCreate", packet => { expect(packet.type).to.be("message"); @@ -2782,7 +2782,7 @@ describe("server", () => { it("should emit before send pong", done => { const engine = listen({ allowUpgrades: false, pingInterval: 4 }, port => { - new ClientSocket("ws://localhost:%d".s(port)); + new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.on("packetCreate", packet => { conn.close(); @@ -2850,7 +2850,7 @@ describe("server", () => { }); // client - var socket = new ClientSocket("ws://localhost:%d".s(port)); + var socket = new ClientSocket(`ws://localhost:${port}`); socket.on("open", () => { let lastSent = 0; let lastReceived = 0; @@ -3135,7 +3135,7 @@ describe("server", () => { for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf, { compress: true }); }); - new ClientSocket("http://localhost:%d".s(port), { + new ClientSocket(`http://localhost:${port}`, { transports: ["websocket"] }); } @@ -3165,7 +3165,7 @@ describe("server", () => { for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf, { compress: true }); }); - new ClientSocket("http://localhost:%d".s(port), { + new ClientSocket(`http://localhost:${port}`, { transports: ["websocket"] }); } @@ -3184,7 +3184,7 @@ describe("server", () => { function testForTransport(transport, done) { const engine = listen(port => { - const socket = new ClientSocket("ws://localhost:%d".s(port), { + const socket = new ClientSocket(`ws://localhost:${port}`, { extraHeaders: headers, transports: [transport] }); @@ -3218,7 +3218,7 @@ describe("server", () => { }); conn.send("hi"); }); - new ClientSocket("ws://localhost:%d".s(port), { + new ClientSocket(`ws://localhost:${port}`, { extraHeaders: headers, transports: ["polling"] }); @@ -3253,7 +3253,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling" }) .end((err, res) => { expect(err).to.be(null); @@ -3265,7 +3265,7 @@ describe("server", () => { const sid = JSON.parse(res.text.substring(4)).sid; request - .post("http://localhost:%d/engine.io/".s(port)) + .post(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", sid }) .send("1:6") .end((err, res) => { @@ -3290,7 +3290,7 @@ describe("server", () => { }); request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling" }) .end((err, res) => { expect(err).to.be(null); @@ -3302,7 +3302,7 @@ describe("server", () => { const sid = JSON.parse(res.text.substring(4)).sid; request - .post("http://localhost:%d/engine.io/".s(port)) + .post(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", sid }) .send("1:6") .end((err, res) => { @@ -3333,7 +3333,7 @@ describe("server", () => { partialDone(); }); - client = new ClientSocket("ws://localhost:%d".s(port), { + client = new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); @@ -3354,7 +3354,7 @@ describe("server", () => { partialDone(); }); - client = new ClientSocket("ws://localhost:%d".s(port)); + client = new ClientSocket(`ws://localhost:${port}`); client.on("upgrade", () => { partialDone(); @@ -3376,7 +3376,7 @@ describe("server", () => { partialDone(); }); - client = new ClientSocket("ws://localhost:%d".s(port)); + client = new ClientSocket(`ws://localhost:${port}`); client.on("upgrade", () => { partialDone(); @@ -3391,7 +3391,7 @@ describe("server", () => { { cors: { origin: true, headers: ["my-header"], credentials: true } }, port => { request - .options("http://localhost:%d/engine.io/".s(port)) + .options(`http://localhost:${port}/engine.io/`) .set("Origin", "http://engine.io") .query({ transport: "polling" }) .end((err, res) => { @@ -3421,7 +3421,7 @@ describe("server", () => { { cors: { origin: true, headers: ["my-header"], credentials: true } }, port => { request - .get("http://localhost:%d/engine.io/".s(port)) + .get(`http://localhost:${port}/engine.io/`) .set("Origin", "http://engine.io") .query({ transport: "polling" }) .end((err, res) => { @@ -3455,7 +3455,7 @@ describe("server", () => { }, port => { request - .options("http://localhost:%d/engine.io/".s(port)) + .options(`http://localhost:${port}/engine.io/`) .set("Origin", "http://bad-domain.com") .query({ transport: "polling" }) .end((err, res) => { @@ -3489,7 +3489,7 @@ describe("server", () => { }, port => { request - .options("http://localhost:%d/engine.io/".s(port)) + .options(`http://localhost:${port}/engine.io/`) .set("Origin", "http://good-domain.com") .query({ transport: "polling" }) .end((err, res) => { @@ -3531,7 +3531,7 @@ describe("server", () => { { allowUpgrades: false, wsEngine: require("eiows").Server }, port => { expect(engine.ws instanceof require("eiows").Server).to.be.ok(); - const socket = new ClientSocket("ws://localhost:%d".s(port)); + const socket = new ClientSocket(`ws://localhost:${port}`); engine.on("connection", conn => { conn.send("a"); }); @@ -3549,7 +3549,7 @@ describe("server", () => { describe("remoteAddress", () => { it("should be defined (polling)", done => { const engine = listen({ transports: ["polling"] }, port => { - new ClientSocket("ws://localhost:%d".s(port), { + new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); engine.on("connection", socket => { @@ -3567,7 +3567,7 @@ describe("server", () => { it("should be defined (ws)", done => { const engine = listen({ transports: ["websocket"] }, port => { - new ClientSocket("ws://localhost:%d".s(port), { + new ClientSocket(`ws://localhost:${port}`, { transports: ["websocket"] }); engine.on("connection", socket => { From 6d9f0c364a91b97d42bbebb188b1264331ab0e5a Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 8 Nov 2021 08:12:10 +0100 Subject: [PATCH 27/58] chore: bump mocha to version 9.1.3 The uWebSocket.js App has no close method for now, so we can't properly stop the engine at the end of the tests, hence the "--exit". --- package-lock.json | 638 ++++++++++++++++++++++++++++++++++++++++------ package.json | 12 +- 2 files changed, 572 insertions(+), 78 deletions(-) diff --git a/package-lock.json b/package-lock.json index 00d95efb2..d5b30ea83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -139,6 +139,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==" }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -154,6 +160,18 @@ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", "dev": true }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -163,6 +181,22 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", @@ -212,6 +246,12 @@ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", @@ -228,10 +268,25 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, "chalk": { @@ -245,6 +300,33 @@ "supports-color": "^5.3.0" } }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -269,12 +351,6 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -327,6 +403,12 @@ "ms": "2.1.2" } }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -334,9 +416,9 @@ "dev": true }, "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "eiows": { @@ -345,6 +427,12 @@ "integrity": "sha512-7cwZl/itR8NP6YXObVo583sxQcoKdcpeuRZqelnuqlQZvJH7oBSVvMRq8vZQkL4IXCXJPL8iC2M6haSawmtOsg==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "engine.io-client": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.0.0.tgz", @@ -363,9 +451,9 @@ } }, "engine.io-client-v3": { - "version": "npm:engine.io-client@3.5.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", - "integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", + "version": "npm:engine.io-client@3.5.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz", + "integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==", "dev": true, "requires": { "component-emitter": "~1.3.0", @@ -377,7 +465,7 @@ "parseqs": "0.0.6", "parseuri": "0.0.6", "ws": "~7.4.2", - "xmlhttprequest-ssl": "~1.5.4", + "xmlhttprequest-ssl": "~1.6.2", "yeast": "0.1.2" }, "dependencies": { @@ -416,9 +504,9 @@ "dev": true }, "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", + "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==", "dev": true } } @@ -438,6 +526,12 @@ } } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -501,6 +595,31 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "form-data": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", @@ -524,6 +643,19 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -538,6 +670,15 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -545,9 +686,9 @@ "dev": true }, "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "has-binary2": { @@ -580,9 +721,9 @@ "dev": true }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "indexof": { @@ -616,30 +757,163 @@ "loose-envify": "^1.0.0" } }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -684,36 +958,64 @@ } }, "mocha": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", - "dev": true, - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.3.1", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -725,39 +1027,24 @@ } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "^4.0.0" } } } @@ -767,11 +1054,23 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -786,6 +1085,24 @@ "wrappy": "1" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "parseqs": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", @@ -798,12 +1115,24 @@ "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", "dev": true }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, "prettier": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", @@ -822,6 +1151,15 @@ "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -837,6 +1175,21 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -852,12 +1205,32 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -867,6 +1240,21 @@ "safe-buffer": "~5.1.0" } }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "superagent": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", @@ -911,6 +1299,15 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -939,6 +1336,58 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -956,11 +1405,56 @@ "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", "dev": true }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index 1229d8ca0..ca17ce905 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,9 @@ "babel-eslint": "^8.0.2", "eiows": "^3.3.0", "engine.io-client": "6.0.0", - "engine.io-client-v3": "npm:engine.io-client@3.5.0", + "engine.io-client-v3": "npm:engine.io-client@3.5.2", "expect.js": "^0.3.1", - "mocha": "^4.0.1", + "mocha": "^9.1.3", "prettier": "^1.19.1", "rimraf": "^3.0.2", "superagent": "^3.8.1", @@ -58,10 +58,10 @@ "scripts": { "compile": "rimraf ./build && tsc", "test": "npm run compile && npm run format:check && npm run test:default && npm run test:compat-v3 && npm run test:eiows && npm run test:uws", - "test:default": "mocha --bail", - "test:compat-v3": "EIO_CLIENT=3 mocha", - "test:eiows": "EIO_WS_ENGINE=eiows mocha", - "test:uws": "EIO_WS_ENGINE=uws mocha", + "test:default": "mocha --bail --exit", + "test:compat-v3": "EIO_CLIENT=3 mocha --exit", + "test:eiows": "EIO_WS_ENGINE=eiows mocha --exit", + "test:uws": "EIO_WS_ENGINE=uws mocha --exit", "format:check": "prettier --check 'lib/**/*.ts' 'test/**/*.js'", "format:fix": "prettier --write 'lib/**/*.ts' 'test/**/*.js'", "prepack": "npm run compile" From 972de60ffb210e7b90f70189f375b6ac67ab46f7 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 8 Nov 2021 08:20:30 +0100 Subject: [PATCH 28/58] docs: add changelog for release 6.0.1 Merged from the 6.0.x branch. --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 876651fc4..dd897ea17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [6.0.1](https://github.com/socketio/engine.io/compare/6.0.0...6.0.1) (2021-11-06) + + +### Bug Fixes + +* fix payload encoding for v3 clients ([3f42262](https://github.com/socketio/engine.io/commit/3f42262fd27a77a7383cdbb44ede7c6211a9782b)) + + + # [6.0.0](https://github.com/socketio/engine.io/compare/5.2.0...6.0.0) (2021-10-08) The codebase was migrated to TypeScript ([c0d6eaa](https://github.com/socketio/engine.io/commit/c0d6eaa1ba1291946dc8425d5f533d5f721862dd)) From 4c306af9458d9caab36c7f8294d8c252b273b8dc Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 8 Nov 2021 08:37:22 +0100 Subject: [PATCH 29/58] chore(release): 6.1.0 Diff: https://github.com/socketio/engine.io/compare/6.0.0...6.1.0 --- CHANGELOG.md | 19 +++++++++++++++++++ package-lock.json | 16 ++++++++-------- package.json | 4 ++-- test/engine.io.js | 6 +++--- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd897ea17..57d5b6129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +# [6.1.0](https://github.com/socketio/engine.io/compare/6.0.0...6.1.0) (2021-11-08) + + +### Bug Fixes + +* fix payload encoding for v3 clients ([ed50fc3](https://github.com/socketio/engine.io/commit/ed50fc346b9c58459bf4e6fe5c45e8d34faac8da)) + + +### Features + +* add an implementation based on uWebSockets.js ([271e2df](https://github.com/socketio/engine.io/commit/271e2df94d39bbd13c33cab98cdd5915f9d28536)) + + +### Performance Improvements + +* refresh ping timer ([#628](https://github.com/socketio/engine.io/issues/628)) ([37474c7](https://github.com/socketio/engine.io/commit/37474c7e67be7c5f25f9ca2d4ea99f3a256bd2de)) + + + ## [6.0.1](https://github.com/socketio/engine.io/compare/6.0.0...6.0.1) (2021-11-06) diff --git a/package-lock.json b/package-lock.json index d5b30ea83..07c3c5570 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.0.0", + "version": "6.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -119,9 +119,9 @@ } }, "@socket.io/component-emitter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-2.0.0.tgz", - "integrity": "sha512-tfCc1aFpZQtnVXQhZDlwefCmT03U75O/NNd65X37U20r6vfERhwRBcZYANnFt0/GEU/Acb3Z1ZVeK+qbV32VJw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", + "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", "dev": true }, "@types/cookie": { @@ -434,12 +434,12 @@ "dev": true }, "engine.io-client": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.0.0.tgz", - "integrity": "sha512-74Nr22GETU/hD3t/WZX2exOlqX1HdxL3mqYsxd3dvgzK6jIJ+IcVvJRRUzH96P4ZEmjJV9uHiQykP1KyRZ0yDw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.0.tgz", + "integrity": "sha512-kNwjOjrU981utPZSzFbNU6lYda7B5xQs875x2DyCaalktt3ZSKc7DpCY18dml6JFzFH8dxsKAW0LMaGFMD7GmQ==", "dev": true, "requires": { - "@socket.io/component-emitter": "~2.0.0", + "@socket.io/component-emitter": "~3.0.0", "debug": "~4.3.1", "engine.io-parser": "~5.0.0", "has-cors": "1.1.0", diff --git a/package.json b/package.json index ca17ce905..72d5fbdc0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.0.0", + "version": "6.1.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "type": "commonjs", "main": "./build/engine.io.js", @@ -45,7 +45,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.3.0", - "engine.io-client": "6.0.0", + "engine.io-client": "6.1.0", "engine.io-client-v3": "npm:engine.io-client@3.5.2", "expect.js": "^0.3.1", "mocha": "^9.1.3", diff --git a/test/engine.io.js b/test/engine.io.js index 503462468..08e10ac53 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -14,9 +14,9 @@ describe("engine", () => { expect(protocol).to.be.a("number"); }); - it.skip("should be the same version as client", () => { - const version = require("../package").version; - expect(version).to.be(require("engine.io-client/package").version); + it("should be the same version as client", () => { + const version = require("../package.json").version; + expect(version).to.be(require("engine.io-client/package.json").version); }); describe("engine()", () => { From b04967b52eb1574cfaa6e24bdf164096322be96a Mon Sep 17 00:00:00 2001 From: Jeff Winder Date: Tue, 14 Dec 2021 09:09:23 +0100 Subject: [PATCH 30/58] refactor: import Node's setTimeout & clearTimeout to prevent ambiguity (#632) --- lib/socket.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/socket.ts b/lib/socket.ts index 6697ed45a..33619cd41 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -3,6 +3,7 @@ import debugModule from "debug"; import { IncomingMessage } from "http"; import { Transport } from "./transport"; import { Server } from "./server"; +import { setTimeout, clearTimeout } from "timers"; const debug = debugModule("engine:socket"); From c0e194d44933bd83bf9a4b126fca68ba7bf5098c Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 11 Jan 2022 15:34:18 +0100 Subject: [PATCH 31/58] fix: properly handle invalid data sent by a malicious websocket client **IMPORTANT SECURITY FIX** A malicious client could send a specially crafted HTTP request, triggering an uncaught exception and killing the Node.js process: > RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear > at Receiver.getInfo (/.../node_modules/ws/lib/receiver.js:176:14) > at Receiver.startLoop (/.../node_modules/ws/lib/receiver.js:136:22) > at Receiver._write (/.../node_modules/ws/lib/receiver.js:83:10) > at writeOrBuffer (internal/streams/writable.js:358:12) This bug was introduced by [1], included in `engine.io@4.0.0`, so previous releases are not impacted. [1]: https://github.com/socketio/engine.io/commit/f3c291fa613a9d50c924d74293035737fdace4f2 Thanks to Marcus Wejderot from Mevisio for the responsible disclosure. --- lib/server.ts | 3 --- test/server.js | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/lib/server.ts b/lib/server.ts index 78648dfc7..c09b2e1d6 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -609,9 +609,6 @@ export class Server extends BaseServer { client.maybeUpgrade(transport); } } else { - // transport error handling takes over - websocket.removeListener("error", onUpgradeError); - const closeConnection = (errorCode, errorContext) => abortUpgrade(socket, errorCode, errorContext); this.handshake(req._query.transport, req, closeConnection); diff --git a/test/server.js b/test/server.js index 80086c84d..05f5d7bf0 100644 --- a/test/server.js +++ b/test/server.js @@ -157,6 +157,46 @@ describe("server", () => { } ); }); + + it("should not throw when the client sends invalid data during the handshake (ws only)", done => { + listen(port => { + // will throw "RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear" + request + .get(`http://localhost:${port}/engine.io/`) + .set("connection", "upgrade") + .set("upgrade", "websocket") + .set("Sec-WebSocket-Version", "13") + .set("Sec-WebSocket-Key", "DXR4dX615eRds8nRmlhqtw==") + .query({ transport: "websocket", EIO: 4 }) + .send("test") + .end(() => {}); + + setTimeout(done, 50); + }); + }); + + it("should not throw when the client sends invalid data during the handshake (upgrade)", done => { + listen(port => { + request + .get(`http://localhost:${port}/engine.io/`) + .query({ transport: "polling", EIO: 4 }) + .end((err, res) => { + const sid = JSON.parse(res.text.substring(1)).sid; + + request + .get(`http://localhost:${port}/engine.io/`) + .set("connection", "upgrade") + .set("upgrade", "websocket") + .set("Sec-WebSocket-Version", "13") + .set("Sec-WebSocket-Key", "DXR4dX615eRds8nRmlhqtw==") + .query({ transport: "websocket", EIO: 4, sid }) + .send("test") + .end(() => {}); + + setTimeout(done, 50); + }); + }); + }); }); describe("handshake", () => { From f3b761dc560e6cd045ef42939dc28d7138f245e9 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 11 Jan 2022 16:08:20 +0100 Subject: [PATCH 32/58] chore(release): 6.1.1 Diff: https://github.com/socketio/engine.io/compare/6.1.0...6.1.1 --- CHANGELOG.md | 22 ++++++++++++++++++++++ package-lock.json | 2 +- package.json | 2 +- test/engine.io.js | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d5b6129..ed478dc9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## [6.1.1](https://github.com/socketio/engine.io/compare/6.1.0...6.1.1) (2022-01-11) + +:warning: This release contains an important security fix :warning: + +A malicious client could send a specially crafted HTTP request, triggering an uncaught exception and killing the Node.js process: + +> RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear +> at Receiver.getInfo (/.../node_modules/ws/lib/receiver.js:176:14) +> at Receiver.startLoop (/.../node_modules/ws/lib/receiver.js:136:22) +> at Receiver._write (/.../node_modules/ws/lib/receiver.js:83:10) +> at writeOrBuffer (internal/streams/writable.js:358:12) + +This bug was introduced by [this commit](https://github.com/socketio/engine.io/commit/f3c291fa613a9d50c924d74293035737fdace4f2), included in `engine.io@4.0.0`, so previous releases are not impacted. + +Thanks to Marcus Wejderot from Mevisio for the responsible disclosure. + +### Bug Fixes + +* properly handle invalid data sent by a malicious websocket client ([c0e194d](https://github.com/socketio/engine.io/commit/c0e194d44933bd83bf9a4b126fca68ba7bf5098c)) + + + # [6.1.0](https://github.com/socketio/engine.io/compare/6.0.0...6.1.0) (2021-11-08) diff --git a/package-lock.json b/package-lock.json index 07c3c5570..faf7be60a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.1.0", + "version": "6.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 72d5fbdc0..54a209908 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.1.0", + "version": "6.1.1", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "type": "commonjs", "main": "./build/engine.io.js", diff --git a/test/engine.io.js b/test/engine.io.js index 08e10ac53..a0abe7625 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -14,7 +14,7 @@ describe("engine", () => { expect(protocol).to.be.a("number"); }); - it("should be the same version as client", () => { + it.skip("should be the same version as client", () => { const version = require("../package.json").version; expect(version).to.be(require("engine.io-client/package.json").version); }); From a84595a04ef97c0e5fbb2f76f4fa813e5defbaa5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Jan 2022 07:59:43 +0100 Subject: [PATCH 33/58] chore(deps): bump engine.io from 4.0.0 to 4.1.2 in /examples/latency (#636) Bumps [engine.io](https://github.com/socketio/engine.io) from 4.0.0 to 4.1.2. - [Release notes](https://github.com/socketio/engine.io/releases) - [Changelog](https://github.com/socketio/engine.io/blob/4.1.2/CHANGELOG.md) - [Commits](https://github.com/socketio/engine.io/compare/4.0.0...4.1.2) --- updated-dependencies: - dependency-name: engine.io dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/latency/package-lock.json | 24 ++++++++++++------------ examples/latency/package.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/latency/package-lock.json b/examples/latency/package-lock.json index df4557f9a..059da8d7e 100644 --- a/examples/latency/package-lock.json +++ b/examples/latency/package-lock.json @@ -491,17 +491,17 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, "engine.io": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.0.tgz", - "integrity": "sha512-WyTa1NJR8rRmPXGXNSSgA+XhzfYLVcRBjRoFx7gI3cARnEsyuMpg0PS/PMDnPMMQtkjmVZsi2/ETrpq4mhoYSw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.2.tgz", + "integrity": "sha512-t5z6zjXuVLhXDMiFJPYsPOWEER8B0tIsD3ETgw19S1yg9zryvUfY3Vhtk3Gf4sihw/bQGIqQ//gjvVlu+Ca0bQ==", "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.4.1", "cors": "~2.8.5", - "debug": "~4.1.0", + "debug": "~4.3.1", "engine.io-parser": "~4.0.0", - "ws": "^7.1.2" + "ws": "~7.4.2" }, "dependencies": { "cookie": { @@ -510,11 +510,11 @@ "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -1523,9 +1523,9 @@ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" }, "xmlhttprequest-ssl": { "version": "1.5.5", diff --git a/examples/latency/package.json b/examples/latency/package.json index 2df714738..2f7c6d34a 100644 --- a/examples/latency/package.json +++ b/examples/latency/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "dependencies": { "enchilada": "0.13.0", - "engine.io": "^4.0.0", + "engine.io": "^4.1.2", "engine.io-client": "^4.0.0", "express": "^4.17.1", "smoothie": "1.19.0" From 8b4d6a8176db72f5c2420c5a45f0d97d33af049b Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 14 Jan 2022 08:13:33 +0100 Subject: [PATCH 34/58] fix(uws): handle invalid websocket upgrades When binding to an uWebSockets.js App, there was an unhandled case that could crash the server: ``` curl "http://localhost:3000/engine.io/?EIO=4&transport=websocket" ``` would result in: ``` Error: Returning from a request handler without responding or attaching an abort handler is forbidden! terminate called without an active exception ``` Note: this does not apply to the default server based on ws, because the error was caught elsewhere in the source code. Related: https://github.com/socketio/socket.io/issues/4250 --- lib/server.ts | 7 +++++++ lib/userver.ts | 4 ++++ test/server.js | 5 ----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/server.ts b/lib/server.ts index c09b2e1d6..44ce2ee6a 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -245,6 +245,13 @@ export abstract class BaseServer extends EventEmitter { }); } + if (transport === "websocket" && !upgrade) { + debug("invalid transport upgrade"); + return fn(Server.errors.BAD_REQUEST, { + name: "TRANSPORT_HANDSHAKE_ERROR" + }); + } + if (!this.opts.allowRequest) return fn(); return this.opts.allowRequest(req, (message, success) => { diff --git a/lib/userver.ts b/lib/userver.ts index 76c9a41d1..d28b5ff1c 100644 --- a/lib/userver.ts +++ b/lib/userver.ts @@ -28,6 +28,10 @@ export class uServer extends BaseServer { req.connection = { remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString() }; + + res.onAborted(() => { + debug("response has been aborted"); + }); } protected createTransport(transportName, req) { diff --git a/test/server.js b/test/server.js index 05f5d7bf0..3aff7c0ec 100644 --- a/test/server.js +++ b/test/server.js @@ -603,9 +603,6 @@ describe("server", () => { }); it("should disallow bad requests (handshake error)", function(done) { - if (process.env.EIO_WS_ENGINE === "uws") { - return this.skip(); - } const partialDone = createPartialDone(done, 2); engine = listen( @@ -618,8 +615,6 @@ describe("server", () => { expect(err.code).to.be(3); expect(err.message).to.be("Bad request"); expect(err.context.name).to.be("TRANSPORT_HANDSHAKE_ERROR"); - expect(err.context.error).to.be.an(Error); - expect(err.context.error.name).to.be("TypeError"); partialDone(); }); From 49bb7cf66518d4b49baf883a16ee1fe1ed8aed28 Mon Sep 17 00:00:00 2001 From: Yosi Attias Date: Sun, 9 Jan 2022 19:52:29 +0200 Subject: [PATCH 35/58] fix(uws): expose additional uWebSockets.js options (#634) You can now pass additional options: ```js const { App } = require("uWebSockets.js"); const { uServer } = require("engine.io"); const app = new App(); const server = new uServer(); server.attach(app, { compression: uWS.DEDICATED_COMPRESSOR_128KB, // defaults to none idleTimeout: 60, // defaults to 120 maxBackpressure: 8 * 1024 // defaults to 1024 * 1024 }); app.listen(3000); ``` Related: https://github.com/socketio/engine.io/issues/633 --- lib/userver.ts | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/userver.ts b/lib/userver.ts index d28b5ff1c..38c9b5a81 100644 --- a/lib/userver.ts +++ b/lib/userver.ts @@ -1,10 +1,28 @@ import debugModule from "debug"; import { AttachOptions, BaseServer, Server } from "./server"; -import { HttpRequest, HttpResponse } from "uWebSockets.js"; +import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; import transports from "./transports-uws"; const debug = debugModule("engine:uws"); +export interface uOptions { + /** + * What permessage-deflate compression to use. uWS.DISABLED, uWS.SHARED_COMPRESSOR or any of the uWS.DEDICATED_COMPRESSOR_xxxKB. + * @default uWS.DISABLED + */ + compression?: number; + /** + * Maximum amount of seconds that may pass without sending or getting a message. Connection is closed if this timeout passes. Resolution (granularity) for timeouts are typically 4 seconds, rounded to closest. Disable by using 0. + * @default 120 + */ + idleTimeout?: number; + /** + * Maximum length of allowed backpressure per socket when publishing or sending messages. Slow receivers with too high backpressure will be skipped until they catch up or timeout. + * @default 1024 * 1024 + */ + maxBackpressure?: number; +} + export class uServer extends BaseServer { protected init() {} protected cleanup() {} @@ -43,13 +61,18 @@ export class uServer extends BaseServer { * @param app * @param options */ - public attach(app /* : TemplatedApp */, options: AttachOptions = {}) { + public attach( + app /* : TemplatedApp */, + options: AttachOptions & uOptions = {} + ) { const path = (options.path || "/engine.io").replace(/\/$/, "") + "/"; - - app + (app as TemplatedApp) .any(path, this.handleRequest.bind(this)) // .ws(path, { + compression: options.compression, + idleTimeout: options.idleTimeout, + maxBackpressure: options.maxBackpressure, maxPayloadLength: this.opts.maxHttpBufferSize, upgrade: this.handleUpgrade.bind(this), open: ws => { From 45112a30d1af4cc25b21a5d658a748583cb64ed4 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 18 Jan 2022 17:03:03 +0100 Subject: [PATCH 36/58] fix(uws): fix HTTP long-polling with CORS When binding to an uWebSockets.js application, the server could crash with the following error: ``` TypeError: res.onData is not a function at Polling.onDataRequest (build/transports-uws/polling.js:133:13) at Polling.onRequest (build/transports-uws/polling.js:47:18) at callback (build/userver.js:80:56) ``` Related: https://github.com/socketio/engine.io/issues/637 --- lib/userver.ts | 4 ++++ test/server.js | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/userver.ts b/lib/userver.ts index 38c9b5a81..66fa40414 100644 --- a/lib/userver.ts +++ b/lib/userver.ts @@ -275,6 +275,10 @@ class ResponseWrapper { this.res.end(data); } + public onData(fn) { + this.res.onData(fn); + } + public onAborted(fn) { this.res.onAborted(fn); } diff --git a/test/server.js b/test/server.js index 3aff7c0ec..d7d64428b 100644 --- a/test/server.js +++ b/test/server.js @@ -3552,6 +3552,31 @@ describe("server", () => { } ); }); + + it("should work with CORS enabled", done => { + engine = listen( + { cors: { origin: true, headers: ["my-header"], credentials: true } }, + port => { + const client = new ClientSocket(`ws://localhost:${port}`, { + transports: ["polling"] + }); + engine.on("connection", socket => { + socket.on("message", msg => { + expect(msg).to.be("hey"); + socket.send("holà"); + }); + }); + client.on("open", () => { + client.send("hey"); + }); + client.on("message", msg => { + expect(msg).to.be("holà"); + client.close(); + done(); + }); + } + ); + }); }); describe("wsEngine option", () => { From 3f1e312a2c3ba3550f446a0486c5dad5a6ff2065 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 18 Jan 2022 17:09:36 +0100 Subject: [PATCH 37/58] chore: bump package-lock.json file version Note to future self: URL for dependencies from GitHub must use "git+https" instead of "git+ssh" to make the CI pass, else you will encounter the following error: ``` npm ERR! Error while executing: npm ERR! /usr/bin/git ls-remote -h -t ssh://git@github.com/uNetworking/uWebSockets.js.git npm ERR! npm ERR! Warning: Permanently added the RSA host key for IP address '140.82.112.3' to the list of known hosts. npm ERR! git@github.com: Permission denied (publickey). npm ERR! fatal: Could not read from remote repository. npm ERR! npm ERR! Please make sure you have the correct access rights npm ERR! and the repository exists. npm ERR! npm ERR! exited with error code: 128 ``` --- package-lock.json | 1976 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1959 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index faf7be60a..dedb25386 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,1948 @@ { "name": "engine.io", "version": "6.1.1", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "engine.io", + "version": "6.1.1", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "ws": "~8.2.3" + }, + "devDependencies": { + "babel-eslint": "^8.0.2", + "eiows": "^3.3.0", + "engine.io-client": "6.1.0", + "engine.io-client-v3": "npm:engine.io-client@3.5.2", + "expect.js": "^0.3.1", + "mocha": "^9.1.3", + "prettier": "^1.19.1", + "rimraf": "^3.0.2", + "superagent": "^3.8.1", + "typescript": "^4.4.3", + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", + "integrity": "sha512-cuAuTTIQ9RqcFRJ/Y8PvTh+paepNcaGxwQwjIDRWPXmzzyAeCO4KqS9ikMvq0MCbRk6GlYKwfzStrcP3/jSL8g==", + "dev": true, + "dependencies": { + "@babel/highlight": "7.0.0-beta.44" + } + }, + "node_modules/@babel/generator": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz", + "integrity": "sha512-5xVb7hlhjGcdkKpMXgicAVgx8syK5VJz193k0i/0sLP6DzE6lRrU1K3B/rFefgdo9LPGMAOOOAWW4jycj07ShQ==", + "dev": true, + "dependencies": { + "@babel/types": "7.0.0-beta.44", + "jsesc": "^2.5.1", + "lodash": "^4.2.0", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz", + "integrity": "sha512-MHRG2qZMKMFaBavX0LWpfZ2e+hLloT++N7rfM3DYOMUOGCD8cVjqZpwiL8a0bOX3IYcQev1ruciT0gdFFRTxzg==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "7.0.0-beta.44", + "@babel/template": "7.0.0-beta.44", + "@babel/types": "7.0.0-beta.44" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz", + "integrity": "sha512-w0YjWVwrM2HwP6/H3sEgrSQdkCaxppqFeJtAnB23pRiJB5E/O9Yp7JAAeWBl+gGEgmBFinnTyOv2RN7rcSmMiw==", + "dev": true, + "dependencies": { + "@babel/types": "7.0.0-beta.44" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz", + "integrity": "sha512-aQ7QowtkgKKzPGf0j6u77kBMdUFVBKNHw2p/3HX/POt5/oz8ec5cs0GwlgM8Hz7ui5EwJnzyfRmkNF1Nx1N7aA==", + "dev": true, + "dependencies": { + "@babel/types": "7.0.0-beta.44" + } + }, + "node_modules/@babel/highlight": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz", + "integrity": "sha512-Il19yJvy7vMFm8AVAh6OZzaFoAd0hbkeMZiX3P5HGD+z7dyI7RzndHB0dg6Urh/VAFfHtpOIzDUSxmY6coyZWQ==", + "dev": true, + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz", + "integrity": "sha512-w750Sloq0UNifLx1rUqwfbnC6uSUk0mfwwgGRfdLiaUzfAOiH0tHJE6ILQIUi3KYkjiCDTskoIsnfqZvWLBDng==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.44", + "@babel/types": "7.0.0-beta.44", + "babylon": "7.0.0-beta.44", + "lodash": "^4.2.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz", + "integrity": "sha512-UHuDz8ukQkJCDASKHf+oDt3FVUzFd+QYfuBIsiNu/4+/ix6pP/C+uQZJ6K1oEfbCMv/IKWbgDEh7fcsnIE5AtA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.44", + "@babel/generator": "7.0.0-beta.44", + "@babel/helper-function-name": "7.0.0-beta.44", + "@babel/helper-split-export-declaration": "7.0.0-beta.44", + "@babel/types": "7.0.0-beta.44", + "babylon": "7.0.0-beta.44", + "debug": "^3.1.0", + "globals": "^11.1.0", + "invariant": "^2.2.0", + "lodash": "^4.2.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@babel/types": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz", + "integrity": "sha512-5eTV4WRmqbaFM3v9gHAIljEQJU4Ssc6fxL61JN+Oe2ga/BwyjzjamwkCVVAQjHGuAX8i0BWo42dshL8eO5KfLQ==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.2.0", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", + "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", + "dev": true + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "node_modules/@types/node": { + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==" + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/babel-eslint": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.6.tgz", + "integrity": "sha512-aCdHjhzcILdP8c9lej7hvXKvQieyRt20SF102SIGyY4cUIiw6UaAtK4j2o3dXX74jEmy0TJ0CEhv4fTIM3SzcA==", + "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.44", + "@babel/traverse": "7.0.0-beta.44", + "@babel/types": "7.0.0-beta.44", + "babylon": "7.0.0-beta.44", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/babylon": { + "version": "7.0.0-beta.44", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", + "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", + "dev": true, + "bin": { + "babylon": "bin/babylon.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/eiows": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.3.2.tgz", + "integrity": "sha512-7cwZl/itR8NP6YXObVo583sxQcoKdcpeuRZqelnuqlQZvJH7oBSVvMRq8vZQkL4IXCXJPL8iC2M6haSawmtOsg==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=8.12.0 <15.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/engine.io-client": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.0.tgz", + "integrity": "sha512-kNwjOjrU981utPZSzFbNU6lYda7B5xQs875x2DyCaalktt3ZSKc7DpCY18dml6JFzFH8dxsKAW0LMaGFMD7GmQ==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.0.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0", + "yeast": "0.1.2" + } + }, + "node_modules/engine.io-client-v3": { + "name": "engine.io-client", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz", + "integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==", + "dev": true, + "dependencies": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.6.2", + "yeast": "0.1.2" + } + }, + "node_modules/engine.io-client-v3/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/engine.io-client-v3/node_modules/engine.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", + "dev": true, + "dependencies": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "node_modules/engine.io-client-v3/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/engine.io-client-v3/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-client-v3/node_modules/xmlhttprequest-ssl": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", + "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.0.tgz", + "integrity": "sha512-wn6QavHEqXoM+cg+x8uUG7GhxLBCfKEKNEsCNc7V2ugj3gB3lK91l1MuZiy6xFB2V9D1eew0aWkmpiT/aBb/KA==", + "dependencies": { + "base64-arraybuffer": "~1.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser/node_modules/base64-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", + "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect.js": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz", + "integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "dev": true, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dev": true, + "dependencies": { + "isarray": "2.0.1" + } + }, + "node_modules/has-binary2/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + }, + "node_modules/has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", + "dev": true + }, + "node_modules/parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . Thanks to @shadowgate15, @spence-s, and @niftylettuce. Superagent is sponsored by Forward Email at .", + "dev": true, + "dependencies": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/uWebSockets.js": { + "version": "20.0.0", + "resolved": "git+https://git@github.com/uNetworking/uWebSockets.js.git#4558ee00f9f1f686fffe1accbfc2e85b1af9c50f", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.0.0-beta.44", @@ -501,7 +2441,8 @@ "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true + "dev": true, + "requires": {} }, "xmlhttprequest-ssl": { "version": "1.6.3", @@ -1220,6 +3161,15 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1231,15 +3181,6 @@ "strip-ansi": "^6.0.1" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -1320,17 +3261,17 @@ "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, - "uWebSockets.js": { - "version": "github:uNetworking/uWebSockets.js#4558ee00f9f1f686fffe1accbfc2e85b1af9c50f", - "from": "github:uNetworking/uWebSockets.js#v20.0.0", - "dev": true - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "uWebSockets.js": { + "version": "git+https://git@github.com/uNetworking/uWebSockets.js.git#4558ee00f9f1f686fffe1accbfc2e85b1af9c50f", + "dev": true, + "from": "uWebSockets.js@uNetworking/uWebSockets.js#v20.0.0" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1397,7 +3338,8 @@ "ws": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} }, "xmlhttprequest-ssl": { "version": "2.0.0", From e122e4be7bc6b131552dba4dd4581a2ffb3797c6 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 18 Jan 2022 17:55:55 +0100 Subject: [PATCH 38/58] refactor: add additional types Merged from https://github.com/socketio/engine.io/pull/630 --- lib/socket.ts | 22 +++++++++++++--------- lib/transport.ts | 3 ++- package-lock.json | 46 ++++++++++++++++++++++------------------------ 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/lib/socket.ts b/lib/socket.ts index 33619cd41..c3a816193 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -4,6 +4,7 @@ import { IncomingMessage } from "http"; import { Transport } from "./transport"; import { Server } from "./server"; import { setTimeout, clearTimeout } from "timers"; +import { Packet, PacketType, RawData } from "engine.io-parser"; const debug = debugModule("engine:socket"); @@ -18,7 +19,7 @@ export class Socket extends EventEmitter { private server: Server; private upgrading: boolean; private upgraded: boolean; - private writeBuffer: any[]; + private writeBuffer: Packet[]; private packetsFn: any[]; private sentCallbackFn: any[]; private cleanupFn: any[]; @@ -116,7 +117,7 @@ export class Socket extends EventEmitter { * @param {Object} packet * @api private */ - private onPacket(packet) { + private onPacket(packet: Packet) { if ("open" !== this.readyState) { return debug("packet received with closed socket"); } @@ -417,7 +418,7 @@ export class Socket extends EventEmitter { /** * Sends a message packet. * - * @param {String} message + * @param {Object} data * @param {Object} options * @param {Function} callback * @return {Socket} for chaining @@ -436,12 +437,14 @@ export class Socket extends EventEmitter { /** * Sends a packet. * - * @param {String} packet type - * @param {String} optional, data + * @param {String} type - packet type + * @param {String} data * @param {Object} options + * @param {Function} callback + * * @api private */ - private sendPacket(type, data?, options?, callback?) { + private sendPacket(type: PacketType, data?: RawData, options?, callback?) { if ("function" === typeof options) { callback = options; options = null; @@ -453,10 +456,11 @@ export class Socket extends EventEmitter { if ("closing" !== this.readyState && "closed" !== this.readyState) { debug('sending packet "%s" (%s)', type, data); - const packet: any = { - type: type, - options: options + const packet: Packet = { + type, + options }; + if (data) packet.data = data; // exports packetCreate event diff --git a/lib/transport.ts b/lib/transport.ts index 53d8ad023..70bc9cfa3 100644 --- a/lib/transport.ts +++ b/lib/transport.ts @@ -3,6 +3,7 @@ import * as parser_v4 from "engine.io-parser"; import * as parser_v3 from "./parser-v3/index"; import debugModule from "debug"; import { IncomingMessage } from "http"; +import { Packet } from "engine.io-parser"; const debug = debugModule("engine:transport"); @@ -111,7 +112,7 @@ export abstract class Transport extends EventEmitter { * @param {Object} packet * @api protected */ - protected onPacket(packet) { + protected onPacket(packet: Packet) { this.emit("packet", packet); } diff --git a/package-lock.json b/package-lock.json index dedb25386..be7d844b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -150,6 +150,14 @@ "to-fast-properties": "^2.0.0" } }, + "node_modules/@socket.io/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", @@ -645,24 +653,16 @@ } }, "node_modules/engine.io-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.0.tgz", - "integrity": "sha512-wn6QavHEqXoM+cg+x8uUG7GhxLBCfKEKNEsCNc7V2ugj3gB3lK91l1MuZiy6xFB2V9D1eew0aWkmpiT/aBb/KA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", + "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", "dependencies": { - "base64-arraybuffer": "~1.0.1" + "@socket.io/base64-arraybuffer": "~1.0.2" }, "engines": { "node": ">=10.0.0" } }, - "node_modules/engine.io-parser/node_modules/base64-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", - "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2058,6 +2058,11 @@ "to-fast-properties": "^2.0.0" } }, + "@socket.io/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==" + }, "@socket.io/component-emitter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", @@ -2453,18 +2458,11 @@ } }, "engine.io-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.0.tgz", - "integrity": "sha512-wn6QavHEqXoM+cg+x8uUG7GhxLBCfKEKNEsCNc7V2ugj3gB3lK91l1MuZiy6xFB2V9D1eew0aWkmpiT/aBb/KA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", + "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", "requires": { - "base64-arraybuffer": "~1.0.1" - }, - "dependencies": { - "base64-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", - "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==" - } + "@socket.io/base64-arraybuffer": "~1.0.2" } }, "escalade": { @@ -3270,7 +3268,7 @@ "uWebSockets.js": { "version": "git+https://git@github.com/uNetworking/uWebSockets.js.git#4558ee00f9f1f686fffe1accbfc2e85b1af9c50f", "dev": true, - "from": "uWebSockets.js@uNetworking/uWebSockets.js#v20.0.0" + "from": "uWebSockets.js@github:uNetworking/uWebSockets.js#v20.0.0" }, "vary": { "version": "1.1.2", From 90fb0a99acd44fe424f14516d9575b09a0cfc582 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 18 Jan 2022 18:00:23 +0100 Subject: [PATCH 39/58] chore(release): 6.1.2 Diff: https://github.com/socketio/engine.io/compare/6.1.1...6.1.2 --- CHANGELOG.md | 11 +++++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed478dc9a..f72320937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [6.1.2](https://github.com/socketio/engine.io/compare/6.1.1...6.1.2) (2022-01-18) + + +### Bug Fixes + +* **uws:** expose additional uWebSockets.js options ([#634](https://github.com/socketio/engine.io/issues/634)) ([49bb7cf](https://github.com/socketio/engine.io/commit/49bb7cf66518d4b49baf883a16ee1fe1ed8aed28)) +* **uws:** fix HTTP long-polling with CORS ([45112a3](https://github.com/socketio/engine.io/commit/45112a30d1af4cc25b21a5d658a748583cb64ed4)) +* **uws:** handle invalid websocket upgrades ([8b4d6a8](https://github.com/socketio/engine.io/commit/8b4d6a8176db72f5c2420c5a45f0d97d33af049b)) + + + ## [6.1.1](https://github.com/socketio/engine.io/compare/6.1.0...6.1.1) (2022-01-11) :warning: This release contains an important security fix :warning: diff --git a/package-lock.json b/package-lock.json index be7d844b0..c93faa7bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.1.1", + "version": "6.1.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 54a209908..cac0c586a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.1.1", + "version": "6.1.2", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "type": "commonjs", "main": "./build/engine.io.js", From a463d268ed90064e7863679bda423951de108c36 Mon Sep 17 00:00:00 2001 From: Jeffrey van Norden Date: Thu, 17 Feb 2022 06:36:49 +0100 Subject: [PATCH 40/58] fix(typings): allow CorsOptionsDelegate as cors options (#641) Reference: https://www.npmjs.com/package/cors#configuring-cors-asynchronously Related: https://github.com/DefinitelyTyped/DefinitelyTyped/commit/54a59cd8f0bdbee257ada2716fdc1b7c94909c40 --- lib/server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server.ts b/lib/server.ts index 44ce2ee6a..739e38aa3 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -9,7 +9,7 @@ import { serialize } from "cookie"; import { Server as DEFAULT_WS_ENGINE } from "ws"; import { IncomingMessage, Server as HttpServer } from "http"; import { CookieSerializeOptions } from "cookie"; -import { CorsOptions } from "cors"; +import { CorsOptions, CorsOptionsDelegate } from "cors"; const debug = debugModule("engine"); @@ -105,7 +105,7 @@ export interface ServerOptions { /** * the options that will be forwarded to the cors module */ - cors?: CorsOptions; + cors?: CorsOptions | CorsOptionsDelegate; /** * whether to enable compatibility with Socket.IO v2 clients * @default false From 33674403084c329dc6ad026c4122333a6f8a9992 Mon Sep 17 00:00:00 2001 From: e3dio <85405955+e3dio@users.noreply.github.com> Date: Fri, 18 Feb 2022 10:14:44 -0700 Subject: [PATCH 41/58] fix(uws): properly handle chunked content (#642) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the engine based on µWebSockets.js (introduced in version 6.1.0), a huge request body split in multiple chunks would throw the following error: > node:buffer:254 > TypedArrayPrototypeSet(target, source, targetStart); > ^ > > TypeError: Cannot perform %TypedArray%.prototype.set on a detached ArrayBuffer > at Buffer.set () > at _copyActual (node:buffer:254:3) > node:buffer:254 > TypedArrayPrototypeSet(target, source, targetStart); > ^ > > TypeError: Cannot perform %TypedArray%.prototype.set on a detached ArrayBuffer > at Buffer.set () > at _copyActual (node:buffer:254:3) > at Function.concat (node:buffer:562:12) > at onEnd (.../node_modules/engine.io/build/transports-uws/polling.js:126:32) > at .../node_modules/engine.io/build/transports-uws/polling.js:143:17 Note: µWebSockets.js does not currently support chunked transfer encoding. --- lib/transports-uws/polling.ts | 58 ++++++++++++++++++++++++++-------- test/server.js | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/lib/transports-uws/polling.ts b/lib/transports-uws/polling.ts index 8d6b98279..57a1c4099 100644 --- a/lib/transports-uws/polling.ts +++ b/lib/transports-uws/polling.ts @@ -122,6 +122,20 @@ export class Polling extends Transport { return; } + const expectedContentLength = Number(req.headers["content-length"]); + + if (!expectedContentLength) { + this.onError("content-length header required"); + res.writeStatus("411 Length Required").end(); + return; + } + + if (expectedContentLength > this.maxHttpBufferSize) { + this.onError("payload too large"); + res.writeStatus("413 Payload Too Large").end(); + return; + } + const isBinary = "application/octet-stream" === req.headers["content-type"]; if (isBinary && this.protocol === 4) { @@ -131,11 +145,11 @@ export class Polling extends Transport { this.dataReq = req; this.dataRes = res; - let chunks = []; - let contentLength = 0; + let buffer; + let offset = 0; const cleanup = () => { - this.dataReq = this.dataRes = chunks = null; + this.dataReq = this.dataRes = null; }; const onClose = () => { @@ -154,8 +168,8 @@ export class Polling extends Transport { res.writeHeader(key, String(headers[key])); }); - const onEnd = () => { - this.onData(Buffer.concat(chunks).toString()); + const onEnd = buffer => { + this.onData(buffer.toString()); if (this.readyState !== "closing") { res.end("ok"); @@ -165,18 +179,36 @@ export class Polling extends Transport { res.onAborted(onClose); - res.onData((chunk, isLast) => { - chunks.push(Buffer.from(chunk)); - contentLength += Buffer.byteLength(chunk); - if (contentLength > this.maxHttpBufferSize) { - this.onError("payload too large"); - res.writeStatus("413 Payload Too Large"); - res.end(); + res.onData((arrayBuffer, isLast) => { + const totalLength = offset + arrayBuffer.byteLength; + if (totalLength > expectedContentLength) { + this.onError("content-length mismatch"); + res.close(); // calls onAborted return; } + + if (!buffer) { + if (isLast) { + onEnd(Buffer.from(arrayBuffer)); + return; + } + buffer = Buffer.allocUnsafe(expectedContentLength); + } + + Buffer.from(arrayBuffer).copy(buffer, offset); + if (isLast) { - onEnd(); + if (totalLength != expectedContentLength) { + this.onError("content-length mismatch"); + res.writeStatus("400 Content-Length Mismatch").end(); + cleanup(); + return; + } + onEnd(buffer); + return; } + + offset = totalLength; }); } diff --git a/test/server.js b/test/server.js index d7d64428b..fff27d58a 100644 --- a/test/server.js +++ b/test/server.js @@ -1955,6 +1955,65 @@ describe("server", () => { }); }); + it("should arrive when content is split in multiple chunks (polling)", done => { + const engine = listen( + { + maxHttpBufferSize: 1e10 + }, + port => { + const client = new ClientSocket(`ws://localhost:${port}`, { + transports: ["polling"] + }); + + engine.on("connection", socket => { + socket.on("message", data => { + client.close(); + done(); + }); + }); + + client.on("open", () => { + client.send("a".repeat(1e6)); + }); + } + ); + }); + + it("should arrive when content is sent with chunked transfer-encoding (polling)", function(done) { + if (process.env.EIO_WS_ENGINE === "uws") { + // µWebSockets.js does not currently support chunked encoding: https://github.com/uNetworking/uWebSockets.js/issues/669 + return this.skip(); + } + const engine = listen(port => { + const client = new ClientSocket(`ws://localhost:${port}`, { + transports: ["polling"] + }); + + engine.on("connection", socket => { + socket.on("message", data => { + expect(data).to.eql("123"); + + client.close(); + done(); + }); + }); + + client.on("open", () => { + const req = http.request({ + host: "localhost", + port, + path: `/engine.io/?EIO=4&transport=polling&sid=${client.id}`, + method: "POST" + }); + + req.write(process.env.EIO_CLIENT === "3" ? "4:41" : "41"); + req.write("2"); + req.write("3"); + req.end(); + }); + }); + }); + it("should arrive as ArrayBuffer if requested when binary data sent as Buffer (polling)", done => { const binaryData = Buffer.allocUnsafe(5); for (let i = 0; i < binaryData.length; i++) { From 5df4f18f3efbb3498f523ef600d7af61137c7d7f Mon Sep 17 00:00:00 2001 From: e3dio <85405955+e3dio@users.noreply.github.com> Date: Tue, 22 Feb 2022 13:38:19 -0700 Subject: [PATCH 42/58] perf(uws): remove nested inner functions --- lib/transports-uws/polling.ts | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/transports-uws/polling.ts b/lib/transports-uws/polling.ts index 57a1c4099..67581abd1 100644 --- a/lib/transports-uws/polling.ts +++ b/lib/transports-uws/polling.ts @@ -148,15 +148,6 @@ export class Polling extends Transport { let buffer; let offset = 0; - const cleanup = () => { - this.dataReq = this.dataRes = null; - }; - - const onClose = () => { - cleanup(); - this.onError("data request connection closed prematurely"); - }; - const headers = { // text/html is required instead of text/plain to avoid an // unwanted download dialog on certain user-agents (GH-43) @@ -164,20 +155,20 @@ export class Polling extends Transport { }; this.headers(req, headers); - Object.keys(headers).forEach(key => { + for (let key in headers) { res.writeHeader(key, String(headers[key])); - }); + } const onEnd = buffer => { this.onData(buffer.toString()); - - if (this.readyState !== "closing") { - res.end("ok"); - } - cleanup(); + this.onDataRequestCleanup(); + res.end("ok"); }; - res.onAborted(onClose); + res.onAborted(() => { + this.onDataRequestCleanup(); + this.onError("data request connection closed prematurely"); + }); res.onData((arrayBuffer, isLast) => { const totalLength = offset + arrayBuffer.byteLength; @@ -201,7 +192,7 @@ export class Polling extends Transport { if (totalLength != expectedContentLength) { this.onError("content-length mismatch"); res.writeStatus("400 Content-Length Mismatch").end(); - cleanup(); + this.onDataRequestCleanup(); return; } onEnd(buffer); @@ -212,6 +203,15 @@ export class Polling extends Transport { }); } + /** + * Cleanup request. + * + * @api private + */ + private onDataRequestCleanup() { + this.dataReq = this.dataRes = null; + } + /** * Processes the incoming data payload. * From 1bc5b1a2fde06692194cab037c88e55b0934ed6b Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 23 Feb 2022 07:36:58 +0100 Subject: [PATCH 43/58] chore: bump engine.io-parser to version 5.0.3 In order to make sure the types added in [1] are included. Related: - https://github.com/socketio/engine.io/issues/639 - https://github.com/socketio/engine.io/issues/640 [1]: https://github.com/socketio/engine.io-parser/commit/ad5bd7ddf5a1430702568a10dbef5a7c745f703d --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cac0c586a..ce37021bb 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.0", + "engine.io-parser": "~5.0.3", "ws": "~8.2.3" }, "devDependencies": { From ce3fe9d80b94297ad64606db6142778c37a8d762 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 23 Feb 2022 07:40:31 +0100 Subject: [PATCH 44/58] chore(release): 6.1.3 Diff: https://github.com/socketio/engine.io/compare/6.1.2...6.1.3 --- CHANGELOG.md | 10 ++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f72320937..207ca1cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [6.1.3](https://github.com/socketio/engine.io/compare/6.1.2...6.1.3) (2022-02-23) + + +### Bug Fixes + +* **typings:** allow CorsOptionsDelegate as cors options ([#641](https://github.com/socketio/engine.io/issues/641)) ([a463d26](https://github.com/socketio/engine.io/commit/a463d268ed90064e7863679bda423951de108c36)) +* **uws:** properly handle chunked content ([#642](https://github.com/socketio/engine.io/issues/642)) ([3367440](https://github.com/socketio/engine.io/commit/33674403084c329dc6ad026c4122333a6f8a9992)) + + + ## [6.1.2](https://github.com/socketio/engine.io/compare/6.1.1...6.1.2) (2022-01-18) diff --git a/package-lock.json b/package-lock.json index c93faa7bd..9e8dca054 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "engine.io", - "version": "6.1.2", + "version": "6.1.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "engine.io", - "version": "6.1.1", + "version": "6.1.3", "license": "MIT", "dependencies": { "@types/cookie": "^0.4.1", diff --git a/package.json b/package.json index ce37021bb..4ec390138 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.1.2", + "version": "6.1.3", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "type": "commonjs", "main": "./build/engine.io.js", From e24b27b8efaf621af372f44b7c0ece3d801c603e Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 28 Feb 2022 07:01:44 +0100 Subject: [PATCH 45/58] refactor: return an HTTP 413 response for too large payloads Before this, the connection was closed abrutly with an HTTP 502 response. See also: https://github.com/socketio/engine.io/commit/f8100f92370f4228fbcc7a92bcc4bd85d9c0f001 Related: https://github.com/socketio/socket.io/issues/4293 --- lib/transports/polling.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/transports/polling.ts b/lib/transports/polling.ts index c96010f1d..fdb4d2644 100644 --- a/lib/transports/polling.ts +++ b/lib/transports/polling.ts @@ -52,7 +52,7 @@ export class Polling extends Transport { * @param {http.IncomingMessage} * @api private */ - onRequest(req) { + onRequest(req: IncomingMessage & { res: ServerResponse }) { const res = req.res; if ("GET" === req.method) { @@ -112,7 +112,7 @@ export class Polling extends Transport { * * @api private */ - onDataRequest(req, res) { + onDataRequest(req: IncomingMessage, res: ServerResponse) { if (this.dataReq) { // assert: this.dataRes, '.dataReq and .dataRes should be (un)set together' this.onError("data request overlap from client"); @@ -155,8 +155,8 @@ export class Polling extends Transport { } if (contentLength > this.maxHttpBufferSize) { - chunks = isBinary ? Buffer.concat([]) : ""; - req.connection.destroy(); + res.writeHead(413).end(); + cleanup(); } }; From 657f04e0b81cc31e2a97d163c2fab972c6a38b3f Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 10 Mar 2022 14:13:48 +0100 Subject: [PATCH 46/58] chore: add Node.js 16 in the test matrix See also: https://github.com/nodejs/Release --- .github/workflows/ci.yml | 2 +- package-lock.json | 18 +++++++++--------- package.json | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c51ab3f52..e9ba96264 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x] + node-version: [14, 16] steps: - uses: actions/checkout@v2 diff --git a/package-lock.json b/package-lock.json index 9e8dca054..f5e546ec2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,12 +17,12 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.0", + "engine.io-parser": "~5.0.3", "ws": "~8.2.3" }, "devDependencies": { "babel-eslint": "^8.0.2", - "eiows": "^3.3.0", + "eiows": "^3.8.0", "engine.io-client": "6.1.0", "engine.io-client-v3": "npm:engine.io-client@3.5.2", "expect.js": "^0.3.1", @@ -542,13 +542,13 @@ } }, "node_modules/eiows": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.3.2.tgz", - "integrity": "sha512-7cwZl/itR8NP6YXObVo583sxQcoKdcpeuRZqelnuqlQZvJH7oBSVvMRq8vZQkL4IXCXJPL8iC2M6haSawmtOsg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.8.0.tgz", + "integrity": "sha512-ElPW+EakV8NSsA5QWJCWEx88LO/xyqMFilvsZ4P9+uyVS4ikbq2zndnYC++XLKysC+c922bGkAKeNrqWNmwNyg==", "dev": true, "hasInstallScript": true, "engines": { - "node": ">=8.12.0 <15.0" + "node": ">=8.12.0 <18.0" } }, "node_modules/emoji-regex": { @@ -2367,9 +2367,9 @@ "dev": true }, "eiows": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.3.2.tgz", - "integrity": "sha512-7cwZl/itR8NP6YXObVo583sxQcoKdcpeuRZqelnuqlQZvJH7oBSVvMRq8vZQkL4IXCXJPL8iC2M6haSawmtOsg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.8.0.tgz", + "integrity": "sha512-ElPW+EakV8NSsA5QWJCWEx88LO/xyqMFilvsZ4P9+uyVS4ikbq2zndnYC++XLKysC+c922bGkAKeNrqWNmwNyg==", "dev": true }, "emoji-regex": { diff --git a/package.json b/package.json index 4ec390138..262eeccd2 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ }, "devDependencies": { "babel-eslint": "^8.0.2", - "eiows": "^3.3.0", + "eiows": "^3.8.0", "engine.io-client": "6.1.0", "engine.io-client-v3": "npm:engine.io-client@3.5.2", "expect.js": "^0.3.1", From 088dcb4dff60df39785df13d0a33d3ceaa1dff38 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 10 Mar 2022 14:01:40 +0100 Subject: [PATCH 47/58] feat: add the "maxPayload" field in the handshake details So that clients in HTTP long-polling can decide how many packets they have to send to stay under the maxHttpBufferSize value. This is a backward compatible change which should not mandate a new major revision of the protocol (we stay in v4), as we only add a field in the JSON-encoded handshake data: ``` 0{"sid":"lv_VI97HAXpY6yYWAAAC","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000,"maxPayload":1000000} ``` Related: https://github.com/socketio/socket.io-client/issues/1531 --- lib/socket.ts | 3 ++- test/server.js | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/socket.ts b/lib/socket.ts index c3a816193..73b9b0f1f 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -90,7 +90,8 @@ export class Socket extends EventEmitter { sid: this.id, upgrades: this.getAvailableUpgrades(), pingInterval: this.server.opts.pingInterval, - pingTimeout: this.server.opts.pingTimeout + pingTimeout: this.server.opts.pingTimeout, + maxPayload: this.server.opts.maxHttpBufferSize }) ); diff --git a/test/server.js b/test/server.js index fff27d58a..906bdb6be 100644 --- a/test/server.js +++ b/test/server.js @@ -449,6 +449,7 @@ describe("server", () => { expect(obj.sid).to.be.a("string"); expect(obj.pingTimeout).to.be.a("number"); expect(obj.upgrades).to.be.an("array"); + expect(obj.maxPayload).to.eql(1000000); done(); }); }); @@ -3356,7 +3357,7 @@ describe("server", () => { expect(res.headers["set-cookie"].length).to.be(2); expect(res.headers["set-cookie"][1]).to.be("mycookie=456"); - const sid = JSON.parse(res.text.substring(4)).sid; + const sid = JSON.parse(res.text.substring(5)).sid; request .post(`http://localhost:${port}/engine.io/`) @@ -3393,7 +3394,7 @@ describe("server", () => { expect(res.headers["set-cookie"].length).to.be(2); expect(res.headers["set-cookie"][1]).to.be("mycookie=456"); - const sid = JSON.parse(res.text.substring(4)).sid; + const sid = JSON.parse(res.text.substring(5)).sid; request .post(`http://localhost:${port}/engine.io/`) From d7e3ab7956139304aa38ab5d1d2bb1d28baaf7fe Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sun, 17 Apr 2022 23:53:53 +0200 Subject: [PATCH 48/58] chore(release): 6.2.0 Diff: https://github.com/socketio/engine.io/compare/6.1.3...6.2.0 --- CHANGELOG.md | 19 +++++++++++++++++++ package-lock.json | 44 ++++++++++++++++++-------------------------- package.json | 4 ++-- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 207ca1cdf..cc6870b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +# [6.2.0](https://github.com/socketio/engine.io/compare/6.1.3...6.2.0) (2022-04-17) + + +### Features + +* add the "maxPayload" field in the handshake details ([088dcb4](https://github.com/socketio/engine.io/commit/088dcb4dff60df39785df13d0a33d3ceaa1dff38)) + +So that clients in HTTP long-polling can decide how many packets they have to send to stay under the maxHttpBufferSize +value. + +This is a backward compatible change which should not mandate a new major revision of the protocol (we stay in v4), as +we only add a field in the JSON-encoded handshake data: + +``` +0{"sid":"lv_VI97HAXpY6yYWAAAC","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000,"maxPayload":1000000} +``` + + + ## [6.1.3](https://github.com/socketio/engine.io/compare/6.1.2...6.1.3) (2022-02-23) diff --git a/package-lock.json b/package-lock.json index f5e546ec2..4355bb446 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.1.3", + "version": "6.2.0", "lockfileVersion": 2, "requires": true, "packages": { @@ -23,7 +23,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.8.0", - "engine.io-client": "6.1.0", + "engine.io-client": "6.2.0", "engine.io-client-v3": "npm:engine.io-client@3.5.2", "expect.js": "^0.3.1", "mocha": "^9.1.3", @@ -159,9 +159,9 @@ } }, "node_modules/@socket.io/component-emitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", - "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, "node_modules/@types/cookie": { @@ -558,20 +558,16 @@ "dev": true }, "node_modules/engine.io-client": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.0.tgz", - "integrity": "sha512-kNwjOjrU981utPZSzFbNU6lYda7B5xQs875x2DyCaalktt3ZSKc7DpCY18dml6JFzFH8dxsKAW0LMaGFMD7GmQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.0.tgz", + "integrity": "sha512-gz4quwdj6i17TihLOUv1mZ7/AcsrS0/Oq1ggmqod9/ULDraBIptYoZsyISLD9+chnvN6QmSZD7zuBGuiNk1hRw==", "dev": true, "dependencies": { - "@socket.io/component-emitter": "~3.0.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.0.0", - "has-cors": "1.1.0", - "parseqs": "0.0.6", - "parseuri": "0.0.6", "ws": "~8.2.3", - "xmlhttprequest-ssl": "~2.0.0", - "yeast": "0.1.2" + "xmlhttprequest-ssl": "~2.0.0" } }, "node_modules/engine.io-client-v3": { @@ -2064,9 +2060,9 @@ "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==" }, "@socket.io/component-emitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", - "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, "@types/cookie": { @@ -2379,20 +2375,16 @@ "dev": true }, "engine.io-client": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.0.tgz", - "integrity": "sha512-kNwjOjrU981utPZSzFbNU6lYda7B5xQs875x2DyCaalktt3ZSKc7DpCY18dml6JFzFH8dxsKAW0LMaGFMD7GmQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.0.tgz", + "integrity": "sha512-gz4quwdj6i17TihLOUv1mZ7/AcsrS0/Oq1ggmqod9/ULDraBIptYoZsyISLD9+chnvN6QmSZD7zuBGuiNk1hRw==", "dev": true, "requires": { - "@socket.io/component-emitter": "~3.0.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.0.0", - "has-cors": "1.1.0", - "parseqs": "0.0.6", - "parseuri": "0.0.6", "ws": "~8.2.3", - "xmlhttprequest-ssl": "~2.0.0", - "yeast": "0.1.2" + "xmlhttprequest-ssl": "~2.0.0" } }, "engine.io-client-v3": { diff --git a/package.json b/package.json index 262eeccd2..ff3daf62d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.1.3", + "version": "6.2.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "type": "commonjs", "main": "./build/engine.io.js", @@ -45,7 +45,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.8.0", - "engine.io-client": "6.1.0", + "engine.io-client": "6.2.0", "engine.io-client-v3": "npm:engine.io-client@3.5.2", "expect.js": "^0.3.1", "mocha": "^9.1.3", From ed1d6f912ce61b13e2ae7ce7a1027b8c5fae2f15 Mon Sep 17 00:00:00 2001 From: Lucas Boemeke Date: Wed, 11 May 2022 04:00:17 -0300 Subject: [PATCH 49/58] test: make test script work on Windows (#643) --- package-lock.json | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4355bb446..79099ff1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "engine.io", - "version": "6.1.3", + "version": "6.2.0", "license": "MIT", "dependencies": { "@types/cookie": "^0.4.1", diff --git a/package.json b/package.json index ff3daf62d..99d537f26 100644 --- a/package.json +++ b/package.json @@ -62,8 +62,8 @@ "test:compat-v3": "EIO_CLIENT=3 mocha --exit", "test:eiows": "EIO_WS_ENGINE=eiows mocha --exit", "test:uws": "EIO_WS_ENGINE=uws mocha --exit", - "format:check": "prettier --check 'lib/**/*.ts' 'test/**/*.js'", - "format:fix": "prettier --write 'lib/**/*.ts' 'test/**/*.js'", + "format:check": "prettier --check \"lib/**/*.ts\" \"test/**/*.js\"", + "format:fix": "prettier --write \"lib/**/*.ts' 'test/**/*.js\"", "prepack": "npm run compile" }, "repository": { From 020801ab8ce2d4cba517fe04df89b39d403123a5 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 6 Jun 2022 08:40:21 +0200 Subject: [PATCH 50/58] chore: add changelog for version 3.6.0 Diff: https://github.com/socketio/engine.io/compare/3.5.0...3.6.0 --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc6870b0e..9c72e15ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +# [3.6.0](https://github.com/socketio/engine.io/compare/3.5.0...3.6.0) (2022-06-06) + + +### Bug Fixes + +* add extension in the package.json main entry ([#608](https://github.com/socketio/engine.io/issues/608)) ([3ad0567](https://github.com/socketio/engine.io/commit/3ad0567dbd57cfb7c2ff4e8b7488d80f37022b4a)) +* do not reset the ping timer after upgrade ([1f5d469](https://github.com/socketio/engine.io/commit/1f5d4699862afee1e410fcb0e1f5e751ebcd2f9f)), closes [/github.com/socketio/socket.io-client-swift/pull/1309#issuecomment-768475704](https://github.com//github.com/socketio/socket.io-client-swift/pull/1309/issues/issuecomment-768475704) + + +### Features + +* decrease the default value of maxHttpBufferSize ([58e274c](https://github.com/socketio/engine.io/commit/58e274c437e9cbcf69fd913c813aad8fbd253703)) + +This change reduces the default value from 100 mb to a more sane 1 mb. + +This helps protect the server against denial of service attacks by malicious clients sending huge amounts of data. + +See also: https://github.com/advisories/GHSA-j4f2-536g-r55m + +* increase the default value of pingTimeout ([f55a79a](https://github.com/socketio/engine.io/commit/f55a79a28a5fbc6c9edae876dd11308b89cc979e)) + + + # [6.2.0](https://github.com/socketio/engine.io/compare/6.1.3...6.2.0) (2022-04-17) From 917d1d29e13f2e8f523c3738f6413f67b587aebe Mon Sep 17 00:00:00 2001 From: Lam Wei Li Date: Mon, 6 Jun 2022 14:42:45 +0800 Subject: [PATCH 51/58] refactor: replace deprecated `String.prototype.substr()` (#646) `.substr()` is deprecated so we replace it with `.slice()` which works similarily but isn't deprecated. See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr Signed-off-by: Lam Wei Li --- lib/parser-v3/index.ts | 8 ++++---- lib/server.ts | 4 ++-- test/server.js | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/parser-v3/index.ts b/lib/parser-v3/index.ts index 6949f1875..367ae1376 100644 --- a/lib/parser-v3/index.ts +++ b/lib/parser-v3/index.ts @@ -137,7 +137,7 @@ export function decodePacket (data, binaryType, utf8decode) { type = data.charAt(0); if (type === 'b') { - return decodeBase64Packet(data.substr(1), binaryType); + return decodeBase64Packet(data.slice(1), binaryType); } if (utf8decode) { @@ -152,7 +152,7 @@ export function decodePacket (data, binaryType, utf8decode) { } if (data.length > 1) { - return { type: packetslist[type], data: data.substring(1) }; + return { type: packetslist[type], data: data.slice(1) }; } else { return { type: packetslist[type] }; } @@ -191,7 +191,7 @@ function tryDecode(data) { export function decodeBase64Packet (msg, binaryType) { var type = packetslist[msg.charAt(0)]; - var data = Buffer.from(msg.substr(1), 'base64'); + var data = Buffer.from(msg.slice(1), 'base64'); if (binaryType === 'arraybuffer') { var abv = new Uint8Array(data.length); for (var i = 0; i < abv.length; i++){ @@ -305,7 +305,7 @@ export function decodePayload (data, binaryType, callback) { return callback(err, 0, 1); } - msg = data.substr(i + 1, n); + msg = data.slice(i + 1, i + 1 + n); if (length != msg.length) { // parser error - ignoring payload diff --git a/lib/server.ts b/lib/server.ts index 739e38aa3..58371c9cf 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -555,7 +555,7 @@ export class Server extends BaseServer { return; } - const head = Buffer.from(upgradeHead); // eslint-disable-line node/no-deprecated-api + const head = Buffer.from(upgradeHead); upgradeHead = null; // delegate to ws @@ -643,7 +643,7 @@ export class Server extends BaseServer { path += "/"; function check(req) { - return path === req.url.substr(0, path.length); + return path === req.url.slice(0, path.length); } // cache and clean up listeners diff --git a/test/server.js b/test/server.js index 906bdb6be..5de0e2591 100644 --- a/test/server.js +++ b/test/server.js @@ -181,7 +181,7 @@ describe("server", () => { .get(`http://localhost:${port}/engine.io/`) .query({ transport: "polling", EIO: 4 }) .end((err, res) => { - const sid = JSON.parse(res.text.substring(1)).sid; + const sid = JSON.parse(res.text.slice(1)).sid; request .get(`http://localhost:${port}/engine.io/`) @@ -3357,7 +3357,7 @@ describe("server", () => { expect(res.headers["set-cookie"].length).to.be(2); expect(res.headers["set-cookie"][1]).to.be("mycookie=456"); - const sid = JSON.parse(res.text.substring(5)).sid; + const sid = JSON.parse(res.text.slice(5)).sid; request .post(`http://localhost:${port}/engine.io/`) @@ -3394,7 +3394,7 @@ describe("server", () => { expect(res.headers["set-cookie"].length).to.be(2); expect(res.headers["set-cookie"][1]).to.be("mycookie=456"); - const sid = JSON.parse(res.text.substring(5)).sid; + const sid = JSON.parse(res.text.slice(5)).sid; request .post(`http://localhost:${port}/engine.io/`) From 1b71a6f5cb868c934696ae3cc1a92d1168ec8505 Mon Sep 17 00:00:00 2001 From: Tamas Flamich Date: Fri, 18 Nov 2022 21:49:23 +0100 Subject: [PATCH 52/58] docs: remove "Vanilla JS" highlight from README (#656) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 163077f94..da038e19a 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,6 @@ For more information on the client refer to the - **Future proof** - **100% Node.JS core style** - No API sugar (left for higher level projects) - - Written in readable vanilla JavaScript ## API From 535a01d8898a5cc858c9d6031fc5ecda96ea4579 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sun, 20 Nov 2022 00:12:08 +0100 Subject: [PATCH 53/58] ci: add Node.js 18 in the test matrix A few notes: - the certificates were recreated because Node.js 18 includes OpenSSL v3, which has deprecated support for some legacy ciphers (like RC2) - eiows currently fails to build on Node.js 18, so the tests are temporarily skipped See also: https://github.com/nodejs/Release --- .github/workflows/ci.yml | 28 ++++++++++++++++++++------- package-lock.json | 26 ++++++++++++------------- package.json | 8 ++++---- test/common.js | 2 +- test/fixtures/client.crt | 40 +++++++++++++++++++-------------------- test/fixtures/client.pfx | Bin 1949 -> 1973 bytes test/fixtures/server.crt | 40 +++++++++++++++++++-------------------- test/server.js | 26 +++++++++++-------------- 8 files changed, 90 insertions(+), 80 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9ba96264..62ebbbf17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,21 +6,35 @@ on: schedule: - cron: '0 0 * * 0' +permissions: + contents: read + jobs: test-node: runs-on: ubuntu-latest + timeout-minutes: 10 strategy: matrix: - node-version: [14, 16] + node-version: + - 10 + - 18 steps: - - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm test - env: - CI: true + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Run tests with µWebSockets.js + run: npm run test:uws + if: ${{ matrix.node-version == '18' }} diff --git a/package-lock.json b/package-lock.json index 79099ff1c..65353c7e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ }, "devDependencies": { "babel-eslint": "^8.0.2", - "eiows": "^3.8.0", + "eiows": "^4.1.2", "engine.io-client": "6.2.0", "engine.io-client-v3": "npm:engine.io-client@3.5.2", "expect.js": "^0.3.1", @@ -31,7 +31,7 @@ "rimraf": "^3.0.2", "superagent": "^3.8.1", "typescript": "^4.4.3", - "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.0.0" + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.15.0" }, "engines": { "node": ">=10.0.0" @@ -542,13 +542,13 @@ } }, "node_modules/eiows": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.8.0.tgz", - "integrity": "sha512-ElPW+EakV8NSsA5QWJCWEx88LO/xyqMFilvsZ4P9+uyVS4ikbq2zndnYC++XLKysC+c922bGkAKeNrqWNmwNyg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/eiows/-/eiows-4.1.2.tgz", + "integrity": "sha512-dNu+yOdp/mPROgEIW9pFnx+iBLob130BPqspXxSt63OvICtitrpYQSmdJ2M7irIwqh3oj1JfDKadHPCocKPBpg==", "dev": true, "hasInstallScript": true, "engines": { - "node": ">=8.12.0 <18.0" + "node": ">=14.13.0 <19.0" } }, "node_modules/emoji-regex": { @@ -1751,8 +1751,8 @@ "dev": true }, "node_modules/uWebSockets.js": { - "version": "20.0.0", - "resolved": "git+https://git@github.com/uNetworking/uWebSockets.js.git#4558ee00f9f1f686fffe1accbfc2e85b1af9c50f", + "version": "20.15.0", + "resolved": "git+https://git@github.com/uNetworking/uWebSockets.js.git#77bc1fd5577ae42b56675912eb8481a31f3fefd2", "dev": true }, "node_modules/vary": { @@ -2363,9 +2363,9 @@ "dev": true }, "eiows": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.8.0.tgz", - "integrity": "sha512-ElPW+EakV8NSsA5QWJCWEx88LO/xyqMFilvsZ4P9+uyVS4ikbq2zndnYC++XLKysC+c922bGkAKeNrqWNmwNyg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/eiows/-/eiows-4.1.2.tgz", + "integrity": "sha512-dNu+yOdp/mPROgEIW9pFnx+iBLob130BPqspXxSt63OvICtitrpYQSmdJ2M7irIwqh3oj1JfDKadHPCocKPBpg==", "dev": true }, "emoji-regex": { @@ -3258,9 +3258,9 @@ "dev": true }, "uWebSockets.js": { - "version": "git+https://git@github.com/uNetworking/uWebSockets.js.git#4558ee00f9f1f686fffe1accbfc2e85b1af9c50f", + "version": "git+https://git@github.com/uNetworking/uWebSockets.js.git#77bc1fd5577ae42b56675912eb8481a31f3fefd2", "dev": true, - "from": "uWebSockets.js@github:uNetworking/uWebSockets.js#v20.0.0" + "from": "uWebSockets.js@github:uNetworking/uWebSockets.js#v20.15.0" }, "vary": { "version": "1.1.2", diff --git a/package.json b/package.json index 99d537f26..e03057fd6 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ }, "devDependencies": { "babel-eslint": "^8.0.2", - "eiows": "^3.8.0", + "eiows": "^4.1.2", "engine.io-client": "6.2.0", "engine.io-client-v3": "npm:engine.io-client@3.5.2", "expect.js": "^0.3.1", @@ -53,17 +53,17 @@ "rimraf": "^3.0.2", "superagent": "^3.8.1", "typescript": "^4.4.3", - "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.0.0" + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.15.0" }, "scripts": { "compile": "rimraf ./build && tsc", - "test": "npm run compile && npm run format:check && npm run test:default && npm run test:compat-v3 && npm run test:eiows && npm run test:uws", + "test": "npm run compile && npm run format:check && npm run test:default && npm run test:compat-v3", "test:default": "mocha --bail --exit", "test:compat-v3": "EIO_CLIENT=3 mocha --exit", "test:eiows": "EIO_WS_ENGINE=eiows mocha --exit", "test:uws": "EIO_WS_ENGINE=uws mocha --exit", "format:check": "prettier --check \"lib/**/*.ts\" \"test/**/*.js\"", - "format:fix": "prettier --write \"lib/**/*.ts' 'test/**/*.js\"", + "format:fix": "prettier --write \"lib/**/*.ts\" \"test/**/*.js\"", "prepack": "npm run compile" }, "repository": { diff --git a/test/common.js b/test/common.js index c02d6a429..bb3cebdbb 100644 --- a/test/common.js +++ b/test/common.js @@ -1,5 +1,4 @@ const { listen, uServer } = require(".."); -const { App, us_socket_local_port } = require("uWebSockets.js"); const { Socket } = process.env.EIO_CLIENT === "3" ? require("engine.io-client-v3") @@ -18,6 +17,7 @@ exports.listen = (opts, fn) => { opts.allowEIO3 = true; if (process.env.EIO_WS_ENGINE === "uws") { + const { App, us_socket_local_port } = require("uWebSockets.js"); const engine = new uServer(opts); const app = App(); engine.attach(app, opts); diff --git a/test/fixtures/client.crt b/test/fixtures/client.crt index 7264aa0d4..2957b4abc 100644 --- a/test/fixtures/client.crt +++ b/test/fixtures/client.crt @@ -1,22 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDojCCAYoCAQIwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV -BAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 -ZDAeFw0xNTExMTgxNzM4MDVaFw0yNTExMTUxNzM4MDVaMG0xCzAJBgNVBAYTAkZJ -MRMwEQYDVQQIEwpTb21lLVN0YXRlMREwDwYDVQQHEwhIZWxzaW5raTEhMB8GA1UE -ChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDEwpGb28gQ2xpZW50 -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD66Rn8P8O+MK13sPxIIEMHXDRZ -heRLqGNlNsXzaBLWnKSlV+Wxi1OdCimtAh4ZAVRt1JkK9mQEAGdxC8TRwDMS02+E -UK1H1zvh77Ek4ZcHW8p5CVEm53FTmO+jhL+7BQYXW1yi/XURBv2xm3Q95I7895ag -prMFI8HiOu/Hdi/iDQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQBynfI9C5/zcukL -oQmUSEg5ys99bGUa8QyhSRbp5nb8Wl76KRIxpqGxl+3wcyhES3cH1Vgv4CMvrVag -Qov4TG4B30MvEvd17BMD1BmDNqjOInW72ujS9wo+2K6NASw1r/96Ay7dJ7+3HE0r -gs9yaiRL5UG4y7Sp1gy5JynXNozNowVP/cy84L9K2oyiJpyc5Owg93i3E7X+/eXd -JfFTO6ZfTGNdN+hMjleyj/LXOsoeaYg3GhO9i99nzf3u0HBXI1wRbkRPJ0aNSkH0 -VEValjOGeRdKvlZhsf7x8Kg7Nk7fyIyhmYU/tBOSUasFiB7CfKN/m0P1e6DMqihW -U/k4bzoVND3KuNH5EZmfSs3BE/w5fakObCpkxAMMm6Lxtb6qBV2HlWFfatcL2WoM -nqdCEPjct7crPbgtn3Pa/erlvmAWFoqsCu4Xn4SRGP1JtGXbCLaAW4S5CffaODRp -4urgxD8bfk9Mm/9b/xAAMNkcAi4oBikVTnVWrpul/qIhY0iMfqIu6MkbWAthMT4h -th5AggLxl6+dcPGNaliC5JpRtCis+fWuKxwbf8YKSEIwi0VBQbPP2ATinqfOJ1Yo -/tymAa3IKukil6RuTECTpE06x+Ns0rMJTSN2SGd4AG/n1/SAjhBHoH1J0J4JG30f -5fx0ax5MzGuTrt9yNEUvIrKj6MIgmw== +MIIDtTCCAZ0CFCt+tjtA9647yZp8eNZurQtNp4x+MA0GCSqGSIb3DQEBDQUAMEUx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjIxMTE4MjEyMzU4WhcNMzIxMTE1MjEy +MzU4WjBtMQswCQYDVQQGEwJGSTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UE +BxMISGVsc2lua2kxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDET +MBEGA1UEAxMKRm9vIENsaWVudDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA ++ukZ/D/DvjCtd7D8SCBDB1w0WYXkS6hjZTbF82gS1pykpVflsYtTnQoprQIeGQFU +bdSZCvZkBABncQvE0cAzEtNvhFCtR9c74e+xJOGXB1vKeQlRJudxU5jvo4S/uwUG +F1tcov11EQb9sZt0PeSO/PeWoKazBSPB4jrvx3Yv4g0CAwEAATANBgkqhkiG9w0B +AQ0FAAOCAgEAMgC3j2XJ5IPB1raLrnJh8vTncnqMe6OmXpaxk0ZYb42Y66BJKlaE +UpLmLYIiICmuH6R4lc00W5nexPyCT4g+1CUs3PhOJGEwWOBodv6dFJ4ayGWln1aD +QX+W+PRuJAazd7wruVnPxVoEspVO+hcr5byX0F3Auqd9jdQZwFXsWvAo7tZxUnvC +gdjnHt5QgMxqeqzZPTw7dreMsIjN6NrUPWaa26VCvLH0Nv+Jgs+RSVwBKp8tO3e+ +763bi8Htpzt4YfAB7EuRykGlAI42C5ZDzcsq30NpSGgOwveHnlvdl6KhC0QaK71h +QmXwBmEUNX1f+XRnvk+fNb1acfddLLYoPP0zS1BEYOOs7KkyScagsUMsnUSOfv3d ++etklFvaXFD3+b/KwljH3WH1dG4ro3J6GHXX05ncDydDDksYi6aC3wpPZYY7eMFx +RWSxMZHX/bD1YH80a2+jBoskTqz3ZFkkGySMfUcpDCUwQuiwjhLp4sew9RDRB/lv +kJezNSoYgnT44CT+IPoPEL1m5Evkm3C7fVzvnldO3TsWmOoza99xrQ+9gtzlWxgb +Av6jNbnGG1HgDYcvxpRMKWe+6fUAHCcP0PuO+2rcygemNtEKzfMY6Py66w5L9/WW +t0UJWU1rR+kLDS3qLfQqvnbvUMroZ9zxE9CJq6+aKEQEpc79lfiv464= -----END CERTIFICATE----- diff --git a/test/fixtures/client.pfx b/test/fixtures/client.pfx index 11e663341bbacfaa509c84f252973e39f24bfa1c..381a0a4f29fd0710d09770659fe6a97be2f444e3 100644 GIT binary patch delta 1906 zcmV-&2aWih548_KFoFlM0s#Xsf(Lg72`Yw2hW8Bt2LYgh2WSL>2V^jU2VgLQ1YZUT zDuzgg_YDCD2B3ljP%wf7OacJ_FoFa{kw6%K127H*2zI=6%RQaeGy(z$0Dyu77&5I8 zmIY%8;Wbv0WRJE3W@xnjf}x(SOI>ECA+a`92TVG6skmgj^Ape%9%I-;c}4x8)b%YK zeVl<#b{R^ndFE%MG4`b5Eh9g3`01$0*w71t#zHR(r;kSkosp@CL$_X%uN6%wC9T$f z7xjdfB#9H|koE?)RT8-PI9$N1Gv|2CX0OXCs-S5?tzZ$GF|f=-?0CVSABDzAcE{Ai z;lBI#)L4P(Qf7|P@$F`G%u%T^Bn6*@H;w&1gzib&Z0Ynj^ z3~5iOYnP_LrHxtDYe|3TxHrpqbt)}?AgmG%QOnP88un~X?Au?RX$P=sEHa0II^7&9 z)_4iMaOz001k=&|2St{mdcZpM<6H#w&M`Ji8 z?NaTpgbAnUHwTKDpnaq;Z9mDzH(7IHsRXW0L3`R@IE80*-0Amy!ah69nTalc^Y&EF z%zcnnJ?DBh0j1w95a6vxfs*tT+I;%;z=chKA@e8_mMZK>I&&S06V%j-w-OHFN++e( zHZLyJh5xVfaI;r@7z}S8708V&_TLZd(V223clucD%5U;`gCpr<2)4{bUO^a!EFx&;pgo#MP z1FKL#ML z!{l*RIsdXdzTY8HVPUt+)Ud3jP4ORdm+U`NleLGOu$wikekStBR#uWmd(-s8+-3IKM zEvdMUf#pX1Z!hc@S7^QO-y6KKVNH)nLGy^7YyT?LNNwSaS)$FGY$T=gIrY-yh@_+a7~&MYka z1NXjK3w`3P0lf3gO?IY0e8SwEercfp!woa z%msji;I;rtlbkcBNRw>@Mt@AZPm$(i?nnXx2ml0v0)QCK=NMJW@EO>va#brrs(jL@ zDoWoT8{Gba`dfGLy^z60?TSq@lGZa;;JYq#>S9v>C3iL~alA1ykZ_Sq3H;&7Nr*Ytab7mX;SbsajX

@wk_o_ z9@rlEYht~LwXUc?z8dAZsH9M1nL95e(O>WKfKwX?Ngzy00H8szV%9|S;rft++ysT% z^qtRsKwoYUqb!px@h$J;c@#(k)h4^bl>7G=UF-&KvJns{Tq}%2NYJ1g-kMgo6D*MQ zmMwbk4mw*HCdNG|ZGR!thNw>;<1_6>D0_rJfoEYX7)1N{U=mmn;}CIe>Irt4^P-TM zeQslbszYp-ozJP5`oZ6gVcYyZKyVY5zVnA&e?*%j$Y)xGPCTJ1;o`bczzh7W%OYiP zk6$E~s}*-Yfa_(vpzX0yzn)~;cE$IT}zRO7tVI0a)Ev8tzsU^c6tL9k-tO+#l+Wc(Xj*|6jNRu?`t zq~>y$jions0e{JR0PNJnDyMl>mc&?jdl@=)%9@h7AZkPXV8tdffQH(zD$#8lvl zC9ToKi{LFooG?Utu?)WS!5GjH&^#gVcJ3d4AXP$@K5vtwbmhJJW=g{hk3-?Vt4Jl* zEQ`T0B`_lf2`Yw2hW8Bt2^BFG1QdDHvZUitiC_U2>kC7V#^F5DG>0%TFd;Ar1_dh) s0|FWa00b1QYaG=5cRY8>uHf`3)mWB2Kh;|V2+|8GfPbmt8v+6d0JxcX;{X5v delta 1882 zcmV-g2c`J651kJ{FoFk}0s#Xsf(Ks)2`Yw2hW8Bt2LYgh2T%lp2TU-62S_l21V;u5 zDuzgg_YDCD2B3ljI52_)Gy(wtFoFavkw6%K1~3i;2w6M~9s|Qz;{pN*0Dyu70Cixy zdtqx!YGJKI9B~!qorD;^eEnCmNFWUv5R+L7>}{@JMV3Axhcl|5YLKAWcRfeD!P@9R z10*6dXk3H9>%DgIx?kj3U&gr{>iVGVv)$*~K5lzUdta$x$7qZ%#21-DOoX-6YVG}h z9M-QiAPp!+vg!Ky22Bd8MpB}G?>W{3tE(+tH)c4IQDFd9e(!Xpg!0im9#g7d{d#x^ z{itEqgX<@pu^_4#v}P^Gs&8rR08V|nqOrtb*0G4F^s5?P7zWbgpeXE!j5vaum_{JbqozF<6htObv9^>4W%!xKYDkn z<}EM}u|<3Y4&%2<-f;-PmJA_(*myOr3JE=jw|hvJO~FZ9J+(OZVavMhXq@RMAvFxOsdQ5bQ#Kl_lpJcmf7_d2xd%!i$W^$)=j z4m^ilxmk3pQ+zx+f!QmHOa%NNA@}kP$UNh^@dFYaL#1Hod@UinV41rSf8% zvrmflul|H`(wINAvy}6H^J!vEaXnDCL{zdH9BVYRwb= zbFJkIdY-VTgHZ>r-w2+4o7=xjBTF|uw~9KA&&uRW{P1E&mgj-LiFNBCe0RnV%U9?y zM8+|D2*K>!UbcSVHM{YD9eGuB{r$%4edwmpKw4~rmX0}ftQOJl&r?dH{1GVp;#tczRCGIq7x7z&-A`!CWJQfH-KU~ z&;mUpPBsXL3qd_fK|79(P91hp9PD_$oIa=wZR8SRbwP+Q>s0G=*5(*esiD!NlvttP z<+9o>AyK&5N1hcRbSgIbZ-wesX|)zzF@^W8>$F_f_D#%&EJg`UIi;Q(_oS7& zfyBhpa!r~pDGAudZC>t)x7qb8QdiMYp{LmZ7c!0ar&lIPAPqsd(Yn?wf>6P`d}$Tp z!8M}#KOR959Y^_>;(yb4)EFT$ew~1coJ_U1Im|NQ3p!Tu!!K62o62hS1?hHYK#!AOKcm`aBIX)sQe4IkIKr-&2!qCokAEDhMcWnXML)}AFyzV5 z^LVz+Ybgh{Y$~|IC71qt6$p|FsT#k5%dCZQ7MtJM%HQj1r!feH%-9)sz;hR2(fhDH z>TJ&Hr-mTOEkS7c&&>U(1klRGeN&4%!;xKOqQ|^Id~{`Aa4$^n0DZM21il?)dgHf> zbv*9RszGEmOK+^kIf*iXUO2OE8{_WVnDF%>3*d$^B`_lf2`Yw2hW8Bt2^BFG1Qdf* z@sw5E$Y>Hs;}e|c2<*mZLnkmXFd;Ar1_dh)0|FWa00a~yM!8t!&Rzl|Qew8|P2r{2 U8s0Yq2%owgi0P$+=mG)=0ALt-wEzGB diff --git a/test/fixtures/server.crt b/test/fixtures/server.crt index 0b49d96e5..acc6c646f 100644 --- a/test/fixtures/server.crt +++ b/test/fixtures/server.crt @@ -1,22 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDoTCCAYkCAQEwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV -BAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 -ZDAeFw0xNTExMTgxNzM4MDhaFw0yNTExMTUxNzM4MDhaMGwxCzAJBgNVBAYTAkZJ -MRMwEQYDVQQIEwpTb21lLVN0YXRlMREwDwYDVQQHEwhIZWxzaW5raTEhMB8GA1UE -ChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDEwlsb2NhbGhvc3Qw -gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBAs08Mguowi6W8+BrqiByLr3Fh -pKnnRTtO/wfDDebgP3Da5vIapHt0NbbSIYkju1M1y/9S0uqCScSIIFeSr8wOvmB1 -c0jRX+P6oA6p8lJXG/HEfEuu3uK+Olkds7sHcrtuRaYLEAXNYCUTlT0VdfVvxV96 -0m/5wMRpexjC5h+jAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACvxt9oSl8zDz2i9 -DFtBVA87TYkv7MPqc6HKzpzKjUpay1CmTrXxaw9WKXGC47aoj/wXNqe1W/w5KUdH -79Z7n/TKpQJ9hj3NQJerFGD5EGu+rL4/FshMiwjp0/AiuAIThVa11BDzmEltwf86 -mHfIhRG89m3LmJbCzOLTjqjnt3vL5fcW0KEN+5TDFxINbXC6o+ioZgMeh3n+d01D -MZImN01A0ZwKch81IDmPZD/IrtCLGwG/YfTaQUshMHUaQIKqDIyzDb/VgWGh1vET -J6Zpbvr3xc+mg34588qd6JcyND6X1LKBvLUNqcIrcAaChbabyPUCy1w+XDgcATZg -uerXcELSApivaSVGZVNkrbUskDySjZp2NhpEhDFim3HZye7CUvVIM5AI8BPeI5ZA -nYi2kcya3e9/u3YxfsCklbxCiBUMhVYT3EVHNILr0Eik4NJp92xBLyRk770WvwMc -MAiRNK5+XM0DffzHe9PrcopWRwvPsh8ce9IUsTJQjx0zALc8JKT6VN8J1cNseZR5 -ALuTasgFliRFYCwUJcwCpoXUDJas7hbBDcbecMbFKDCrmUbG6moRqETtljDtn4na -cKwaAk2sp9CSaNoLAsWd45ElXpeoKtNz7dFsRf1nSu2GPdE2SdaPJ6br+bKcO6TW -LRObFtVqZM7TTrPUuVji72CfFMtl +MIIDtDCCAZwCFCt+tjtA9647yZp8eNZurQtNp4x/MA0GCSqGSIb3DQEBDQUAMEUx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjIxMTE4MjEyNDExWhcNMzIxMTE1MjEy +NDExWjBsMQswCQYDVQQGEwJGSTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UE +BxMISGVsc2lua2kxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDES +MBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA +QLNPDILqMIulvPga6ogci69xYaSp50U7Tv8Hww3m4D9w2ubyGqR7dDW20iGJI7tT +Ncv/UtLqgknEiCBXkq/MDr5gdXNI0V/j+qAOqfJSVxvxxHxLrt7ivjpZHbO7B3K7 +bkWmCxAFzWAlE5U9FXX1b8VfetJv+cDEaXsYwuYfowIDAQABMA0GCSqGSIb3DQEB +DQUAA4ICAQCDSuFPJ++HY5WBhpnumbZ7+T0ReWKaerdNnQ2Xgrna5mZfB2xeRkvY +XeJ9VBCpGgEZKCKkhZCjomn/kLkYzRk4Sqr1ivN5NWl6G/9UTttHdRa3xiR1NhKI +AMYghpel30w5e+cWtsdR06P2FvZMuiMFCyqsbPf1xcEIAXN7HJDswq6g0ppTVZ4L +sXljG/J0vp+jAst4XKGLaGqnt8JaBnpNX9NO2Up3h5j7Pa4Nhm/LZ3Ku5ZVDmS1d +B98Bsgr6tQSSyPNfZW0tGXELsNX1I+wUFw9IXFadRTHkhjeT/GhFw3i12uY7rqzm +uJegTtWDkp1QOajhYhLD9WGXb9teldkAAgZawD6ax/uAzqx/4mBFvsUa3FMcua8k +HF9P2lLzKAcyaKt1cvlfUYmDVZ2Gh+9PgM8SqRpMIqK5jMRvFgemxJXS9BMBrQLp +TCvgRwQZD4mUloRlGNewKfJ0oQ1rY29vwdjTL8+BBS/GR8EuzYnqJG/D2nK0guIN +ze+cSDghA5N2pp/ffnpLWmkIDO+fsGAj3eApLhbPQ1xCXnEv6fOjgUmnxdt41m8d ++pEVBICohnvYgoEERDNAi1onJlBd/eyk0Jn37QiwqhQyrmfgwncvlt2SyzS1IZ7s +cEYreG6QHghBhgYiYo0FMuDCjT6g6Ga+T8nOp0xpZtGEWvHwjLjxvQ== -----END CERTIFICATE----- diff --git a/test/server.js b/test/server.js index 5de0e2591..74ad2a17b 100644 --- a/test/server.js +++ b/test/server.js @@ -3646,7 +3646,8 @@ describe("server", () => { } }); - it("should allow loading of other websocket server implementation like eiows", done => { + // FIXME eiows fails to build on Node.js 18 (and has dropped support for Node.js 10) + it.skip("should allow loading of other websocket server implementation like eiows", done => { const engine = listen( { allowUpgrades: false, wsEngine: require("eiows").Server }, port => { @@ -3667,19 +3668,20 @@ describe("server", () => { }); describe("remoteAddress", () => { + const POSSIBLE_VALUES = [ + "0000:0000:0000:0000:0000:0000:0000:0001", + "0000:0000:0000:0000:0000:ffff:7f00:0001", + "::ffff:127.0.0.1", + "::1" + ]; + it("should be defined (polling)", done => { const engine = listen({ transports: ["polling"] }, port => { new ClientSocket(`ws://localhost:${port}`, { transports: ["polling"] }); engine.on("connection", socket => { - if (process.env.EIO_WS_ENGINE === "uws") { - expect(socket.remoteAddress).to.be( - "0000:0000:0000:0000:0000:ffff:7f00:0001" - ); - } else { - expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); - } + expect(POSSIBLE_VALUES).to.contain(socket.remoteAddress); done(); }); }); @@ -3691,13 +3693,7 @@ describe("server", () => { transports: ["websocket"] }); engine.on("connection", socket => { - if (process.env.EIO_WS_ENGINE === "uws") { - expect(socket.remoteAddress).to.be( - "0000:0000:0000:0000:0000:ffff:7f00:0001" - ); - } else { - expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); - } + expect(POSSIBLE_VALUES).to.contain(socket.remoteAddress); done(); }); }); From 7c1270f98c51e51dfae1237492a56276070fd10e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 20 Nov 2022 00:43:37 +0100 Subject: [PATCH 54/58] chore(deps): bump nanoid from 3.1.25 to 3.3.1 (#659) Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.25 to 3.3.1. - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/3.1.25...3.3.1) --- updated-dependencies: - dependency-name: nanoid dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 215 ++++++++++++++++++---------------------------- 1 file changed, 84 insertions(+), 131 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65353c7e6..a77c665b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -387,10 +387,16 @@ } }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -496,9 +502,9 @@ } }, "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dependencies": { "ms": "2.1.2" }, @@ -841,9 +847,9 @@ } }, "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -975,7 +981,7 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1246,32 +1252,32 @@ } }, "node_modules/mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -1288,29 +1294,6 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1323,26 +1306,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mocha/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1352,6 +1315,18 @@ "node": ">=8" } }, + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1379,9 +1354,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -1485,9 +1460,9 @@ } }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -1779,9 +1754,9 @@ } }, "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "node_modules/wrap-ansi": { @@ -2242,9 +2217,9 @@ } }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -2337,9 +2312,9 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } @@ -2588,9 +2563,9 @@ "dev": true }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2700,7 +2675,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { @@ -2889,80 +2864,58 @@ } }, "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2986,9 +2939,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "negotiator": { @@ -3059,9 +3012,9 @@ "dev": true }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "prettier": { @@ -3277,9 +3230,9 @@ } }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { From d196f6a6b746b5e362b131a1a16901a3db12cb21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 20 Nov 2022 00:43:47 +0100 Subject: [PATCH 55/58] chore(deps): bump minimatch from 3.0.4 to 3.1.2 (#660) Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2. - [Release notes](https://github.com/isaacs/minimatch/releases) - [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2) --- updated-dependencies: - dependency-name: minimatch dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index a77c665b8..92ad6281d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1240,9 +1240,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -2855,9 +2855,9 @@ } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" From 99adb00ba11d80ab27a4a2f4afd0eebd8aa406c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 20 Nov 2022 00:46:22 +0100 Subject: [PATCH 56/58] chore(deps): bump xmlhttprequest-ssl and engine.io-client in /examples/latency (#661) Bumps [xmlhttprequest-ssl](https://github.com/mjwwit/node-XMLHttpRequest) to 1.6.3 and updates ancestor dependency [engine.io-client](https://github.com/socketio/engine.io-client). These dependencies need to be updated together. Updates `xmlhttprequest-ssl` from 1.5.5 to 1.6.3 - [Release notes](https://github.com/mjwwit/node-XMLHttpRequest/releases) - [Commits](https://github.com/mjwwit/node-XMLHttpRequest/compare/1.5.5...1.6.3) Updates `engine.io-client` from 4.0.0 to 4.1.4 - [Release notes](https://github.com/socketio/engine.io-client/releases) - [Changelog](https://github.com/socketio/engine.io-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/socketio/engine.io-client/compare/4.0.0...4.1.4) --- updated-dependencies: - dependency-name: xmlhttprequest-ssl dependency-type: indirect - dependency-name: engine.io-client dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/latency/package-lock.json | 56 +++++++++++------------------- examples/latency/package.json | 2 +- 2 files changed, 21 insertions(+), 37 deletions(-) diff --git a/examples/latency/package-lock.json b/examples/latency/package-lock.json index 059da8d7e..d88dfdbe6 100644 --- a/examples/latency/package-lock.json +++ b/examples/latency/package-lock.json @@ -81,7 +81,7 @@ "base64-arraybuffer": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" + "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==" }, "base64-js": { "version": "0.0.7", @@ -93,14 +93,6 @@ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "requires": { - "callsite": "1.0.0" - } - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -525,39 +517,34 @@ } }, "engine.io-client": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.0.tgz", - "integrity": "sha512-dmydBrbZW0AYX4+u7aRzslPw/MSZy3HwwqHx3Es5OPj9q0tnyPeFZZU/UE5aLjba6xIwZTXkB+OdqF5wmR21IA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.1.4.tgz", + "integrity": "sha512-843fqAdKeUMFqKi1sSjnR11tJ4wi8sIefu6+JC1OzkkJBmjtc/gM/rZ53tJfu5Iae/3gApm5veoS+v+gtT0+Fg==", "requires": { "base64-arraybuffer": "0.1.4", "component-emitter": "~1.3.0", - "debug": "~4.1.0", + "debug": "~4.3.1", "engine.io-parser": "~4.0.1", "has-cors": "1.1.0", "parseqs": "0.0.6", - "parseuri": "0.0.5", - "ws": "~7.2.1", - "xmlhttprequest-ssl": "~1.5.4", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.6.2", "yeast": "0.1.2" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "ws": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", - "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==" } } }, @@ -755,7 +742,7 @@ "has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==" }, "http-browserify": { "version": "1.3.2", @@ -1032,12 +1019,9 @@ "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" }, "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "requires": { - "better-assert": "~1.0.0" - } + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" }, "parseurl": { "version": "1.3.3", @@ -1528,9 +1512,9 @@ "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" }, "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", + "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==" }, "xtend": { "version": "3.0.0", @@ -1558,7 +1542,7 @@ "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==" } } } diff --git a/examples/latency/package.json b/examples/latency/package.json index 2f7c6d34a..7f2ea6b5d 100644 --- a/examples/latency/package.json +++ b/examples/latency/package.json @@ -4,7 +4,7 @@ "dependencies": { "enchilada": "0.13.0", "engine.io": "^4.1.2", - "engine.io-client": "^4.0.0", + "engine.io-client": "^4.1.4", "express": "^4.17.1", "smoothie": "1.19.0" } From 425e833ab13373edf1dd5a0706f07100db14e3c6 Mon Sep 17 00:00:00 2001 From: Jonathan Neve Date: Thu, 17 Nov 2022 10:48:55 -0500 Subject: [PATCH 57/58] fix: catch errors when destroying invalid upgrades (#658) Before this change, receiving an HTTP2 upgrade would make the server crash: > Error: read ECONNRESET > at TCP.onStreamRead (node:internal/stream_base_commons:217:20) { > errno: -104, > code: 'ECONNRESET', > syscall: 'read' > } This can be reproduced with Node.js v14.15.3, v16.18.1 and v18.12.1. --- lib/server.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/server.ts b/lib/server.ts index 58371c9cf..c99f8ef86 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -678,6 +678,9 @@ export class Server extends BaseServer { setTimeout(function() { // @ts-ignore if (socket.writable && socket.bytesWritten <= 0) { + socket.on("error", e => { + debug("error while destroying upgrade: %s", e.message); + }); return socket.end(); } }, destroyUpgradeTimeout); From 24b847be6a61b64efc8c8c4d058a69259ad67693 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sun, 20 Nov 2022 02:14:27 +0100 Subject: [PATCH 58/58] chore(release): 6.2.1 Diff: https://github.com/socketio/engine.io/compare/6.2.0...6.2.1 --- CHANGELOG.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c72e15ca..3ec49ec4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,70 @@ +# History + +## 2022 + +- [6.2.1](#621-2022-11-20) (Nov 2022) +- [3.6.0](#360-2022-06-06) (Jun 2022) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) +- [6.2.0](#620-2022-04-17) (Apr 2022) +- [6.1.3](#613-2022-02-23) (Feb 2022) +- [6.1.2](#612-2022-01-18) (Jan 2022) +- [6.1.1](#611-2022-01-11) (Jan 2022) + +## 2021 + +- [6.1.0](#610-2021-11-08) (Nov 2021) +- [6.0.1](#601-2021-11-06) (Nov 2021) +- [**6.0.0**](#600-2021-10-08) (Oct 2021) +- [5.2.0](#520-2021-08-29) (Aug 2021) +- [5.1.1](#511-2021-05-16) (May 2021) +- [5.1.0](#510-2021-05-04) (May 2021) +- [**5.0.0**](#500-2021-03-10) (Mar 2021) +- [4.1.1](#411-2021-02-02) (Feb 2021) +- [4.1.0](#410-2021-01-14) (Jan 2021) +- [4.0.6](#406-2021-01-04) (Jan 2021) + +## 2020 + +- [3.5.0](#350-2020-12-30) (Dec 2020) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) +- [4.0.5](#405-2020-12-07) (Dec 2020) +- [4.0.4](#404-2020-11-17) (Nov 2020) +- [4.0.3](#403-2020-11-17) (Nov 2020) +- [4.0.2](#402-2020-11-09) (Nov 2020) +- [4.0.1](#401-2020-10-21) (Oct 2020) +- [**4.0.0**](#400-2020-09-10) (Sep 2020) +- [3.4.2](#342-2020-06-04) (Jun 2020) +- [3.4.1](#341-2020-04-17) (Apr 2020) + + + +# Release notes + +## [6.2.1](https://github.com/socketio/engine.io/compare/6.2.0...6.2.1) (2022-11-20) + +:warning: This release contains an important security fix :warning: + +A malicious client could send a specially crafted HTTP request, triggering an uncaught exception and killing the Node.js process: + +``` +Error: read ECONNRESET + at TCP.onStreamRead (internal/stream_base_commons.js:209:20) +Emitted 'error' event on Socket instance at: + at emitErrorNT (internal/streams/destroy.js:106:8) + at emitErrorCloseNT (internal/streams/destroy.js:74:3) + at processTicksAndRejections (internal/process/task_queues.js:80:21) { + errno: -104, + code: 'ECONNRESET', + syscall: 'read' +} +``` + +Please upgrade as soon as possible. + +### Bug Fixes + +* catch errors when destroying invalid upgrades ([#658](https://github.com/socketio/engine.io/issues/658)) ([425e833](https://github.com/socketio/engine.io/commit/425e833ab13373edf1dd5a0706f07100db14e3c6)) + + + # [3.6.0](https://github.com/socketio/engine.io/compare/3.5.0...3.6.0) (2022-06-06) diff --git a/package.json b/package.json index e03057fd6..a4eb25412 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "6.2.0", + "version": "6.2.1", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "type": "commonjs", "main": "./build/engine.io.js",