From ebf1a96f4285a7326e42fd21b33f53b64c65050e Mon Sep 17 00:00:00 2001 From: Jacco Flenter Date: Fri, 2 Nov 2018 07:38:16 +0100 Subject: [PATCH 001/124] [feat] Allow configuration of `Access-Control-Allow-Origin` value (#511) It's now possible to specify an origins value (default value is '*') when initialising the engine. This value will be returned as the Access-Control-Allow-Origin value. Related: #449 --- README.md | 1 + lib/server.js | 26 +++++++++++++++++--------- lib/transport.js | 4 +++- lib/transports/index.js | 6 +++--- lib/transports/polling-jsonp.js | 4 ++-- lib/transports/polling-xhr.js | 9 ++++----- lib/transports/polling.js | 4 ++-- lib/transports/websocket.js | 4 ++-- test/server.js | 16 ++++++++-------- 9 files changed, 42 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 1bc83d1ab..480c4ce8f 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,7 @@ to a single process. - `maxHttpBufferSize` (`Number`): how many bytes or characters a message can be, before closing the session (to avoid DoS). Default value is `10E7`. + - `origins` (`String`): the allowed origins (`*`) - `allowRequest` (`Function`): 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 diff --git a/lib/server.js b/lib/server.js index 3884e9ce1..2ae3447e6 100644 --- a/lib/server.js +++ b/lib/server.js @@ -45,6 +45,7 @@ function Server (opts) { this.allowUpgrades = false !== opts.allowUpgrades; this.allowRequest = opts.allowRequest; this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false; + this.origins = opts.origins || '*'; this.cookiePath = false !== opts.cookiePath ? (opts.cookiePath || '/') : false; this.cookieHttpOnly = false !== opts.cookieHttpOnly; this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || true) : false; @@ -221,7 +222,7 @@ Server.prototype.handleRequest = function (req, res) { var self = this; this.verify(req, false, function (err, success) { if (!success) { - sendErrorMessage(req, res, err); + self.sendErrorMessage(req, res, err); return; } @@ -242,7 +243,7 @@ Server.prototype.handleRequest = function (req, res) { * @api private */ -function sendErrorMessage (req, res, code) { +Server.prototype.sendErrorMessage = function (req, res, code) { var headers = { 'Content-Type': 'application/json' }; var isForbidden = !Server.errorMessages.hasOwnProperty(code); @@ -254,12 +255,13 @@ function sendErrorMessage (req, res, code) { })); return; } + + headers['Access-Control-Allow-Origin'] = this.origins; + headers['Vary'] = 'Origin'; if (req.headers.origin) { headers['Access-Control-Allow-Credentials'] = 'true'; - headers['Access-Control-Allow-Origin'] = req.headers.origin; - } else { - headers['Access-Control-Allow-Origin'] = '*'; } + if (res !== undefined) { res.writeHead(400, headers); res.end(JSON.stringify({ @@ -267,7 +269,7 @@ function sendErrorMessage (req, res, code) { message: Server.errorMessages[code] })); } -} +}; /** * generate a socket id. @@ -293,9 +295,12 @@ Server.prototype.handshake = function (transportName, req) { var id = this.generateId(req); debug('handshaking client "%s"', id); + var opts = { + origins: this.origins + }; try { - var transport = new transports[transportName](req); + var transport = new transports[transportName](req, opts); if ('polling' === transportName) { transport.maxHttpBufferSize = this.maxHttpBufferSize; transport.httpCompression = this.httpCompression; @@ -309,7 +314,7 @@ Server.prototype.handshake = function (transportName, req) { transport.supportsBinary = true; } } catch (e) { - sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); + this.sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); return; } var socket = new Socket(id, this, transport, req); @@ -404,7 +409,10 @@ Server.prototype.onWebSocket = function (req, socket) { // transport error handling takes over socket.removeListener('error', onUpgradeError); - var transport = new transports[req._query.transport](req); + var opts = { + origins: this.origins + }; + var transport = new transports[req._query.transport](req, opts); if (req._query && req._query.b64) { transport.supportsBinary = false; } else { diff --git a/lib/transport.js b/lib/transport.js index 933dad5bb..2f8501d9e 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -26,12 +26,14 @@ function noop () {} * Transport constructor. * * @param {http.IncomingMessage} request + * @param {Object} opts allows the origins option to be passed along * @api public */ -function Transport (req) { +function Transport (req, opts) { this.readyState = 'open'; this.discarded = false; + this.origins = opts.origins; } /** diff --git a/lib/transports/index.js b/lib/transports/index.js index fcff3223a..91ef8ab53 100644 --- a/lib/transports/index.js +++ b/lib/transports/index.js @@ -27,10 +27,10 @@ exports.polling.upgradesTo = ['websocket']; * @api private */ -function polling (req) { +function polling (req, opts) { if ('string' === typeof req._query.j) { - return new JSONP(req); + return new JSONP(req, opts); } else { - return new XHR(req); + return new XHR(req, opts); } } diff --git a/lib/transports/polling-jsonp.js b/lib/transports/polling-jsonp.js index 62e66e779..450953ce5 100644 --- a/lib/transports/polling-jsonp.js +++ b/lib/transports/polling-jsonp.js @@ -21,8 +21,8 @@ module.exports = JSONP; * @api public */ -function JSONP (req) { - Polling.call(this, req); +function JSONP (req, opts) { + Polling.call(this, req, opts); this.head = '___eio[' + (req._query.j || '').replace(/[^0-9]/g, '') + ']('; this.foot = ');'; diff --git a/lib/transports/polling-xhr.js b/lib/transports/polling-xhr.js index 3562524e1..ddcd5ed99 100644 --- a/lib/transports/polling-xhr.js +++ b/lib/transports/polling-xhr.js @@ -18,8 +18,8 @@ module.exports = XHR; * @api public */ -function XHR (req) { - Polling.call(this, req); +function XHR (req, opts) { + Polling.call(this, req, opts); } /** @@ -58,11 +58,10 @@ XHR.prototype.onRequest = function (req) { XHR.prototype.headers = function (req, headers) { headers = headers || {}; + headers['Access-Control-Allow-Origin'] = this.origins; + headers['Vary'] = 'Origin'; if (req.headers.origin) { headers['Access-Control-Allow-Credentials'] = 'true'; - headers['Access-Control-Allow-Origin'] = req.headers.origin; - } else { - headers['Access-Control-Allow-Origin'] = '*'; } return Polling.prototype.headers.call(this, req, headers); diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 7c29c29ea..08048a914 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -27,8 +27,8 @@ module.exports = Polling; * @api public. */ -function Polling (req) { - Transport.call(this, req); +function Polling (req, opts) { + Transport.call(this, req, opts); this.closeTimeout = 30 * 1000; this.maxHttpBufferSize = null; diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 7d5511b1d..64a3a5fdf 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -21,8 +21,8 @@ module.exports = WebSocket; * @api public */ -function WebSocket (req) { - Transport.call(this, req); +function WebSocket (req, opts) { + Transport.call(this, req, opts); var self = this; this.socket = req.websocket; this.socket.on('message', this.onData.bind(this)); diff --git a/test/server.js b/test/server.js index 9da4d109b..b694f81aa 100644 --- a/test/server.js +++ b/test/server.js @@ -58,7 +58,7 @@ describe('server', function () { expect(res.body.code).to.be(0); expect(res.body.message).to.be('Transport unknown'); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.header['access-control-allow-origin']).to.be('*'); done(); }); }); @@ -75,7 +75,7 @@ describe('server', function () { expect(res.body.code).to.be(1); expect(res.body.message).to.be('Session ID unknown'); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.header['access-control-allow-origin']).to.be('*'); done(); }); }); @@ -416,7 +416,7 @@ describe('server', function () { expect(res.body.code).to.be(3); expect(res.body.message).to.be('Bad request'); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.header['access-control-allow-origin']).to.be('*'); done(); }); }); @@ -932,7 +932,7 @@ describe('server', function () { it('should trigger transport close before open for ws', function (done) { var opts = { transports: ['websocket'] }; listen(opts, function (port) { - var url = 'ws://%s:%d'.s('0.0.0.50', port); + var url = 'ws://%s:%d'.s('0.0.0.0', port); var socket = new eioc.Socket(url); socket.on('open', function () { done(new Error('Test invalidation')); @@ -2589,7 +2589,7 @@ describe('server', function () { describe('cors', function () { it('should handle OPTIONS requests', function (done) { - listen({handlePreflightRequest: true}, function (port) { + listen({handlePreflightRequest: true, origins: 'engine.io:*'}, function (port) { request.options('http://localhost:%d/engine.io/default/'.s(port)) .set('Origin', 'http://engine.io') .query({ transport: 'polling' }) @@ -2599,7 +2599,7 @@ describe('server', function () { expect(res.body.code).to.be(2); expect(res.body.message).to.be('Bad handshake method'); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.header['access-control-allow-origin']).to.be('engine.io:*'); done(); }); }); @@ -2624,7 +2624,7 @@ describe('server', function () { var headers = {}; if (req.headers.origin) { headers['Access-Control-Allow-Credentials'] = 'true'; - headers['Access-Control-Allow-Origin'] = req.headers.origin; + headers['Access-Control-Allow-Origin'] = '*'; } else { headers['Access-Control-Allow-Origin'] = '*'; } @@ -2642,7 +2642,7 @@ describe('server', function () { expect(res.status).to.be(200); expect(res.body).to.be.empty(); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.header['access-control-allow-origin']).to.be('*'); expect(res.header['access-control-allow-methods']).to.be('GET,HEAD,PUT,PATCH,POST,DELETE'); expect(res.header['access-control-allow-headers']).to.be('origin, content-type, accept'); done(); From 3e2f415f492195329dd9bad936fca93be9c6a691 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 2 Nov 2018 07:39:38 +0100 Subject: [PATCH 002/124] [chore] Bump ws to version 6.1.0 (#564) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index efe90d83f..1bd4c6ca5 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "base64id": "1.0.0", "debug": "~3.1.0", "engine.io-parser": "~2.1.0", - "ws": "~3.3.1", + "ws": "~6.1.0", "cookie": "0.3.1" }, "devDependencies": { From 2c856caf03a9cebdd7027751f10e3b2fc6743ba2 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 8 Nov 2018 00:21:51 +0100 Subject: [PATCH 003/124] [chore] Release 3.3.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1bd4c6ca5..ecc0af67c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "3.2.1", + "version": "3.3.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io", "author": "Guillermo Rauch ", @@ -35,7 +35,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "babel-preset-es2015": "^6.24.0", - "engine.io-client": "3.2.1", + "engine.io-client": "3.3.0", "eslint": "^4.5.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.7.0", From e081616a3f8506c452ceb0f42d7dee5e83be9600 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 19 Nov 2018 22:15:19 +0100 Subject: [PATCH 004/124] [chore] Point towards branch 'develop' of engine.io-client --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ecc0af67c..0e2905454 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "babel-preset-es2015": "^6.24.0", - "engine.io-client": "3.3.0", + "engine.io-client": "https://github.com/socketio/engine.io-client#develop", "eslint": "^4.5.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.7.0", From 995644525160d3a811f914deaf63542b4e4779b1 Mon Sep 17 00:00:00 2001 From: Oliver Salzburg Date: Mon, 19 Nov 2018 22:21:28 +0100 Subject: [PATCH 005/124] [fix] Replace deprecated Buffer usage (#565) The `Buffer` constructor has been deprecated in favor of safer alternatives. See https://nodejs.org/en/docs/guides/buffer-constructor-deprecation/ --- README.md | 2 +- lib/server.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 480c4ce8f..332e99c33 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ var server = engine.listen(80); server.on('connection', function(socket){ socket.send('utf 8 string'); - socket.send(new Buffer([0, 1, 2, 3, 4, 5])); // binary data + socket.send(Buffer.from([0, 1, 2, 3, 4, 5])); // binary data }); ``` diff --git a/lib/server.js b/lib/server.js index 2ae3447e6..afc808a12 100644 --- a/lib/server.js +++ b/lib/server.js @@ -359,8 +359,7 @@ Server.prototype.handleUpgrade = function (req, socket, upgradeHead) { return; } - var head = new Buffer(upgradeHead.length); // eslint-disable-line node/no-deprecated-api - upgradeHead.copy(head); + var head = Buffer.from(upgradeHead); // eslint-disable-line node/no-deprecated-api upgradeHead = null; // delegate to ws From 64d60446599e74e7cb28e83d42775a8a90af1c27 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 19 Nov 2018 22:27:16 +0100 Subject: [PATCH 006/124] [chore] Release 3.3.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0e2905454..5e3f411ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "3.3.0", + "version": "3.3.1", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io", "author": "Guillermo Rauch ", @@ -35,7 +35,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "babel-preset-es2015": "^6.24.0", - "engine.io-client": "https://github.com/socketio/engine.io-client#develop", + "engine.io-client": "3.3.1", "eslint": "^4.5.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.7.0", From ec4e12a063f1f3a837ab1cbcc399569bbc717c13 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 29 Nov 2018 22:49:34 +0100 Subject: [PATCH 007/124] [revert] Allow configuration of `Access-Control-Allow-Origin` value (#511) This reverts commit ebf1a96f4285a7326e42fd21b33f53b64c65050e. Related: https://github.com/socketio/socket.io/issues/3381 --- README.md | 1 - lib/server.js | 26 +++++++++----------------- lib/transport.js | 4 +--- lib/transports/index.js | 6 +++--- lib/transports/polling-jsonp.js | 4 ++-- lib/transports/polling-xhr.js | 9 +++++---- lib/transports/polling.js | 4 ++-- lib/transports/websocket.js | 4 ++-- test/server.js | 16 ++++++++-------- 9 files changed, 32 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 332e99c33..7c632064a 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,6 @@ to a single process. - `maxHttpBufferSize` (`Number`): how many bytes or characters a message can be, before closing the session (to avoid DoS). Default value is `10E7`. - - `origins` (`String`): the allowed origins (`*`) - `allowRequest` (`Function`): 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 diff --git a/lib/server.js b/lib/server.js index afc808a12..df4058ea5 100644 --- a/lib/server.js +++ b/lib/server.js @@ -45,7 +45,6 @@ function Server (opts) { this.allowUpgrades = false !== opts.allowUpgrades; this.allowRequest = opts.allowRequest; this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false; - this.origins = opts.origins || '*'; this.cookiePath = false !== opts.cookiePath ? (opts.cookiePath || '/') : false; this.cookieHttpOnly = false !== opts.cookieHttpOnly; this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || true) : false; @@ -222,7 +221,7 @@ Server.prototype.handleRequest = function (req, res) { var self = this; this.verify(req, false, function (err, success) { if (!success) { - self.sendErrorMessage(req, res, err); + sendErrorMessage(req, res, err); return; } @@ -243,7 +242,7 @@ Server.prototype.handleRequest = function (req, res) { * @api private */ -Server.prototype.sendErrorMessage = function (req, res, code) { +function sendErrorMessage (req, res, code) { var headers = { 'Content-Type': 'application/json' }; var isForbidden = !Server.errorMessages.hasOwnProperty(code); @@ -255,13 +254,12 @@ Server.prototype.sendErrorMessage = function (req, res, code) { })); return; } - - headers['Access-Control-Allow-Origin'] = this.origins; - headers['Vary'] = 'Origin'; if (req.headers.origin) { headers['Access-Control-Allow-Credentials'] = 'true'; + headers['Access-Control-Allow-Origin'] = req.headers.origin; + } else { + headers['Access-Control-Allow-Origin'] = '*'; } - if (res !== undefined) { res.writeHead(400, headers); res.end(JSON.stringify({ @@ -269,7 +267,7 @@ Server.prototype.sendErrorMessage = function (req, res, code) { message: Server.errorMessages[code] })); } -}; +} /** * generate a socket id. @@ -295,12 +293,9 @@ Server.prototype.handshake = function (transportName, req) { var id = this.generateId(req); debug('handshaking client "%s"', id); - var opts = { - origins: this.origins - }; try { - var transport = new transports[transportName](req, opts); + var transport = new transports[transportName](req); if ('polling' === transportName) { transport.maxHttpBufferSize = this.maxHttpBufferSize; transport.httpCompression = this.httpCompression; @@ -314,7 +309,7 @@ Server.prototype.handshake = function (transportName, req) { transport.supportsBinary = true; } } catch (e) { - this.sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); + sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); return; } var socket = new Socket(id, this, transport, req); @@ -408,10 +403,7 @@ Server.prototype.onWebSocket = function (req, socket) { // transport error handling takes over socket.removeListener('error', onUpgradeError); - var opts = { - origins: this.origins - }; - var transport = new transports[req._query.transport](req, opts); + var transport = new transports[req._query.transport](req); if (req._query && req._query.b64) { transport.supportsBinary = false; } else { diff --git a/lib/transport.js b/lib/transport.js index 2f8501d9e..933dad5bb 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -26,14 +26,12 @@ function noop () {} * Transport constructor. * * @param {http.IncomingMessage} request - * @param {Object} opts allows the origins option to be passed along * @api public */ -function Transport (req, opts) { +function Transport (req) { this.readyState = 'open'; this.discarded = false; - this.origins = opts.origins; } /** diff --git a/lib/transports/index.js b/lib/transports/index.js index 91ef8ab53..fcff3223a 100644 --- a/lib/transports/index.js +++ b/lib/transports/index.js @@ -27,10 +27,10 @@ exports.polling.upgradesTo = ['websocket']; * @api private */ -function polling (req, opts) { +function polling (req) { if ('string' === typeof req._query.j) { - return new JSONP(req, opts); + return new JSONP(req); } else { - return new XHR(req, opts); + return new XHR(req); } } diff --git a/lib/transports/polling-jsonp.js b/lib/transports/polling-jsonp.js index 450953ce5..62e66e779 100644 --- a/lib/transports/polling-jsonp.js +++ b/lib/transports/polling-jsonp.js @@ -21,8 +21,8 @@ module.exports = JSONP; * @api public */ -function JSONP (req, opts) { - Polling.call(this, req, opts); +function JSONP (req) { + Polling.call(this, req); this.head = '___eio[' + (req._query.j || '').replace(/[^0-9]/g, '') + ']('; this.foot = ');'; diff --git a/lib/transports/polling-xhr.js b/lib/transports/polling-xhr.js index ddcd5ed99..3562524e1 100644 --- a/lib/transports/polling-xhr.js +++ b/lib/transports/polling-xhr.js @@ -18,8 +18,8 @@ module.exports = XHR; * @api public */ -function XHR (req, opts) { - Polling.call(this, req, opts); +function XHR (req) { + Polling.call(this, req); } /** @@ -58,10 +58,11 @@ XHR.prototype.onRequest = function (req) { XHR.prototype.headers = function (req, headers) { headers = headers || {}; - headers['Access-Control-Allow-Origin'] = this.origins; - headers['Vary'] = 'Origin'; if (req.headers.origin) { headers['Access-Control-Allow-Credentials'] = 'true'; + headers['Access-Control-Allow-Origin'] = req.headers.origin; + } else { + headers['Access-Control-Allow-Origin'] = '*'; } return Polling.prototype.headers.call(this, req, headers); diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 08048a914..7c29c29ea 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -27,8 +27,8 @@ module.exports = Polling; * @api public. */ -function Polling (req, opts) { - Transport.call(this, req, opts); +function Polling (req) { + Transport.call(this, req); this.closeTimeout = 30 * 1000; this.maxHttpBufferSize = null; diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 64a3a5fdf..7d5511b1d 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -21,8 +21,8 @@ module.exports = WebSocket; * @api public */ -function WebSocket (req, opts) { - Transport.call(this, req, opts); +function WebSocket (req) { + Transport.call(this, req); var self = this; this.socket = req.websocket; this.socket.on('message', this.onData.bind(this)); diff --git a/test/server.js b/test/server.js index b694f81aa..9da4d109b 100644 --- a/test/server.js +++ b/test/server.js @@ -58,7 +58,7 @@ describe('server', function () { expect(res.body.code).to.be(0); expect(res.body.message).to.be('Transport unknown'); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('*'); + expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); done(); }); }); @@ -75,7 +75,7 @@ describe('server', function () { expect(res.body.code).to.be(1); expect(res.body.message).to.be('Session ID unknown'); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('*'); + expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); done(); }); }); @@ -416,7 +416,7 @@ describe('server', function () { expect(res.body.code).to.be(3); expect(res.body.message).to.be('Bad request'); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('*'); + expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); done(); }); }); @@ -932,7 +932,7 @@ describe('server', function () { it('should trigger transport close before open for ws', function (done) { var opts = { transports: ['websocket'] }; listen(opts, function (port) { - var url = 'ws://%s:%d'.s('0.0.0.0', port); + var url = 'ws://%s:%d'.s('0.0.0.50', port); var socket = new eioc.Socket(url); socket.on('open', function () { done(new Error('Test invalidation')); @@ -2589,7 +2589,7 @@ describe('server', function () { describe('cors', function () { it('should handle OPTIONS requests', function (done) { - listen({handlePreflightRequest: true, origins: 'engine.io:*'}, function (port) { + listen({handlePreflightRequest: true}, function (port) { request.options('http://localhost:%d/engine.io/default/'.s(port)) .set('Origin', 'http://engine.io') .query({ transport: 'polling' }) @@ -2599,7 +2599,7 @@ describe('server', function () { expect(res.body.code).to.be(2); expect(res.body.message).to.be('Bad handshake method'); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('engine.io:*'); + expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); done(); }); }); @@ -2624,7 +2624,7 @@ describe('server', function () { var headers = {}; if (req.headers.origin) { headers['Access-Control-Allow-Credentials'] = 'true'; - headers['Access-Control-Allow-Origin'] = '*'; + headers['Access-Control-Allow-Origin'] = req.headers.origin; } else { headers['Access-Control-Allow-Origin'] = '*'; } @@ -2642,7 +2642,7 @@ describe('server', function () { expect(res.status).to.be(200); expect(res.body).to.be.empty(); expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('*'); + expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); expect(res.header['access-control-allow-methods']).to.be('GET,HEAD,PUT,PATCH,POST,DELETE'); expect(res.header['access-control-allow-headers']).to.be('origin, content-type, accept'); done(); From cb0ac6fddcad12c454651bf0e1a312a154e228a4 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 29 Nov 2018 22:51:12 +0100 Subject: [PATCH 008/124] [chore] Release 3.3.2 --- package.json | 2 +- test/engine.io.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5e3f411ad..caac4c850 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "3.3.1", + "version": "3.3.2", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io", "author": "Guillermo Rauch ", diff --git a/test/engine.io.js b/test/engine.io.js index 9387094c9..c29d3934c 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -19,7 +19,7 @@ describe('engine', function () { expect(eio.protocol).to.be.a('number'); }); - it('should be the same version as client', function () { + it.skip('should be the same version as client', function () { var version = require('../package').version; expect(version).to.be(require('engine.io-client/package').version); }); From ad844f4b32a15f475f227a2b0277d6df2d063541 Mon Sep 17 00:00:00 2001 From: Oliver Salzburg Date: Tue, 18 Jun 2019 08:51:50 +0200 Subject: [PATCH 009/124] [fix] Deprecated Buffer usage in dependency (#585) The `Buffer` constructor has been deprecated in favor of safer alternatives. See https://nodejs.org/en/docs/guides/buffer-constructor-deprecation/ This was fixed in base64id@2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index caac4c850..0a93a82b9 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "license": "MIT", "dependencies": { "accepts": "~1.3.4", - "base64id": "1.0.0", + "base64id": "2.0.0", "debug": "~3.1.0", "engine.io-parser": "~2.1.0", "ws": "~6.1.0", From 5bbbfe241188b570c1a8417780baf81e344ce089 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 13 Sep 2019 10:26:18 +0200 Subject: [PATCH 010/124] [ci] remove Node.js 4 and 6 from the build matrix We keep Node.js 9 for compatibility with the 'uws' dependency (as Node.js 10 fails), but we'll upgrade later. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 968ece7d4..fbda89be8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ sudo: false language: node_js node_js: - - "4" - - "6" - "8" - "9" git: From a967626a1d1ec990090a63b4999337fd83abf223 Mon Sep 17 00:00:00 2001 From: Dimitar Nestorov Date: Wed, 15 May 2019 22:55:56 +0300 Subject: [PATCH 011/124] [chore] Bump debug to version 4.1.0 (#581) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a93a82b9..018ed62b6 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "dependencies": { "accepts": "~1.3.4", "base64id": "2.0.0", - "debug": "~3.1.0", + "debug": "~4.1.0", "engine.io-parser": "~2.1.0", "ws": "~6.1.0", "cookie": "0.3.1" From c1448951334c7cfc5f1d1fff83c35117b6cf729f Mon Sep 17 00:00:00 2001 From: Brian Kopp Date: Fri, 13 Sep 2019 03:21:37 -0600 Subject: [PATCH 012/124] [feat] add additional debug messages (#586) These additional messages will help more quickly diagnose the reason for error messages. --- lib/server.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/server.js b/lib/server.js index df4058ea5..2d9a45c7a 100644 --- a/lib/server.js +++ b/lib/server.js @@ -148,6 +148,7 @@ Server.prototype.verify = function (req, upgrade, fn) { var isOriginInvalid = checkInvalidHeaderChar(req.headers.origin); if (isOriginInvalid) { req.headers.origin = null; + debug('origin header invalid'); return fn(Server.errors.BAD_REQUEST, false); } @@ -155,6 +156,7 @@ Server.prototype.verify = function (req, upgrade, fn) { var sid = req._query.sid; if (sid) { if (!this.clients.hasOwnProperty(sid)) { + debug('unknown sid "%s"', sid); return fn(Server.errors.UNKNOWN_SID, false); } if (!upgrade && this.clients[sid].transport.name !== transport) { @@ -309,6 +311,7 @@ Server.prototype.handshake = function (transportName, req) { transport.supportsBinary = true; } } catch (e) { + debug('error handshaking to transport "%s"', transportName); sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); return; } @@ -552,23 +555,33 @@ function checkInvalidHeaderChar(val) { val += ''; if (val.length < 1) return false; - if (!validHdrChars[val.charCodeAt(0)]) + if (!validHdrChars[val.charCodeAt(0)]) { + debug('invalid header, index 0, char "%s"', val.charCodeAt(0)); return true; + } if (val.length < 2) return false; - if (!validHdrChars[val.charCodeAt(1)]) + if (!validHdrChars[val.charCodeAt(1)]) { + debug('invalid header, index 1, char "%s"', val.charCodeAt(1)); return true; + } if (val.length < 3) return false; - if (!validHdrChars[val.charCodeAt(2)]) + if (!validHdrChars[val.charCodeAt(2)]) { + debug('invalid header, index 2, char "%s"', val.charCodeAt(2)); return true; + } if (val.length < 4) return false; - if (!validHdrChars[val.charCodeAt(3)]) + if (!validHdrChars[val.charCodeAt(3)]) { + debug('invalid header, index 3, char "%s"', val.charCodeAt(3)); return true; + } for (var i = 4; i < val.length; ++i) { - if (!validHdrChars[val.charCodeAt(i)]) + if (!validHdrChars[val.charCodeAt(i)]) { + debug('invalid header, index "%i", char "%s"', i, val.charCodeAt(i)); return true; + } } return false; } From c471e03e09ce2201c8807fda94babf85455c4bb2 Mon Sep 17 00:00:00 2001 From: Yosi Attias Date: Mon, 12 Aug 2019 20:11:56 +0300 Subject: [PATCH 013/124] [chore] Bump `ws` to latest version (#587) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 018ed62b6..b235dab57 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,10 @@ "dependencies": { "accepts": "~1.3.4", "base64id": "2.0.0", + "cookie": "0.3.1", "debug": "~4.1.0", "engine.io-parser": "~2.1.0", - "ws": "~6.1.0", - "cookie": "0.3.1" + "ws": "^7.1.2" }, "devDependencies": { "babel-eslint": "^8.0.2", From 7bf75812c300aed1b62a10980c84187fddc2d346 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 13 Sep 2019 13:46:43 +0200 Subject: [PATCH 014/124] [chore] Bump engine.io-parser to version 2.2.0 Diff: https://github.com/socketio/engine.io-parser/compare/2.1.3...2.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b235dab57..a8e37e3fe 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "base64id": "2.0.0", "cookie": "0.3.1", "debug": "~4.1.0", - "engine.io-parser": "~2.1.0", + "engine.io-parser": "~2.2.0", "ws": "^7.1.2" }, "devDependencies": { From ecfcc69a7ae8c63dde8861a87715a8be718d510e Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 13 Sep 2019 13:51:49 +0200 Subject: [PATCH 015/124] [chore] Release 3.4.0 Diff: https://github.com/socketio/engine.io/compare/3.3.2...3.4.0 --- package.json | 4 ++-- test/engine.io.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a8e37e3fe..d99b2c31b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "3.3.2", + "version": "3.4.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io", "author": "Guillermo Rauch ", @@ -35,7 +35,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "babel-preset-es2015": "^6.24.0", - "engine.io-client": "3.3.1", + "engine.io-client": "3.4.0", "eslint": "^4.5.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.7.0", diff --git a/test/engine.io.js b/test/engine.io.js index c29d3934c..9387094c9 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -19,7 +19,7 @@ describe('engine', function () { expect(eio.protocol).to.be.a('number'); }); - it.skip('should be the same version as client', function () { + it('should be the same version as client', function () { var version = require('../package').version; expect(version).to.be(require('engine.io-client/package').version); }); From da93fb6ef3d6f6e2a93226480823683accb82aa3 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sun, 12 Jan 2020 22:33:50 +0100 Subject: [PATCH 016/124] refactor: migrate to ES6 syntax --- lib/engine.io.js | 13 +- lib/server.js | 824 +++++---- lib/socket.js | 831 +++++---- lib/transport.js | 197 +-- lib/transports/index.js | 9 +- lib/transports/polling-jsonp.js | 125 +- lib/transports/polling-xhr.js | 104 +- lib/transports/polling.js | 665 ++++---- lib/transports/websocket.js | 216 ++- package-lock.json | 2780 +++++++++++++++++++++++++++++++ 10 files changed, 4191 insertions(+), 1573 deletions(-) create mode 100644 package-lock.json diff --git a/lib/engine.io.js b/lib/engine.io.js index 2a211c2b2..6fe013880 100644 --- a/lib/engine.io.js +++ b/lib/engine.io.js @@ -2,7 +2,8 @@ * Module dependencies. */ -var http = require('http'); +const http = require('http'); +const Server = require('./server'); /** * Invoking the library as a function delegates to attach if the first argument @@ -25,7 +26,7 @@ exports = module.exports = function () { } // if first argument is not an http server, then just make a regular eio server - return exports.Server.apply(null, arguments); + return new Server(arguments); }; /** @@ -42,7 +43,7 @@ exports.protocol = 1; * @api public */ -exports.Server = require('./server'); +exports.Server = Server; /** * Expose Socket constructor. @@ -94,13 +95,13 @@ function listen (port, options, fn) { options = {}; } - var server = http.createServer(function (req, res) { + const server = http.createServer(function (req, res) { res.writeHead(501); res.end('Not Implemented'); }); // create engine server - var engine = exports.attach(server, options); + const engine = exports.attach(server, options); engine.httpServer = server; server.listen(port, fn); @@ -120,7 +121,7 @@ function listen (port, options, fn) { exports.attach = attach; function attach (server, options) { - var engine = new exports.Server(options); + const engine = new Server(options); engine.attach(server, options); return engine; } diff --git a/lib/server.js b/lib/server.js index 2d9a45c7a..2922c6323 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,239 +1,432 @@ +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'); + +class Server extends EventEmitter { + /** + * Server constructor. + * + * @param {Object} options + * @api public + */ + constructor (opts) { + super(); + + this.clients = {}; + this.clientsCount = 0; + + opts = opts || {}; + + this.wsEngine = opts.wsEngine || process.env.EIO_WS_ENGINE || 'ws'; + this.pingTimeout = opts.pingTimeout || 5000; + this.pingInterval = opts.pingInterval || 25000; + this.upgradeTimeout = opts.upgradeTimeout || 10000; + this.maxHttpBufferSize = opts.maxHttpBufferSize || 10E7; + this.transports = opts.transports || Object.keys(transports); + this.allowUpgrades = false !== opts.allowUpgrades; + this.allowRequest = opts.allowRequest; + this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false; + this.cookiePath = false !== opts.cookiePath ? (opts.cookiePath || '/') : false; + this.cookieHttpOnly = false !== opts.cookieHttpOnly; + this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || true) : false; + this.httpCompression = false !== opts.httpCompression ? (opts.httpCompression || {}) : false; + this.initialPacket = opts.initialPacket; + + // initialize compression options + ['perMessageDeflate', 'httpCompression'].forEach((type) => { + let compression = this[type]; + if (true === compression) this[type] = compression = {}; + if (compression && null == compression.threshold) { + compression.threshold = 1024; + } + }); -/** - * Module dependencies. - */ + this.init(); + } -var qs = require('querystring'); -var parse = require('url').parse; -var base64id = require('base64id'); -var transports = require('./transports'); -var EventEmitter = require('events').EventEmitter; -var Socket = require('./socket'); -var util = require('util'); -var debug = require('debug')('engine'); -var cookieMod = require('cookie'); + /** + * Initialize websocket server + * + * @api private + */ + init () { + if (!~this.transports.indexOf('websocket')) return; + + if (this.ws) this.ws.close(); + + let wsModule; + switch (this.wsEngine) { + case 'uws': wsModule = require('uws'); break; + case 'ws': wsModule = require('ws'); break; + default: throw new Error('unknown wsEngine'); + } + this.ws = new wsModule.Server({ + noServer: true, + clientTracking: false, + perMessageDeflate: this.perMessageDeflate, + maxPayload: this.maxHttpBufferSize + }); + } -/** - * Module exports. - */ + /** + * Returns a list of available transports for upgrade given a certain transport. + * + * @return {Array} + * @api public + */ + upgrades (transport) { + if (!this.allowUpgrades) return []; + return transports[transport].upgradesTo || []; + } -module.exports = Server; + /** + * Verifies a request. + * + * @param {http.IncomingMessage} + * @return {Boolean} whether the request is valid + * @api private + */ + verify (req, upgrade, fn) { + // transport check + const transport = req._query.transport; + if (!~this.transports.indexOf(transport)) { + debug('unknown transport "%s"', transport); + return fn(Server.errors.UNKNOWN_TRANSPORT, false); + } -/** - * Server constructor. - * - * @param {Object} options - * @api public - */ + // 'Origin' header check + const isOriginInvalid = checkInvalidHeaderChar(req.headers.origin); + if (isOriginInvalid) { + req.headers.origin = null; + debug('origin header invalid'); + return fn(Server.errors.BAD_REQUEST, false); + } + + // sid check + const sid = req._query.sid; + if (sid) { + if (!this.clients.hasOwnProperty(sid)) { + debug('unknown sid "%s"', sid); + return fn(Server.errors.UNKNOWN_SID, false); + } + if (!upgrade && this.clients[sid].transport.name !== transport) { + debug('bad request: unexpected transport without upgrade'); + return fn(Server.errors.BAD_REQUEST, false); + } + } else { + // handshake is GET only + if ('GET' !== req.method) return fn(Server.errors.BAD_HANDSHAKE_METHOD, false); + if (!this.allowRequest) return fn(null, true); + return this.allowRequest(req, fn); + } -function Server (opts) { - if (!(this instanceof Server)) { - return new Server(opts); + fn(null, true); } - this.clients = {}; - this.clientsCount = 0; - - opts = opts || {}; - - this.wsEngine = opts.wsEngine || process.env.EIO_WS_ENGINE || 'ws'; - this.pingTimeout = opts.pingTimeout || 5000; - this.pingInterval = opts.pingInterval || 25000; - this.upgradeTimeout = opts.upgradeTimeout || 10000; - this.maxHttpBufferSize = opts.maxHttpBufferSize || 10E7; - this.transports = opts.transports || Object.keys(transports); - this.allowUpgrades = false !== opts.allowUpgrades; - this.allowRequest = opts.allowRequest; - this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false; - this.cookiePath = false !== opts.cookiePath ? (opts.cookiePath || '/') : false; - this.cookieHttpOnly = false !== opts.cookieHttpOnly; - this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || true) : false; - this.httpCompression = false !== opts.httpCompression ? (opts.httpCompression || {}) : false; - this.initialPacket = opts.initialPacket; - - var self = this; - - // initialize compression options - ['perMessageDeflate', 'httpCompression'].forEach(function (type) { - var compression = self[type]; - if (true === compression) self[type] = compression = {}; - if (compression && null == compression.threshold) { - compression.threshold = 1024; + /** + * Prepares a request by processing the query string. + * + * @api 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) : {}; } - }); + } - this.init(); -} + /** + * Closes all clients. + * + * @api public + */ + close () { + debug('closing all open clients'); + for (let i in this.clients) { + if (this.clients.hasOwnProperty(i)) { + 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 + } + return this; + } -/** - * Protocol errors mappings. - */ + /** + * Handles an Engine.IO HTTP request. + * + * @param {http.IncomingMessage} request + * @param {http.ServerResponse|http.OutgoingMessage} response + * @api public + */ + handleRequest (req, res) { + debug('handling "%s" http request "%s"', req.method, req.url); + this.prepare(req); + req.res = res; + + const self = this; + this.verify(req, false, function (err, success) { + if (!success) { + sendErrorMessage(req, res, err); + return; + } -Server.errors = { - UNKNOWN_TRANSPORT: 0, - UNKNOWN_SID: 1, - BAD_HANDSHAKE_METHOD: 2, - BAD_REQUEST: 3, - FORBIDDEN: 4 -}; + if (req._query.sid) { + debug('setting new request for existing client'); + self.clients[req._query.sid].transport.onRequest(req); + } else { + self.handshake(req._query.transport, req); + } + }); + } -Server.errorMessages = { - 0: 'Transport unknown', - 1: 'Session ID unknown', - 2: 'Bad handshake method', - 3: 'Bad request', - 4: 'Forbidden' -}; + /** + * generate a socket id. + * Overwrite this method to generate your custom socket id + * + * @param {Object} request object + * @api public + */ + generateId (req) { + return base64id.generateId(); + } -/** - * Inherits from EventEmitter. - */ + /** + * Handshakes a new client. + * + * @param {String} transport name + * @param {Object} request object + * @api private + */ + handshake (transportName, req) { + const id = this.generateId(req); + + debug('handshaking client "%s"', id); + + try { + var transport = new transports[transportName](req); + if ('polling' === transportName) { + transport.maxHttpBufferSize = this.maxHttpBufferSize; + transport.httpCompression = this.httpCompression; + } else if ('websocket' === transportName) { + transport.perMessageDeflate = this.perMessageDeflate; + } -util.inherits(Server, EventEmitter); + if (req._query && req._query.b64) { + transport.supportsBinary = false; + } else { + transport.supportsBinary = true; + } + } catch (e) { + debug('error handshaking to transport "%s"', transportName); + sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); + return; + } + const socket = new Socket(id, this, transport, req); + const self = this; + + if (false !== this.cookie) { + transport.on('headers', function (headers) { + headers['Set-Cookie'] = cookieMod.serialize(self.cookie, id, + { + path: self.cookiePath, + httpOnly: self.cookiePath ? self.cookieHttpOnly : false + }); + }); + } -/** - * Initialize websocket server - * - * @api private - */ + transport.onRequest(req); -Server.prototype.init = function () { - if (!~this.transports.indexOf('websocket')) return; + this.clients[id] = socket; + this.clientsCount++; - if (this.ws) this.ws.close(); + socket.once('close', function () { + delete self.clients[id]; + self.clientsCount--; + }); - var wsModule; - switch (this.wsEngine) { - case 'uws': wsModule = require('uws'); break; - case 'ws': wsModule = require('ws'); break; - default: throw new Error('unknown wsEngine'); + this.emit('connection', socket); } - this.ws = new wsModule.Server({ - noServer: true, - clientTracking: false, - perMessageDeflate: this.perMessageDeflate, - maxPayload: this.maxHttpBufferSize - }); -}; -/** - * Returns a list of available transports for upgrade given a certain transport. - * - * @return {Array} - * @api public - */ - -Server.prototype.upgrades = function (transport) { - if (!this.allowUpgrades) return []; - return transports[transport].upgradesTo || []; -}; + /** + * Handles an Engine.IO HTTP Upgrade. + * + * @api public + */ + handleUpgrade (req, socket, upgradeHead) { + this.prepare(req); + + const self = this; + this.verify(req, true, function (err, success) { + if (!success) { + abortConnection(socket, err); + return; + } -/** - * Verifies a request. - * - * @param {http.IncomingMessage} - * @return {Boolean} whether the request is valid - * @api private - */ + const head = Buffer.from(upgradeHead); // eslint-disable-line node/no-deprecated-api + upgradeHead = null; -Server.prototype.verify = function (req, upgrade, fn) { - // transport check - var transport = req._query.transport; - if (!~this.transports.indexOf(transport)) { - debug('unknown transport "%s"', transport); - return fn(Server.errors.UNKNOWN_TRANSPORT, false); + // delegate to ws + self.ws.handleUpgrade(req, socket, head, function (conn) { + self.onWebSocket(req, conn); + }); + }); } - // 'Origin' header check - var isOriginInvalid = checkInvalidHeaderChar(req.headers.origin); - if (isOriginInvalid) { - req.headers.origin = null; - debug('origin header invalid'); - return fn(Server.errors.BAD_REQUEST, false); - } + /** + * Called upon a ws.io connection. + * + * @param {ws.Socket} websocket + * @api private + */ + onWebSocket (req, socket) { + socket.on('error', onUpgradeError); + + if (transports[req._query.transport] !== undefined && !transports[req._query.transport].prototype.handlesUpgrades) { + debug('transport doesnt handle upgraded requests'); + socket.close(); + return; + } - // sid check - var sid = req._query.sid; - if (sid) { - if (!this.clients.hasOwnProperty(sid)) { - debug('unknown sid "%s"', sid); - return fn(Server.errors.UNKNOWN_SID, false); + // get client id + const id = req._query.sid; + + // keep a reference to the ws.Socket + req.websocket = socket; + + if (id) { + const client = this.clients[id]; + if (!client) { + debug('upgrade attempt for closed client'); + socket.close(); + } else if (client.upgrading) { + debug('transport has already been trying to upgrade'); + socket.close(); + } else if (client.upgraded) { + debug('transport had already been upgraded'); + socket.close(); + } else { + debug('upgrading existing transport'); + + // transport error handling takes over + socket.removeListener('error', onUpgradeError); + + const transport = new transports[req._query.transport](req); + if (req._query && req._query.b64) { + transport.supportsBinary = false; + } else { + transport.supportsBinary = true; + } + transport.perMessageDeflate = this.perMessageDeflate; + client.maybeUpgrade(transport); + } + } else { + // transport error handling takes over + socket.removeListener('error', onUpgradeError); + + this.handshake(req._query.transport, req); } - if (!upgrade && this.clients[sid].transport.name !== transport) { - debug('bad request: unexpected transport without upgrade'); - return fn(Server.errors.BAD_REQUEST, false); + + function onUpgradeError () { + debug('websocket error before upgrade'); + // socket.close() not needed } - } else { - // handshake is GET only - if ('GET' !== req.method) return fn(Server.errors.BAD_HANDSHAKE_METHOD, false); - if (!this.allowRequest) return fn(null, true); - return this.allowRequest(req, fn); } - fn(null, true); -}; - -/** - * Prepares a request by processing the query string. - * - * @api private - */ + /** + * Captures upgrade requests for a http.Server. + * + * @param {http.Server} server + * @param {Object} options + * @api public + */ + attach (server, options) { + const self = this; + options = options || {}; + let path = (options.path || '/engine.io').replace(/\/$/, ''); + + const destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000; + + // normalize path + path += '/'; + + function check (req) { + if ('OPTIONS' === req.method && false === options.handlePreflightRequest) { + return false; + } + return path === req.url.substr(0, path.length); + } -Server.prototype.prepare = function (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) : {}; - } -}; + // 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)); -/** - * Closes all clients. - * - * @api public - */ + // add request handler + server.on('request', function (req, res) { + if (check(req)) { + debug('intercepting request for path "%s"', path); + if ('OPTIONS' === req.method && 'function' === typeof options.handlePreflightRequest) { + options.handlePreflightRequest.call(server, req, res); + } else { + self.handleRequest(req, res); + } + } else { + let i = 0; + const l = listeners.length; + for (; i < l; i++) { + listeners[i].call(server, req, res); + } + } + }); -Server.prototype.close = function () { - debug('closing all open clients'); - for (var i in this.clients) { - if (this.clients.hasOwnProperty(i)) { - this.clients[i].close(true); + if (~self.transports.indexOf('websocket')) { + server.on('upgrade', function (req, socket, head) { + if (check(req)) { + self.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 + // and if no eio thing handles the upgrade + // then the socket needs to die! + setTimeout(function () { + if (socket.writable && socket.bytesWritten <= 0) { + return socket.end(); + } + }, destroyUpgradeTimeout); + } + }); } } - 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 - } - return this; -}; +} /** - * Handles an Engine.IO HTTP request. - * - * @param {http.IncomingMessage} request - * @param {http.ServerResponse|http.OutgoingMessage} response - * @api public + * Protocol errors mappings. */ -Server.prototype.handleRequest = function (req, res) { - debug('handling "%s" http request "%s"', req.method, req.url); - this.prepare(req); - req.res = res; - - var self = this; - this.verify(req, false, function (err, success) { - if (!success) { - sendErrorMessage(req, res, err); - return; - } +Server.errors = { + UNKNOWN_TRANSPORT: 0, + UNKNOWN_SID: 1, + BAD_HANDSHAKE_METHOD: 2, + BAD_REQUEST: 3, + FORBIDDEN: 4 +}; - if (req._query.sid) { - debug('setting new request for existing client'); - self.clients[req._query.sid].transport.onRequest(req); - } else { - self.handshake(req._query.transport, req); - } - }); +Server.errorMessages = { + 0: 'Transport unknown', + 1: 'Session ID unknown', + 2: 'Bad handshake method', + 3: 'Bad request', + 4: 'Forbidden' }; /** @@ -245,9 +438,9 @@ Server.prototype.handleRequest = function (req, res) { */ function sendErrorMessage (req, res, code) { - var headers = { 'Content-Type': 'application/json' }; + const headers = {'Content-Type': 'application/json'}; - var isForbidden = !Server.errorMessages.hasOwnProperty(code); + const isForbidden = !Server.errorMessages.hasOwnProperty(code); if (isForbidden) { res.writeHead(403, headers); res.end(JSON.stringify({ @@ -271,229 +464,6 @@ function sendErrorMessage (req, res, code) { } } -/** - * generate a socket id. - * Overwrite this method to generate your custom socket id - * - * @param {Object} request object - * @api public - */ - -Server.prototype.generateId = function (req) { - return base64id.generateId(); -}; - -/** - * Handshakes a new client. - * - * @param {String} transport name - * @param {Object} request object - * @api private - */ - -Server.prototype.handshake = function (transportName, req) { - var id = this.generateId(req); - - debug('handshaking client "%s"', id); - - try { - var transport = new transports[transportName](req); - if ('polling' === transportName) { - transport.maxHttpBufferSize = this.maxHttpBufferSize; - transport.httpCompression = this.httpCompression; - } else if ('websocket' === transportName) { - transport.perMessageDeflate = this.perMessageDeflate; - } - - if (req._query && req._query.b64) { - transport.supportsBinary = false; - } else { - transport.supportsBinary = true; - } - } catch (e) { - debug('error handshaking to transport "%s"', transportName); - sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); - return; - } - var socket = new Socket(id, this, transport, req); - var self = this; - - if (false !== this.cookie) { - transport.on('headers', function (headers) { - headers['Set-Cookie'] = cookieMod.serialize(self.cookie, id, - { - path: self.cookiePath, - httpOnly: self.cookiePath ? self.cookieHttpOnly : false - }); - }); - } - - transport.onRequest(req); - - this.clients[id] = socket; - this.clientsCount++; - - socket.once('close', function () { - delete self.clients[id]; - self.clientsCount--; - }); - - this.emit('connection', socket); -}; - -/** - * Handles an Engine.IO HTTP Upgrade. - * - * @api public - */ - -Server.prototype.handleUpgrade = function (req, socket, upgradeHead) { - this.prepare(req); - - var self = this; - this.verify(req, true, function (err, success) { - if (!success) { - abortConnection(socket, err); - return; - } - - var head = Buffer.from(upgradeHead); // eslint-disable-line node/no-deprecated-api - upgradeHead = null; - - // delegate to ws - self.ws.handleUpgrade(req, socket, head, function (conn) { - self.onWebSocket(req, conn); - }); - }); -}; - -/** - * Called upon a ws.io connection. - * - * @param {ws.Socket} websocket - * @api private - */ - -Server.prototype.onWebSocket = function (req, socket) { - socket.on('error', onUpgradeError); - - if (transports[req._query.transport] !== undefined && !transports[req._query.transport].prototype.handlesUpgrades) { - debug('transport doesnt handle upgraded requests'); - socket.close(); - return; - } - - // get client id - var id = req._query.sid; - - // keep a reference to the ws.Socket - req.websocket = socket; - - if (id) { - var client = this.clients[id]; - if (!client) { - debug('upgrade attempt for closed client'); - socket.close(); - } else if (client.upgrading) { - debug('transport has already been trying to upgrade'); - socket.close(); - } else if (client.upgraded) { - debug('transport had already been upgraded'); - socket.close(); - } else { - debug('upgrading existing transport'); - - // transport error handling takes over - socket.removeListener('error', onUpgradeError); - - var transport = new transports[req._query.transport](req); - if (req._query && req._query.b64) { - transport.supportsBinary = false; - } else { - transport.supportsBinary = true; - } - transport.perMessageDeflate = this.perMessageDeflate; - client.maybeUpgrade(transport); - } - } else { - // transport error handling takes over - socket.removeListener('error', onUpgradeError); - - this.handshake(req._query.transport, req); - } - - function onUpgradeError () { - debug('websocket error before upgrade'); - // socket.close() not needed - } -}; - -/** - * Captures upgrade requests for a http.Server. - * - * @param {http.Server} server - * @param {Object} options - * @api public - */ - -Server.prototype.attach = function (server, options) { - var self = this; - options = options || {}; - var path = (options.path || '/engine.io').replace(/\/$/, ''); - - var destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000; - - // normalize path - path += '/'; - - function check (req) { - if ('OPTIONS' === req.method && false === options.handlePreflightRequest) { - return false; - } - return path === req.url.substr(0, path.length); - } - - // cache and clean up listeners - var listeners = server.listeners('request').slice(0); - server.removeAllListeners('request'); - server.on('close', self.close.bind(self)); - server.on('listening', self.init.bind(self)); - - // add request handler - server.on('request', function (req, res) { - if (check(req)) { - debug('intercepting request for path "%s"', path); - if ('OPTIONS' === req.method && 'function' === typeof options.handlePreflightRequest) { - options.handlePreflightRequest.call(server, req, res); - } else { - self.handleRequest(req, res); - } - } else { - for (var i = 0, l = listeners.length; i < l; i++) { - listeners[i].call(server, req, res); - } - } - }); - - if (~self.transports.indexOf('websocket')) { - server.on('upgrade', function (req, socket, head) { - if (check(req)) { - self.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 - // and if no eio thing handles the upgrade - // then the socket needs to die! - setTimeout(function () { - if (socket.writable && socket.bytesWritten <= 0) { - return socket.end(); - } - }, destroyUpgradeTimeout); - } - }); - } -}; - /** * Closes the connection * @@ -504,8 +474,8 @@ Server.prototype.attach = function (server, options) { function abortConnection (socket, code) { if (socket.writable) { - var message = Server.errorMessages.hasOwnProperty(code) ? Server.errorMessages[code] : String(code || ''); - var length = Buffer.byteLength(message); + const message = Server.errorMessages.hasOwnProperty(code) ? Server.errorMessages[code] : String(code || ''); + const length = Buffer.byteLength(message); socket.write( 'HTTP/1.1 400 Bad Request\r\n' + 'Connection: close\r\n' + @@ -518,6 +488,8 @@ function abortConnection (socket, code) { socket.destroy(); } +module.exports = Server; + /* eslint-disable */ /** @@ -532,7 +504,7 @@ function abortConnection (socket, code) { * so take care when making changes to the implementation so that the source * code size does not exceed v8's default max_inlined_source_size setting. **/ -var validHdrChars = [ +const validHdrChars = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 - 15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 - 47 @@ -549,7 +521,7 @@ var validHdrChars = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // ... 255 -]; +] function checkInvalidHeaderChar(val) { val += ''; @@ -577,7 +549,7 @@ function checkInvalidHeaderChar(val) { debug('invalid header, index 3, char "%s"', val.charCodeAt(3)); return true; } - for (var i = 4; i < val.length; ++i) { + for (let i = 4; i < val.length; ++i) { if (!validHdrChars[val.charCodeAt(i)]) { debug('invalid header, index "%i", char "%s"', i, val.charCodeAt(i)); return true; diff --git a/lib/socket.js b/lib/socket.js index 177b25c69..415a65a1d 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -1,486 +1,465 @@ -/** - * Module dependencies. - */ - -var EventEmitter = require('events').EventEmitter; -var util = require('util'); -var debug = require('debug')('engine:socket'); +const EventEmitter = require('events'); +const debug = require('debug')('engine:socket'); + +class Socket extends EventEmitter { + /** + * Client class (abstract). + * + * @api private + */ + constructor (id, server, transport, req) { + super(); + this.id = id; + this.server = server; + this.upgrading = false; + this.upgraded = false; + this.readyState = 'opening'; + this.writeBuffer = []; + this.packetsFn = []; + this.sentCallbackFn = []; + this.cleanupFn = []; + this.request = req; -/** - * Module exports. - */ + // Cache IP since it might not be in the req later + if (req.websocket && req.websocket._socket) { + this.remoteAddress = req.websocket._socket.remoteAddress; + } else { + this.remoteAddress = req.connection.remoteAddress; + } -module.exports = Socket; + this.checkIntervalTimer = null; + this.upgradeTimeoutTimer = null; + this.pingTimeoutTimer = null; -/** - * Client class (abstract). - * - * @api private - */ - -function Socket (id, server, transport, req) { - this.id = id; - this.server = server; - this.upgrading = false; - this.upgraded = false; - this.readyState = 'opening'; - this.writeBuffer = []; - this.packetsFn = []; - this.sentCallbackFn = []; - this.cleanupFn = []; - this.request = req; - - // Cache IP since it might not be in the req later - if (req.websocket && req.websocket._socket) { - this.remoteAddress = req.websocket._socket.remoteAddress; - } else { - this.remoteAddress = req.connection.remoteAddress; + this.setTransport(transport); + this.onOpen(); } - this.checkIntervalTimer = null; - this.upgradeTimeoutTimer = null; - this.pingTimeoutTimer = null; - - this.setTransport(transport); - this.onOpen(); -} - -/** - * Inherits from EventEmitter. - */ + /** + * Called upon transport considered open. + * + * @api private + */ + onOpen () { + this.readyState = 'open'; + + // sends an `open` packet + this.transport.sid = this.id; + this.sendPacket('open', JSON.stringify({ + sid: this.id, + upgrades: this.getAvailableUpgrades(), + pingInterval: this.server.pingInterval, + pingTimeout: this.server.pingTimeout + })); + + if (this.server.initialPacket) { + this.sendPacket('message', this.server.initialPacket); + } -util.inherits(Socket, EventEmitter); + this.emit('open'); + this.setPingTimeout(); + } -/** - * Called upon transport considered open. - * - * @api private - */ + /** + * Called upon transport packet. + * + * @param {Object} packet + * @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.setPingTimeout(); + + switch (packet.type) { + case 'ping': + debug('got ping'); + this.sendPacket('pong'); + 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'); + } + } -Socket.prototype.onOpen = function () { - this.readyState = 'open'; + /** + * Called upon transport error. + * + * @param {Error} error object + * @api private + */ + onError (err) { + debug('transport error'); + this.onClose('transport error', err); + } - // sends an `open` packet - this.transport.sid = this.id; - this.sendPacket('open', JSON.stringify({ - sid: this.id, - upgrades: this.getAvailableUpgrades(), - pingInterval: this.server.pingInterval, - pingTimeout: this.server.pingTimeout - })); + /** + * Sets and resets ping timeout timer based on client pings. + * + * @api private + */ + setPingTimeout () { + clearTimeout(this.pingTimeoutTimer); + this.pingTimeoutTimer = setTimeout(() => { + this.onClose('ping timeout'); + }, this.server.pingInterval + this.server.pingTimeout); + } - if (this.server.initialPacket) { - this.sendPacket('message', this.server.initialPacket); + /** + * Attaches handlers for the given transport. + * + * @param {Transport} transport + * @api private + */ + setTransport (transport) { + const onError = this.onError.bind(this); + const onPacket = this.onPacket.bind(this); + const flush = this.flush.bind(this); + const onClose = this.onClose.bind(this, 'transport close'); + + this.transport = transport; + this.transport.once('error', onError); + this.transport.on('packet', onPacket); + this.transport.on('drain', flush); + this.transport.once('close', onClose); + // this function will manage packet events (also message callbacks) + this.setupSendCallback(); + + this.cleanupFn.push(function () { + transport.removeListener('error', onError); + transport.removeListener('packet', onPacket); + transport.removeListener('drain', flush); + transport.removeListener('close', onClose); + }); } - this.emit('open'); - this.setPingTimeout(); -}; - -/** - * Called upon transport packet. - * - * @param {Object} packet - * @api private - */ - -Socket.prototype.onPacket = function (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.setPingTimeout(); + /** + * Upgrades socket to the given transport + * + * @param {Transport} transport + * @api private + */ + maybeUpgrade (transport) { + debug('might upgrade socket transport from "%s" to "%s"' + , this.transport.name, transport.name); - switch (packet.type) { - case 'ping': - debug('got ping'); - this.sendPacket('pong'); - 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'); - } -}; - -/** - * Called upon transport error. - * - * @param {Error} error object - * @api private - */ - -Socket.prototype.onError = function (err) { - debug('transport error'); - this.onClose('transport error', err); -}; - -/** - * Sets and resets ping timeout timer based on client pings. - * - * @api private - */ - -Socket.prototype.setPingTimeout = function () { - var self = this; - clearTimeout(self.pingTimeoutTimer); - self.pingTimeoutTimer = setTimeout(function () { - self.onClose('ping timeout'); - }, self.server.pingInterval + self.server.pingTimeout); -}; - -/** - * Attaches handlers for the given transport. - * - * @param {Transport} transport - * @api private - */ - -Socket.prototype.setTransport = function (transport) { - var onError = this.onError.bind(this); - var onPacket = this.onPacket.bind(this); - var flush = this.flush.bind(this); - var onClose = this.onClose.bind(this, 'transport close'); - - this.transport = transport; - this.transport.once('error', onError); - this.transport.on('packet', onPacket); - this.transport.on('drain', flush); - this.transport.once('close', onClose); - // this function will manage packet events (also message callbacks) - this.setupSendCallback(); - - this.cleanupFn.push(function () { - transport.removeListener('error', onError); - transport.removeListener('packet', onPacket); - transport.removeListener('drain', flush); - transport.removeListener('close', onClose); - }); -}; - -/** - * Upgrades socket to the given transport - * - * @param {Transport} transport - * @api private - */ - -Socket.prototype.maybeUpgrade = function (transport) { - debug('might upgrade socket transport from "%s" to "%s"' - , this.transport.name, transport.name); - - this.upgrading = true; - - var self = this; - - // set transport upgrade timer - self.upgradeTimeoutTimer = setTimeout(function () { - debug('client did not complete upgrade - closing transport'); - cleanup(); - if ('open' === transport.readyState) { - transport.close(); - } - }, this.server.upgradeTimeout); + this.upgrading = true; - function 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') { - debug('got upgrade packet - upgrading'); + const self = this; + + // set transport upgrade timer + self.upgradeTimeoutTimer = setTimeout(function () { + debug('client did not complete upgrade - closing transport'); cleanup(); - self.transport.discard(); - self.upgraded = true; - self.clearTransport(); - self.setTransport(transport); - self.emit('upgrade', transport); - self.setPingTimeout(); - self.flush(); - if (self.readyState === 'closing') { - transport.close(function () { - self.onClose('forced close'); - }); + if ('open' === transport.readyState) { + transport.close(); + } + }, this.server.upgradeTimeout); + + function 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') { + debug('got upgrade packet - upgrading'); + cleanup(); + self.transport.discard(); + self.upgraded = true; + self.clearTransport(); + self.setTransport(transport); + self.emit('upgrade', transport); + self.setPingTimeout(); + self.flush(); + if (self.readyState === 'closing') { + transport.close(function () { + self.onClose('forced close'); + }); + } + } else { + cleanup(); + transport.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) { - debug('writing a noop packet to polling for fast upgrade'); - self.transport.send([{ type: 'noop' }]); + // we force a polling cycle to ensure a fast upgrade + function check () { + if ('polling' === self.transport.name && self.transport.writable) { + debug('writing a noop packet to polling for fast upgrade'); + self.transport.send([{ type: 'noop' }]); + } } - } - function cleanup () { - self.upgrading = false; + function cleanup () { + self.upgrading = false; - clearInterval(self.checkIntervalTimer); - self.checkIntervalTimer = null; - - clearTimeout(self.upgradeTimeoutTimer); - self.upgradeTimeoutTimer = null; + clearInterval(self.checkIntervalTimer); + self.checkIntervalTimer = null; - transport.removeListener('packet', onPacket); - transport.removeListener('close', onTransportClose); - transport.removeListener('error', onError); - self.removeListener('close', onClose); - } + clearTimeout(self.upgradeTimeoutTimer); + self.upgradeTimeoutTimer = null; - function onError (err) { - debug('client did not complete upgrade - %s', err); - cleanup(); - transport.close(); - transport = null; - } + transport.removeListener('packet', onPacket); + transport.removeListener('close', onTransportClose); + transport.removeListener('error', onError); + self.removeListener('close', onClose); + } - function onTransportClose () { - onError('transport closed'); - } + function onError (err) { + debug('client did not complete upgrade - %s', err); + cleanup(); + transport.close(); + transport = null; + } - function onClose () { - onError('socket closed'); - } + function onTransportClose () { + onError('transport closed'); + } - transport.on('packet', onPacket); - transport.once('close', onTransportClose); - transport.once('error', onError); + function onClose () { + onError('socket closed'); + } - self.once('close', onClose); -}; + transport.on('packet', onPacket); + transport.once('close', onTransportClose); + transport.once('error', onError); -/** - * Clears listeners and timers associated with current transport. - * - * @api private - */ + self.once('close', onClose); + } -Socket.prototype.clearTransport = function () { - var cleanup; + /** + * Clears listeners and timers associated with current transport. + * + * @api private + */ + clearTransport () { + let cleanup; - var toCleanUp = this.cleanupFn.length; + const toCleanUp = this.cleanupFn.length; - for (var i = 0; i < toCleanUp; i++) { - cleanup = this.cleanupFn.shift(); - cleanup(); - } + for (let i = 0; i < toCleanUp; i++) { + cleanup = this.cleanupFn.shift(); + cleanup(); + } - // silence further transport errors and prevent uncaught exceptions - this.transport.on('error', function () { - debug('error triggered by discarded transport'); - }); + // silence further transport errors and prevent uncaught exceptions + this.transport.on('error', function () { + debug('error triggered by discarded transport'); + }); - // ensure transport won't stay open - this.transport.close(); + // ensure transport won't stay open + this.transport.close(); - clearTimeout(this.pingTimeoutTimer); -}; + clearTimeout(this.pingTimeoutTimer); + } -/** - * Called upon transport considered closed. - * Possible reasons: `ping timeout`, `client error`, `parse error`, - * `transport error`, `server close`, `transport close` - */ + /** + * Called upon transport considered closed. + * Possible reasons: `ping timeout`, `client error`, `parse error`, + * `transport error`, `server close`, `transport close` + */ + onClose (reason, description) { + if ('closed' !== this.readyState) { + this.readyState = 'closed'; + clearTimeout(this.pingTimeoutTimer); + 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 = []; + }); + this.packetsFn = []; + this.sentCallbackFn = []; + this.clearTransport(); + this.emit('close', reason, description); + } + } -Socket.prototype.onClose = function (reason, description) { - if ('closed' !== this.readyState) { - this.readyState = 'closed'; - clearTimeout(this.pingTimeoutTimer); - clearInterval(this.checkIntervalTimer); - this.checkIntervalTimer = null; - clearTimeout(this.upgradeTimeoutTimer); - var self = this; - // clean writeBuffer in next tick, so developers can still - // grab the writeBuffer on 'close' event - process.nextTick(function () { - self.writeBuffer = []; + /** + * Setup and manage send callback + * + * @api private + */ + setupSendCallback () { + const self = this; + this.transport.on('drain', onDrain); + + this.cleanupFn.push(function () { + self.transport.removeListener('drain', onDrain); }); - this.packetsFn = []; - this.sentCallbackFn = []; - this.clearTransport(); - this.emit('close', reason, description); - } -}; - -/** - * Setup and manage send callback - * - * @api private - */ - -Socket.prototype.setupSendCallback = function () { - var 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) { - var seqFn = self.sentCallbackFn.splice(0, 1)[0]; - if ('function' === typeof seqFn) { - debug('executing send callback'); - seqFn(self.transport); - } else if (Array.isArray(seqFn)) { - debug('executing batch send callback'); - for (var l = seqFn.length, i = 0; i < l; i++) { - if ('function' === typeof seqFn[i]) { - seqFn[i](self.transport); + + // the message was sent successfully, execute the callback + function onDrain () { + if (self.sentCallbackFn.length > 0) { + const seqFn = self.sentCallbackFn.splice(0, 1)[0]; + if ('function' === typeof seqFn) { + debug('executing send callback'); + seqFn(self.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); + } } } } } } -}; - -/** - * Sends a message packet. - * - * @param {String} message - * @param {Object} options - * @param {Function} callback - * @return {Socket} for chaining - * @api public - */ - -Socket.prototype.send = -Socket.prototype.write = function (data, options, callback) { - this.sendPacket('message', data, options, callback); - return this; -}; - -/** - * Sends a packet. - * - * @param {String} packet type - * @param {String} optional, data - * @param {Object} options - * @api private - */ - -Socket.prototype.sendPacket = function (type, data, options, callback) { - if ('function' === typeof options) { - callback = options; - options = null; + + /** + * Sends a message packet. + * + * @param {String} message + * @param {Object} options + * @param {Function} callback + * @return {Socket} for chaining + * @api public + */ + send (data, options, callback) { + this.sendPacket('message', data, options, callback); + return this; } - options = options || {}; - options.compress = false !== options.compress; + write (data, options, callback) { + this.sendPacket('message', data, options, callback); + return this; + } - if ('closing' !== this.readyState && 'closed' !== this.readyState) { - debug('sending packet "%s" (%s)', type, data); + /** + * Sends a packet. + * + * @param {String} packet type + * @param {String} optional, data + * @param {Object} options + * @api private + */ + sendPacket (type, data, options, callback) { + if ('function' === typeof options) { + callback = options; + options = null; + } - var packet = { - type: type, - options: options - }; - if (data) packet.data = data; + options = options || {}; + options.compress = false !== options.compress; - // exports packetCreate event - this.emit('packetCreate', packet); + if ('closing' !== this.readyState && 'closed' !== this.readyState) { + debug('sending packet "%s" (%s)', type, data); - this.writeBuffer.push(packet); + const packet = { + type: type, + options: options + }; + if (data) packet.data = data; - // add send callback to object, if defined - if (callback) this.packetsFn.push(callback); + // exports packetCreate event + this.emit('packetCreate', packet); - this.flush(); - } -}; - -/** - * Attempts to flush the packets buffer. - * - * @api private - */ - -Socket.prototype.flush = function () { - if ('closed' !== this.readyState && - this.transport.writable && - this.writeBuffer.length) { - debug('flushing buffer to transport'); - this.emit('flush', this.writeBuffer); - this.server.emit('flush', this, this.writeBuffer); - var wbuf = this.writeBuffer; - this.writeBuffer = []; - if (!this.transport.supportsFraming) { - this.sentCallbackFn.push(this.packetsFn); - } else { - this.sentCallbackFn.push.apply(this.sentCallbackFn, this.packetsFn); + this.writeBuffer.push(packet); + + // add send callback to object, if defined + if (callback) this.packetsFn.push(callback); + + this.flush(); } - this.packetsFn = []; - this.transport.send(wbuf); - this.emit('drain'); - this.server.emit('drain', this); } -}; - -/** - * Get available upgrades for this socket. - * - * @api private - */ - -Socket.prototype.getAvailableUpgrades = function () { - var availableUpgrades = []; - var allUpgrades = this.server.upgrades(this.transport.name); - for (var i = 0, l = allUpgrades.length; i < l; ++i) { - var upg = allUpgrades[i]; - if (this.server.transports.indexOf(upg) !== -1) { - availableUpgrades.push(upg); + + /** + * Attempts to flush the packets buffer. + * + * @api private + */ + flush () { + if ('closed' !== this.readyState && + this.transport.writable && + this.writeBuffer.length) { + debug('flushing buffer to transport'); + this.emit('flush', this.writeBuffer); + this.server.emit('flush', this, this.writeBuffer); + const wbuf = this.writeBuffer; + this.writeBuffer = []; + if (!this.transport.supportsFraming) { + this.sentCallbackFn.push(this.packetsFn); + } else { + this.sentCallbackFn.push.apply(this.sentCallbackFn, this.packetsFn); + } + this.packetsFn = []; + this.transport.send(wbuf); + this.emit('drain'); + this.server.emit('drain', this); } } - return availableUpgrades; -}; - -/** - * Closes the socket and underlying transport. - * - * @param {Boolean} optional, discard - * @return {Socket} for chaining - * @api public - */ -Socket.prototype.close = function (discard) { - if ('open' !== this.readyState) return; + /** + * Get available upgrades for this socket. + * + * @api private + */ + getAvailableUpgrades () { + const availableUpgrades = []; + const allUpgrades = this.server.upgrades(this.transport.name); + let i = 0; + const l = allUpgrades.length; + for (; i < l; ++i) { + const upg = allUpgrades[i]; + if (this.server.transports.indexOf(upg) !== -1) { + availableUpgrades.push(upg); + } + } + return availableUpgrades; + } - this.readyState = 'closing'; + /** + * Closes the socket and underlying transport. + * + * @param {Boolean} optional, discard + * @return {Socket} for chaining + * @api public + */ + close (discard) { + if ('open' !== this.readyState) return; + + this.readyState = 'closing'; + + if (this.writeBuffer.length) { + this.once('drain', this.closeTransport.bind(this, discard)); + return; + } - if (this.writeBuffer.length) { - this.once('drain', this.closeTransport.bind(this, discard)); - return; + this.closeTransport(discard); } - this.closeTransport(discard); -}; - -/** - * Closes the underlying transport. - * - * @param {Boolean} discard - * @api private - */ + /** + * Closes the underlying transport. + * + * @param {Boolean} discard + * @api private + */ + closeTransport (discard) { + if (discard) this.transport.discard(); + this.transport.close(this.onClose.bind(this, 'forced close')); + } +} -Socket.prototype.closeTransport = function (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.js index 933dad5bb..0beed0a04 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -1,18 +1,6 @@ - -/** - * Module dependencies. - */ - -var EventEmitter = require('events').EventEmitter; -var parser = require('engine.io-parser'); -var util = require('util'); -var debug = require('debug')('engine:transport'); - -/** - * Expose the constructor. - */ - -module.exports = Transport; +const EventEmitter = require('events'); +const parser = require('engine.io-parser'); +const debug = require('debug')('engine:transport'); /** * Noop function. @@ -22,107 +10,98 @@ module.exports = Transport; function noop () {} -/** - * Transport constructor. - * - * @param {http.IncomingMessage} request - * @api public - */ - -function Transport (req) { - this.readyState = 'open'; - this.discarded = false; -} - -/** - * Inherits from EventEmitter. - */ - -util.inherits(Transport, EventEmitter); - -/** - * Flags the transport as discarded. - * - * @api private - */ - -Transport.prototype.discard = function () { - this.discarded = true; -}; - -/** - * Called with an incoming HTTP request. - * - * @param {http.IncomingMessage} request - * @api private - */ - -Transport.prototype.onRequest = function (req) { - debug('setting request'); - this.req = req; -}; - -/** - * Closes the transport. - * - * @api private - */ +class Transport extends EventEmitter { + /** + * Transport constructor. + * + * @param {http.IncomingMessage} request + * @api public + */ + constructor (req) { + super(); + this.readyState = 'open'; + this.discarded = false; + } -Transport.prototype.close = function (fn) { - if ('closed' === this.readyState || 'closing' === this.readyState) return; + /** + * Flags the transport as discarded. + * + * @api private + */ + discard () { + this.discarded = true; + } - this.readyState = 'closing'; - this.doClose(fn || noop); -}; + /** + * Called with an incoming HTTP request. + * + * @param {http.IncomingMessage} request + * @api private + */ + onRequest (req) { + debug('setting request'); + this.req = req; + } -/** - * Called with a transport error. - * - * @param {String} message error - * @param {Object} error description - * @api private - */ + /** + * Closes the transport. + * + * @api private + */ + close (fn) { + if ('closed' === this.readyState || 'closing' === this.readyState) return; -Transport.prototype.onError = function (msg, desc) { - if (this.listeners('error').length) { - var err = new Error(msg); - err.type = 'TransportError'; - err.description = desc; - this.emit('error', err); - } else { - debug('ignored transport error %s (%s)', msg, desc); + this.readyState = 'closing'; + this.doClose(fn || noop); } -}; - -/** - * Called with parsed out a packets from the data stream. - * - * @param {Object} packet - * @api private - */ -Transport.prototype.onPacket = function (packet) { - this.emit('packet', packet); -}; + /** + * Called with a transport error. + * + * @param {String} message error + * @param {Object} error description + * @api private + */ + onError (msg, desc) { + if (this.listeners('error').length) { + const err = new Error(msg); + err.type = 'TransportError'; + err.description = desc; + this.emit('error', err); + } else { + debug('ignored transport error %s (%s)', msg, desc); + } + } -/** - * Called with the encoded packet data. - * - * @param {String} data - * @api private - */ + /** + * Called with parsed out a packets from the data stream. + * + * @param {Object} packet + * @api private + */ + onPacket (packet) { + this.emit('packet', packet); + } -Transport.prototype.onData = function (data) { - this.onPacket(parser.decodePacket(data)); -}; + /** + * Called with the encoded packet data. + * + * @param {String} data + * @api private + */ + onData (data) { + this.onPacket(parser.decodePacket(data)); + } -/** - * Called upon transport close. - * - * @api private - */ + /** + * Called upon transport close. + * + * @api private + */ + onClose () { + this.readyState = 'closed'; + this.emit('close'); + } +} -Transport.prototype.onClose = function () { - this.readyState = 'closed'; - this.emit('close'); -}; +module.exports = Transport; diff --git a/lib/transports/index.js b/lib/transports/index.js index fcff3223a..0b6a40b42 100644 --- a/lib/transports/index.js +++ b/lib/transports/index.js @@ -1,10 +1,5 @@ - -/** - * Module dependencies. - */ - -var XHR = require('./polling-xhr'); -var JSONP = require('./polling-jsonp'); +const XHR = require('./polling-xhr'); +const JSONP = require('./polling-jsonp'); /** * Export transports. diff --git a/lib/transports/polling-jsonp.js b/lib/transports/polling-jsonp.js index 62e66e779..de3de36e7 100644 --- a/lib/transports/polling-jsonp.js +++ b/lib/transports/polling-jsonp.js @@ -1,75 +1,58 @@ - -/** - * Module dependencies. - */ - -var Polling = require('./polling'); -var qs = require('querystring'); -var rDoubleSlashes = /\\\\n/g; -var rSlashes = /(\\)?\\n/g; -var util = require('util'); - -/** - * Module exports. - */ - -module.exports = JSONP; - -/** - * JSON-P polling transport. - * - * @api public - */ - -function JSONP (req) { - Polling.call(this, req); - - this.head = '___eio[' + (req._query.j || '').replace(/[^0-9]/g, '') + ']('; - this.foot = ');'; -} - -/** - * Inherits from Polling. - */ - -util.inherits(JSONP, Polling); - -/** - * Handles incoming data. - * Due to a bug in \n handling by browsers, we expect a escaped string. - * - * @api private - */ - -JSONP.prototype.onData = function (data) { - // we leverage the qs module so that we get built-in DoS protection - // and the fast alternative to decodeURIComponent - data = qs.parse(data).d; - if ('string' === typeof data) { - // client will send already escaped newlines as \\\\n and newlines as \\n - // \\n must be replaced with \n and \\\\n with \\n - data = data.replace(rSlashes, function (match, slashes) { - return slashes ? match : '\n'; - }); - Polling.prototype.onData.call(this, data.replace(rDoubleSlashes, '\\n')); +const Polling = require('./polling'); +const qs = require('querystring'); +const rDoubleSlashes = /\\\\n/g; +const rSlashes = /(\\)?\\n/g; + +class JSONP extends Polling { + /** + * JSON-P polling transport. + * + * @api public + */ + constructor (req) { + super(req); + + this.head = '___eio[' + (req._query.j || '').replace(/[^0-9]/g, '') + ']('; + this.foot = ');'; } -}; - -/** - * Performs the write. - * - * @api private - */ -JSONP.prototype.doWrite = function (data, options, callback) { - // we must output valid javascript, not valid json - // see: http://timelessrepo.com/json-isnt-a-javascript-subset - var js = JSON.stringify(data) - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029'); + /** + * Handles incoming data. + * Due to a bug in \n handling by browsers, we expect a escaped string. + * + * @api private + */ + onData (data) { + // we leverage the qs module so that we get built-in DoS protection + // and the fast alternative to decodeURIComponent + data = qs.parse(data).d; + if ('string' === typeof data) { + // client will send already escaped newlines as \\\\n and newlines as \\n + // \\n must be replaced with \n and \\\\n with \\n + data = data.replace(rSlashes, function (match, slashes) { + return slashes ? match : '\n'; + }); + super.onData(data.replace(rDoubleSlashes, '\\n')); + } + } - // prepare response - data = this.head + js + this.foot; + /** + * Performs the write. + * + * @api private + */ + doWrite (data, options, callback) { + // we must output valid javascript, not valid json + // see: http://timelessrepo.com/json-isnt-a-javascript-subset + const js = JSON.stringify(data) + .replace(/\u2028/g, '\\u2028') + .replace(/\u2029/g, '\\u2029'); + + // prepare response + data = this.head + js + this.foot; + + super.doWrite(data, options, callback); + } +} - Polling.prototype.doWrite.call(this, data, options, callback); -}; +module.exports = JSONP; diff --git a/lib/transports/polling-xhr.js b/lib/transports/polling-xhr.js index 3562524e1..977e3c16d 100644 --- a/lib/transports/polling-xhr.js +++ b/lib/transports/polling-xhr.js @@ -1,69 +1,43 @@ - -/** - * Module dependencies. - */ - -var Polling = require('./polling'); -var util = require('util'); - -/** - * Module exports. - */ - -module.exports = XHR; - -/** - * Ajax polling transport. - * - * @api public - */ - -function XHR (req) { - Polling.call(this, req); -} - -/** - * Inherits from Polling. - */ - -util.inherits(XHR, Polling); - -/** - * Overrides `onRequest` to handle `OPTIONS`.. - * - * @param {http.IncomingMessage} - * @api private - */ - -XHR.prototype.onRequest = function (req) { - if ('OPTIONS' === req.method) { - var res = req.res; - var headers = this.headers(req); - headers['Access-Control-Allow-Headers'] = 'Content-Type'; - res.writeHead(200, headers); - res.end(); - } else { - Polling.prototype.onRequest.call(this, req); +const Polling = require('./polling'); + +class XHR extends Polling { + /** + * Overrides `onRequest` to handle `OPTIONS`.. + * + * @param {http.IncomingMessage} + * @api private + */ + onRequest (req) { + if ('OPTIONS' === req.method) { + const res = req.res; + const headers = this.headers(req); + headers['Access-Control-Allow-Headers'] = 'Content-Type'; + res.writeHead(200, headers); + res.end(); + } else { + super.onRequest(req); + } } -}; -/** - * Returns headers for a response. - * - * @param {http.IncomingMessage} request - * @param {Object} extra headers - * @api private - */ - -XHR.prototype.headers = function (req, headers) { - headers = headers || {}; - - if (req.headers.origin) { - headers['Access-Control-Allow-Credentials'] = 'true'; - headers['Access-Control-Allow-Origin'] = req.headers.origin; - } else { - headers['Access-Control-Allow-Origin'] = '*'; + /** + * Returns headers for a response. + * + * @param {http.IncomingMessage} request + * @param {Object} extra headers + * @api private + */ + headers (req, headers) { + headers = headers || {}; + + if (req.headers.origin) { + headers['Access-Control-Allow-Credentials'] = 'true'; + headers['Access-Control-Allow-Origin'] = req.headers.origin; + } else { + headers['Access-Control-Allow-Origin'] = '*'; + } + + return super.headers(req, headers); } +} - return Polling.prototype.headers.call(this, req, headers); -}; +module.exports = XHR; diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 7c29c29ea..063360aaf 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -1,407 +1,380 @@ +const Transport = require('../transport'); +const parser = require('engine.io-parser'); +const zlib = require('zlib'); +const accepts = require('accepts'); +const debug = require('debug')('engine:polling'); -/** - * Module requirements. - */ - -var Transport = require('../transport'); -var parser = require('engine.io-parser'); -var zlib = require('zlib'); -var accepts = require('accepts'); -var util = require('util'); -var debug = require('debug')('engine:polling'); - -var compressionMethods = { +const compressionMethods = { gzip: zlib.createGzip, deflate: zlib.createDeflate }; -/** - * Exports the constructor. - */ - -module.exports = Polling; - -/** - * HTTP polling constructor. - * - * @api public. - */ - -function Polling (req) { - Transport.call(this, req); - - this.closeTimeout = 30 * 1000; - this.maxHttpBufferSize = null; - this.httpCompression = null; -} +class Polling extends Transport { + /** + * HTTP polling constructor. + * + * @api public. + */ + constructor (req) { + super(req); + + this.closeTimeout = 30 * 1000; + this.maxHttpBufferSize = null; + this.httpCompression = null; + } -/** - * Inherits from Transport. - * - * @api public. - */ - -util.inherits(Polling, Transport); - -/** - * Transport name - * - * @api public - */ - -Polling.prototype.name = 'polling'; - -/** - * Overrides onRequest. - * - * @param {http.IncomingMessage} - * @api private - */ - -Polling.prototype.onRequest = function (req) { - var res = req.res; - - if ('GET' === req.method) { - this.onPollRequest(req, res); - } else if ('POST' === req.method) { - this.onDataRequest(req, res); - } else { - res.writeHead(500); - res.end(); + /** + * Transport name + * + * @api public + */ + get name () { + return 'polling'; } -}; -/** - * The client sends a request awaiting for us to send data. - * - * @api private - */ - -Polling.prototype.onPollRequest = function (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.writeHead(500); - res.end(); - return; + /** + * Overrides onRequest. + * + * @param {http.IncomingMessage} + * @api private + */ + onRequest (req) { + const res = req.res; + + if ('GET' === req.method) { + this.onPollRequest(req, res); + } else if ('POST' === req.method) { + this.onDataRequest(req, res); + } else { + res.writeHead(500); + res.end(); + } } - debug('setting request'); + /** + * 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.writeHead(500); + res.end(); + return; + } - this.req = req; - this.res = res; + debug('setting request'); - var self = this; + this.req = req; + this.res = res; - function onClose () { - self.onError('poll connection closed prematurely'); - } + const self = this; - function cleanup () { - req.removeListener('close', onClose); - self.req = self.res = null; - } + function onClose () { + self.onError('poll connection closed prematurely'); + } - req.cleanup = cleanup; - req.on('close', onClose); + function cleanup () { + req.removeListener('close', onClose); + self.req = self.res = null; + } - this.writable = true; - this.emit('drain'); + req.cleanup = cleanup; + req.on('close', onClose); - // 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' }]); - } -}; + this.writable = true; + this.emit('drain'); -/** - * The client sends a request with data. - * - * @api private - */ - -Polling.prototype.onDataRequest = function (req, res) { - if (this.dataReq) { - // assert: this.dataRes, '.dataReq and .dataRes should be (un)set together' - this.onError('data request overlap from client'); - res.writeHead(500); - res.end(); - return; + // 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' }]); + } } - var isBinary = 'application/octet-stream' === req.headers['content-type']; - - this.dataReq = req; - this.dataRes = res; + /** + * 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.writeHead(500); + res.end(); + return; + } - var chunks = isBinary ? Buffer.concat([]) : ''; - var self = this; + const isBinary = 'application/octet-stream' === req.headers['content-type']; - function cleanup () { - req.removeListener('data', onData); - req.removeListener('end', onEnd); - req.removeListener('close', onClose); - self.dataReq = self.dataRes = chunks = null; - } + this.dataReq = req; + this.dataRes = res; - function onClose () { - cleanup(); - self.onError('data request connection closed prematurely'); - } + let chunks = isBinary ? Buffer.concat([]) : ''; + const self = this; - function onData (data) { - var contentLength; - if (isBinary) { - chunks = Buffer.concat([chunks, data]); - contentLength = chunks.length; - } else { - chunks += data; - contentLength = Buffer.byteLength(chunks); + function cleanup () { + req.removeListener('data', onData); + req.removeListener('end', onEnd); + req.removeListener('close', onClose); + self.dataReq = self.dataRes = chunks = null; } - if (contentLength > self.maxHttpBufferSize) { - chunks = isBinary ? Buffer.concat([]) : ''; - req.connection.destroy(); + function onClose () { + cleanup(); + self.onError('data request connection closed prematurely'); } - } - function onEnd () { - self.onData(chunks); - - var 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', - 'Content-Length': 2 - }; + function onData (data) { + let contentLength; + if (isBinary) { + chunks = Buffer.concat([chunks, data]); + contentLength = chunks.length; + } else { + chunks += data; + contentLength = Buffer.byteLength(chunks); + } + + if (contentLength > self.maxHttpBufferSize) { + chunks = isBinary ? Buffer.concat([]) : ''; + req.connection.destroy(); + } + } - res.writeHead(200, self.headers(req, headers)); - res.end('ok'); - cleanup(); - } + function onEnd () { + self.onData(chunks); - req.on('close', onClose); - if (!isBinary) req.setEncoding('utf8'); - req.on('data', onData); - req.on('end', onEnd); -}; + 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', + 'Content-Length': 2 + }; -/** - * Processes the incoming data payload. - * - * @param {String} encoded payload - * @api private - */ - -Polling.prototype.onData = function (data) { - debug('received "%s"', data); - var self = this; - var callback = function (packet) { - if ('close' === packet.type) { - debug('got xhr close packet'); - self.onClose(); - return false; + res.writeHead(200, self.headers(req, headers)); + res.end('ok'); + cleanup(); } - self.onPacket(packet); - }; - - parser.decodePayload(data, callback); -}; + req.on('close', onClose); + if (!isBinary) req.setEncoding('utf8'); + req.on('data', onData); + req.on('end', onEnd); + } -/** - * Overrides onClose. - * - * @api private - */ + /** + * Processes the incoming data payload. + * + * @param {String} encoded payload + * @api private + */ + onData (data) { + debug('received "%s"', data); + const self = this; + const callback = function (packet) { + if ('close' === packet.type) { + debug('got xhr close packet'); + self.onClose(); + return false; + } + + self.onPacket(packet); + }; -Polling.prototype.onClose = function () { - if (this.writable) { - // close pending poll request - this.send([{ type: 'noop' }]); + parser.decodePayload(data, callback); } - Transport.prototype.onClose.call(this); -}; -/** - * Writes a packet payload. - * - * @param {Object} packet - * @api private - */ - -Polling.prototype.send = function (packets) { - this.writable = false; - - if (this.shouldClose) { - debug('appending close packet to payload'); - packets.push({ type: 'close' }); - this.shouldClose(); - this.shouldClose = null; + /** + * Overrides onClose. + * + * @api private + */ + onClose () { + if (this.writable) { + // close pending poll request + this.send([{ type: 'noop' }]); + } + super.onClose(); } - var self = this; - parser.encodePayload(packets, this.supportsBinary, function (data) { - var compress = packets.some(function (packet) { - return packet.options && packet.options.compress; - }); - self.write(data, { compress: compress }); - }); -}; - -/** - * Writes data as response to poll request. - * - * @param {String} data - * @param {Object} options - * @api private - */ - -Polling.prototype.write = function (data, options) { - debug('writing "%s"', data); - var self = this; - this.doWrite(data, options, function () { - self.req.cleanup(); - }); -}; - -/** - * Performs the write. - * - * @api private - */ - -Polling.prototype.doWrite = function (data, options, callback) { - var self = this; - - // explicit UTF-8 is required for pages not served under utf - var isString = typeof data === 'string'; - var contentType = isString - ? 'text/plain; charset=UTF-8' - : 'application/octet-stream'; - - var headers = { - 'Content-Type': contentType - }; + /** + * 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; + } - if (!this.httpCompression || !options.compress) { - respond(data); - return; + const self = this; + parser.encodePayload(packets, this.supportsBinary, function (data) { + const compress = packets.some(function (packet) { + return packet.options && packet.options.compress; + }); + self.write(data, { compress: compress }); + }); } - var len = isString ? Buffer.byteLength(data) : data.length; - if (len < this.httpCompression.threshold) { - respond(data); - return; + /** + * Writes data as response to poll request. + * + * @param {String} data + * @param {Object} options + * @api private + */ + write (data, options) { + debug('writing "%s"', data); + const self = this; + this.doWrite(data, options, function () { + self.req.cleanup(); + }); } - var encoding = accepts(this.req).encodings(['gzip', 'deflate']); - if (!encoding) { - respond(data); - return; - } + /** + * Performs the write. + * + * @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 + ? 'text/plain; charset=UTF-8' + : 'application/octet-stream'; + + const headers = { + 'Content-Type': contentType + }; + + if (!this.httpCompression || !options.compress) { + respond(data); + return; + } + + const len = isString ? Buffer.byteLength(data) : data.length; + if (len < this.httpCompression.threshold) { + respond(data); + return; + } - this.compress(data, encoding, function (err, data) { - if (err) { - self.res.writeHead(500); - self.res.end(); - callback(err); + const encoding = accepts(this.req).encodings(['gzip', 'deflate']); + if (!encoding) { + respond(data); return; } - headers['Content-Encoding'] = encoding; - respond(data); - }); + this.compress(data, encoding, function (err, data) { + if (err) { + self.res.writeHead(500); + self.res.end(); + callback(err); + return; + } + + 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(); + 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(); + } } -}; -/** - * Compresses data. - * - * @api private - */ - -Polling.prototype.compress = function (data, encoding, callback) { - debug('compressing'); - - var buffers = []; - var 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); -}; + /** + * 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 - */ + /** + * Closes the transport. + * + * @api private + */ + doClose (fn) { + debug('closing'); -Polling.prototype.doClose = function (fn) { - debug('closing'); + const self = this; + let closeTimeoutTimer; - var self = this; - var closeTimeoutTimer; + if (this.dataReq) { + debug('aborting ongoing data request'); + this.dataReq.destroy(); + } - if (this.dataReq) { - debug('aborting ongoing data request'); - this.dataReq.destroy(); - } + 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); + } - 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); + function onClose () { + clearTimeout(closeTimeoutTimer); + fn(); + self.onClose(); + } } - function onClose () { - clearTimeout(closeTimeoutTimer); - fn(); - self.onClose(); - } -}; + /** + * Returns headers for a response. + * + * @param {http.IncomingMessage} 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'; + } -/** - * Returns headers for a response. - * - * @param {http.IncomingMessage} request - * @param {Object} extra headers - * @api private - */ - -Polling.prototype.headers = function (req, headers) { - headers = headers || {}; - - // prevent XSS warnings on IE - // https://github.com/LearnBoost/socket.io/pull/1333 - var ua = req.headers['user-agent']; - if (ua && (~ua.indexOf(';MSIE') || ~ua.indexOf('Trident/'))) { - headers['X-XSS-Protection'] = '0'; + this.emit('headers', headers); + return headers; } +} - this.emit('headers', headers); - return headers; -}; +module.exports = Polling; diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 7d5511b1d..ee228e9ee 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -1,134 +1,116 @@ - -/** - * Module dependencies. - */ - -var Transport = require('../transport'); -var parser = require('engine.io-parser'); -var util = require('util'); -var debug = require('debug')('engine:ws'); - -/** - * Export the constructor. - */ - -module.exports = WebSocket; - -/** - * WebSocket transport - * - * @param {http.IncomingMessage} - * @api public - */ - -function WebSocket (req) { - Transport.call(this, req); - var self = this; - this.socket = req.websocket; - 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', onHeaders); - this.writable = true; - this.perMessageDeflate = null; - - function onHeaders (headers) { - self.emit('headers', headers); +const Transport = require('../transport'); +const parser = require('engine.io-parser'); +const debug = require('debug')('engine:ws'); + +class WebSocket extends Transport { + /** + * WebSocket transport + * + * @param {http.IncomingMessage} + * @api public + */ + constructor (req) { + super(req); + this.socket = req.websocket; + 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; } -} - -/** - * Inherits from Transport. - */ - -util.inherits(WebSocket, Transport); - -/** - * Transport name - * - * @api public - */ - -WebSocket.prototype.name = 'websocket'; - -/** - * Advertise upgrade support. - * - * @api public - */ -WebSocket.prototype.handlesUpgrades = true; - -/** - * Advertise framing support. - * - * @api public - */ + /** + * Transport name + * + * @api public + */ + get name () { + return 'websocket'; + } -WebSocket.prototype.supportsFraming = true; + /** + * Advertise upgrade support. + * + * @api public + */ + get handlesUpgrades () { + return true; + } -/** - * Processes the incoming data. - * - * @param {String} encoded packet - * @api private - */ + /** + * Advertise framing support. + * + * @api public + */ + get supportsFraming () { + return true; + } -WebSocket.prototype.onData = function (data) { - debug('received "%s"', data); - Transport.prototype.onData.call(this, data); -}; + /** + * Processes the incoming data. + * + * @param {String} encoded packet + * @api private + */ + onData (data) { + debug('received "%s"', data); + super.onData(data); + } -/** - * Writes a packet payload. - * - * @param {Array} packets - * @api private - */ + /** + * Writes a packet payload. + * + * @param {Array} packets + * @api private + */ + send (packets) { + var self = this; + + for (var i = 0; i < packets.length; i++) { + var packet = packets[i]; + parser.encodePacket(packet, self.supportsBinary, send); + } -WebSocket.prototype.send = function (packets) { - var self = this; + function send (data) { + debug('writing "%s"', data); - for (var i = 0; i < packets.length; i++) { - var packet = packets[i]; - parser.encodePacket(packet, self.supportsBinary, send); - } + // always creates a new object since ws modifies it + var opts = {}; + if (packet.options) { + opts.compress = packet.options.compress; + } - function send (data) { - debug('writing "%s"', data); + if (self.perMessageDeflate) { + var len = 'string' === typeof data ? Buffer.byteLength(data) : data.length; + if (len < self.perMessageDeflate.threshold) { + opts.compress = false; + } + } - // always creates a new object since ws modifies it - var opts = {}; - if (packet.options) { - opts.compress = packet.options.compress; + self.writable = false; + self.socket.send(data, opts, onEnd); } - if (self.perMessageDeflate) { - var len = 'string' === typeof data ? Buffer.byteLength(data) : data.length; - if (len < self.perMessageDeflate.threshold) { - opts.compress = false; - } + function onEnd (err) { + if (err) return self.onError('write error', err.stack); + self.writable = true; + self.emit('drain'); } - - self.writable = false; - self.socket.send(data, opts, onEnd); } - function onEnd (err) { - if (err) return self.onError('write error', err.stack); - self.writable = true; - self.emit('drain'); + /** + * Closes the transport. + * + * @api private + */ + doClose (fn) { + debug('closing'); + this.socket.close(); + fn && fn(); } -}; - -/** - * Closes the transport. - * - * @api private - */ +} -WebSocket.prototype.doClose = function (fn) { - debug('closing'); - this.socket.close(); - fn && fn(); -}; +module.exports = WebSocket; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..542c3b12f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2780 @@ +{ + "name": "engine.io", + "version": "3.4.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@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, + "requires": { + "@babel/highlight": "7.0.0-beta.44" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@babel/helper-get-function-arity": "7.0.0-beta.44", + "@babel/template": "7.0.0-beta.44", + "@babel/types": "7.0.0-beta.44" + } + }, + "@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, + "requires": { + "@babel/types": "7.0.0-beta.44" + } + }, + "@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, + "requires": { + "@babel/types": "7.0.0-beta.44" + } + }, + "@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, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@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" + }, + "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" + } + } + } + }, + "@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, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.2.0", + "to-fast-properties": "^2.0.0" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "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=" + }, + "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", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "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" + } + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "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==" + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "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" + } + }, + "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", + "integrity": "sha512-aCdHjhzcILdP8c9lej7hvXKvQieyRt20SF102SIGyY4cUIiw6UaAtK4j2o3dXX74jEmy0TJ0CEhv4fTIM3SzcA==", + "dev": true, + "requires": { + "@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" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + } + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + } + } + }, + "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 + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, + "base64id": { + "version": "2.0.0", + "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=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "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" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "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", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "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.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "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", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "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, + "requires": { + "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.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "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 + }, + "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 + }, + "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" + } + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", + "dev": true + }, + "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 + }, + "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.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "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 + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "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 + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "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" + } + }, + "engine.io-client": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz", + "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~6.1.0", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", + "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz", + "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "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, + "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" + }, + "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 + }, + "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" + } + }, + "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" + } + } + } + }, + "eslint-config-standard": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", + "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", + "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.1.tgz", + "integrity": "sha512-GcNwsYv8MfoEBSbAmV+PSVn2RlhpCShbLImtNviAYa/LE0PgNqxH5tLi1Ld9yeFwdjHsarXK+7G9vsyddmB6dw==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.0.tgz", + "integrity": "sha512-NK42oA0mUc8Ngn4kONOPsPB1XhbUvNHqF+g307dPV28aknPoiNnKLFd9em4nkswwepdF5ouieqv5Th/63U7YJQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.1", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-node": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.2.1.tgz", + "integrity": "sha512-xhPXrh0Vl/b7870uEbaumb2Q+LxaEcOQ3kS1jtIXanBAwpMre1l5q/l2l/hESYJGEFKuI78bp6Uw50hlpr7B+g==", + "dev": true, + "requires": { + "ignore": "^3.3.6", + "minimatch": "^3.0.4", + "resolve": "^1.3.3", + "semver": "5.3.0" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz", + "integrity": "sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==", + "dev": true + }, + "eslint-plugin-standard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", + "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", + "dev": true + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "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.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "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 + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "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" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "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", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "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 + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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" + } + }, + "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 + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "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": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "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", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + } + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "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", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "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" + }, + "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-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" + } + } + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "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-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "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 + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "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.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "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" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "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, + "requires": { + "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", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" + }, + "mime-types": { + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "requires": { + "mime-db": "1.42.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", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "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" + } + }, + "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" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "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": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "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==", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, + "ms": { + "version": "2.1.2", + "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", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "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 + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "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 + }, + "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 + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "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 + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "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 + }, + "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.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz", + "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regexpp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "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": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", + "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "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==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "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", + "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", + "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.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "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 + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "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" + }, + "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-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" + } + } + } + }, + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.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": "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" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "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", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "dev": true, + "requires": { + "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" + }, + "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" + } + } + } + }, + "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, + "requires": { + "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", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "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 + }, + "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=", + "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 + }, + "uws": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/uws/-/uws-9.14.0.tgz", + "integrity": "sha512-HNMztPP5A1sKuVFmdZ6BPVpBQd5bUjNC8EFMFiICK+oho/OQsAJy5hnIx4btMHiOk8j04f/DbIlqnEZ9d72dqg==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz", + "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==", + "requires": { + "async-limiter": "^1.0.0" + } + }, + "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 + }, + "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", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + } + } +} From 33564b2391e0d4393b636bbb956ba9a8ae5a38e0 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sun, 12 Jan 2020 22:47:07 +0100 Subject: [PATCH 017/124] refactor: use prettier to format code --- .eslintrc.json | 10 +- lib/engine.io.js | 24 +- lib/server.js | 244 +- lib/socket.js | 246 +- lib/transport.js | 46 +- lib/transports/index.js | 12 +- lib/transports/polling-jsonp.js | 26 +- lib/transports/polling-xhr.js | 16 +- lib/transports/polling.js | 169 +- lib/transports/websocket.js | 45 +- package-lock.json | 1105 +-------- package.json | 14 +- test/common.js | 11 +- test/engine.io.js | 213 +- test/fixtures/server-close-upgraded.js | 10 +- test/fixtures/server-close-upgrading.js | 10 +- test/fixtures/server-close.js | 10 +- test/jsonp.js | 266 ++- test/server.js | 2873 +++++++++++++---------- 19 files changed, 2305 insertions(+), 3045 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 6d3c020b6..082c0cb59 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,10 +1,4 @@ { - "extends": "standard", - "parser": "babel-eslint", - "rules": { - "yoda": 0, - "semi": [2, "always"], - "no-extra-semi": 2, - "semi-spacing": [2, { "before": false, "after": true }] - } + "extends": "prettier", + "parser": "babel-eslint" } diff --git a/lib/engine.io.js b/lib/engine.io.js index 6fe013880..2c1ce9d10 100644 --- a/lib/engine.io.js +++ b/lib/engine.io.js @@ -2,8 +2,8 @@ * Module dependencies. */ -const http = require('http'); -const Server = require('./server'); +const http = require("http"); +const Server = require("./server"); /** * Invoking the library as a function delegates to attach if the first argument @@ -18,7 +18,7 @@ const Server = require('./server'); * @api public */ -exports = module.exports = function () { +exports = module.exports = function() { // backwards compatible use as `.attach` // if first argument is an http server if (arguments.length && arguments[0] instanceof http.Server) { @@ -51,7 +51,7 @@ exports.Server = Server; * @api public */ -exports.Socket = require('./socket'); +exports.Socket = require("./socket"); /** * Expose Transport constructor. @@ -59,7 +59,7 @@ exports.Socket = require('./socket'); * @api public */ -exports.Transport = require('./transport'); +exports.Transport = require("./transport"); /** * Expose mutable list of available transports. @@ -67,7 +67,7 @@ exports.Transport = require('./transport'); * @api public */ -exports.transports = require('./transports'); +exports.transports = require("./transports"); /** * Exports parser. @@ -75,7 +75,7 @@ exports.transports = require('./transports'); * @api public */ -exports.parser = require('engine.io-parser'); +exports.parser = require("engine.io-parser"); /** * Creates an http.Server exclusively used for WS upgrades. @@ -89,15 +89,15 @@ exports.parser = require('engine.io-parser'); exports.listen = listen; -function listen (port, options, fn) { - if ('function' === typeof options) { +function listen(port, options, fn) { + if ("function" === typeof options) { fn = options; options = {}; } - const server = http.createServer(function (req, res) { + const server = http.createServer(function(req, res) { res.writeHead(501); - res.end('Not Implemented'); + res.end("Not Implemented"); }); // create engine server @@ -120,7 +120,7 @@ function listen (port, options, fn) { exports.attach = attach; -function attach (server, options) { +function attach(server, options) { const engine = new Server(options); engine.attach(server, options); return engine; diff --git a/lib/server.js b/lib/server.js index 2922c6323..89eaf606d 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,11 +1,11 @@ -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 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"); class Server extends EventEmitter { /** @@ -14,7 +14,7 @@ class Server extends EventEmitter { * @param {Object} options * @api public */ - constructor (opts) { + constructor(opts) { super(); this.clients = {}; @@ -22,23 +22,26 @@ class Server extends EventEmitter { opts = opts || {}; - this.wsEngine = opts.wsEngine || process.env.EIO_WS_ENGINE || 'ws'; + this.wsEngine = opts.wsEngine || process.env.EIO_WS_ENGINE || "ws"; this.pingTimeout = opts.pingTimeout || 5000; this.pingInterval = opts.pingInterval || 25000; this.upgradeTimeout = opts.upgradeTimeout || 10000; - this.maxHttpBufferSize = opts.maxHttpBufferSize || 10E7; + this.maxHttpBufferSize = opts.maxHttpBufferSize || 10e7; this.transports = opts.transports || Object.keys(transports); this.allowUpgrades = false !== opts.allowUpgrades; this.allowRequest = opts.allowRequest; - this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false; - this.cookiePath = false !== opts.cookiePath ? (opts.cookiePath || '/') : false; + this.cookie = false !== opts.cookie ? opts.cookie || "io" : false; + this.cookiePath = + false !== opts.cookiePath ? opts.cookiePath || "/" : false; this.cookieHttpOnly = false !== opts.cookieHttpOnly; - this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || true) : false; - this.httpCompression = false !== opts.httpCompression ? (opts.httpCompression || {}) : false; + this.perMessageDeflate = + false !== opts.perMessageDeflate ? opts.perMessageDeflate || true : false; + this.httpCompression = + false !== opts.httpCompression ? opts.httpCompression || {} : false; this.initialPacket = opts.initialPacket; // initialize compression options - ['perMessageDeflate', 'httpCompression'].forEach((type) => { + ["perMessageDeflate", "httpCompression"].forEach(type => { let compression = this[type]; if (true === compression) this[type] = compression = {}; if (compression && null == compression.threshold) { @@ -54,16 +57,21 @@ class Server extends EventEmitter { * * @api private */ - init () { - if (!~this.transports.indexOf('websocket')) return; + init() { + if (!~this.transports.indexOf("websocket")) return; if (this.ws) this.ws.close(); let wsModule; switch (this.wsEngine) { - case 'uws': wsModule = require('uws'); break; - case 'ws': wsModule = require('ws'); break; - default: throw new Error('unknown wsEngine'); + case "uws": + wsModule = require("uws"); + break; + case "ws": + wsModule = require("ws"); + break; + default: + throw new Error("unknown wsEngine"); } this.ws = new wsModule.Server({ noServer: true, @@ -79,7 +87,7 @@ class Server extends EventEmitter { * @return {Array} * @api public */ - upgrades (transport) { + upgrades(transport) { if (!this.allowUpgrades) return []; return transports[transport].upgradesTo || []; } @@ -91,7 +99,7 @@ class Server extends EventEmitter { * @return {Boolean} whether the request is valid * @api private */ - verify (req, upgrade, fn) { + verify(req, upgrade, fn) { // transport check const transport = req._query.transport; if (!~this.transports.indexOf(transport)) { @@ -103,7 +111,7 @@ class Server extends EventEmitter { const isOriginInvalid = checkInvalidHeaderChar(req.headers.origin); if (isOriginInvalid) { req.headers.origin = null; - debug('origin header invalid'); + debug("origin header invalid"); return fn(Server.errors.BAD_REQUEST, false); } @@ -115,12 +123,13 @@ class Server extends EventEmitter { return fn(Server.errors.UNKNOWN_SID, false); } if (!upgrade && this.clients[sid].transport.name !== transport) { - debug('bad request: unexpected transport without upgrade'); + debug("bad request: unexpected transport without upgrade"); return fn(Server.errors.BAD_REQUEST, false); } } else { // handshake is GET only - if ('GET' !== req.method) return fn(Server.errors.BAD_HANDSHAKE_METHOD, false); + if ("GET" !== req.method) + return fn(Server.errors.BAD_HANDSHAKE_METHOD, false); if (!this.allowRequest) return fn(null, true); return this.allowRequest(req, fn); } @@ -133,10 +142,10 @@ class Server extends EventEmitter { * * @api private */ - prepare (req) { + 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) : {}; + req._query = ~req.url.indexOf("?") ? qs.parse(parse(req.url).query) : {}; } } @@ -145,15 +154,15 @@ class Server extends EventEmitter { * * @api public */ - close () { - debug('closing all open clients'); + close() { + debug("closing all open clients"); for (let i in this.clients) { if (this.clients.hasOwnProperty(i)) { this.clients[i].close(true); } } if (this.ws) { - debug('closing webSocketServer'); + debug("closing webSocketServer"); this.ws.close(); // don't delete this.ws because it can be used again if the http server starts listening again } @@ -167,20 +176,20 @@ class Server extends EventEmitter { * @param {http.ServerResponse|http.OutgoingMessage} response * @api public */ - handleRequest (req, res) { + handleRequest(req, res) { debug('handling "%s" http request "%s"', req.method, req.url); this.prepare(req); req.res = res; const self = this; - this.verify(req, false, function (err, success) { + this.verify(req, false, function(err, success) { if (!success) { sendErrorMessage(req, res, err); return; } if (req._query.sid) { - debug('setting new request for existing client'); + debug("setting new request for existing client"); self.clients[req._query.sid].transport.onRequest(req); } else { self.handshake(req._query.transport, req); @@ -195,7 +204,7 @@ class Server extends EventEmitter { * @param {Object} request object * @api public */ - generateId (req) { + generateId(req) { return base64id.generateId(); } @@ -206,17 +215,17 @@ class Server extends EventEmitter { * @param {Object} request object * @api private */ - handshake (transportName, req) { + handshake(transportName, req) { const id = this.generateId(req); debug('handshaking client "%s"', id); try { var transport = new transports[transportName](req); - if ('polling' === transportName) { + if ("polling" === transportName) { transport.maxHttpBufferSize = this.maxHttpBufferSize; transport.httpCompression = this.httpCompression; - } else if ('websocket' === transportName) { + } else if ("websocket" === transportName) { transport.perMessageDeflate = this.perMessageDeflate; } @@ -234,12 +243,11 @@ class Server extends EventEmitter { const self = this; if (false !== this.cookie) { - transport.on('headers', function (headers) { - headers['Set-Cookie'] = cookieMod.serialize(self.cookie, id, - { - path: self.cookiePath, - httpOnly: self.cookiePath ? self.cookieHttpOnly : false - }); + transport.on("headers", function(headers) { + headers["Set-Cookie"] = cookieMod.serialize(self.cookie, id, { + path: self.cookiePath, + httpOnly: self.cookiePath ? self.cookieHttpOnly : false + }); }); } @@ -248,12 +256,12 @@ class Server extends EventEmitter { this.clients[id] = socket; this.clientsCount++; - socket.once('close', function () { + socket.once("close", function() { delete self.clients[id]; self.clientsCount--; }); - this.emit('connection', socket); + this.emit("connection", socket); } /** @@ -261,11 +269,11 @@ class Server extends EventEmitter { * * @api public */ - handleUpgrade (req, socket, upgradeHead) { + handleUpgrade(req, socket, upgradeHead) { this.prepare(req); const self = this; - this.verify(req, true, function (err, success) { + this.verify(req, true, function(err, success) { if (!success) { abortConnection(socket, err); return; @@ -275,7 +283,7 @@ class Server extends EventEmitter { upgradeHead = null; // delegate to ws - self.ws.handleUpgrade(req, socket, head, function (conn) { + self.ws.handleUpgrade(req, socket, head, function(conn) { self.onWebSocket(req, conn); }); }); @@ -287,11 +295,14 @@ class Server extends EventEmitter { * @param {ws.Socket} websocket * @api private */ - onWebSocket (req, socket) { - socket.on('error', onUpgradeError); - - if (transports[req._query.transport] !== undefined && !transports[req._query.transport].prototype.handlesUpgrades) { - debug('transport doesnt handle upgraded requests'); + onWebSocket(req, socket) { + socket.on("error", onUpgradeError); + + if ( + transports[req._query.transport] !== undefined && + !transports[req._query.transport].prototype.handlesUpgrades + ) { + debug("transport doesnt handle upgraded requests"); socket.close(); return; } @@ -305,19 +316,19 @@ class Server extends EventEmitter { if (id) { const client = this.clients[id]; if (!client) { - debug('upgrade attempt for closed client'); + debug("upgrade attempt for closed client"); socket.close(); } else if (client.upgrading) { - debug('transport has already been trying to upgrade'); + debug("transport has already been trying to upgrade"); socket.close(); } else if (client.upgraded) { - debug('transport had already been upgraded'); + debug("transport had already been upgraded"); socket.close(); } else { - debug('upgrading existing transport'); + debug("upgrading existing transport"); // transport error handling takes over - socket.removeListener('error', onUpgradeError); + socket.removeListener("error", onUpgradeError); const transport = new transports[req._query.transport](req); if (req._query && req._query.b64) { @@ -330,13 +341,13 @@ class Server extends EventEmitter { } } else { // transport error handling takes over - socket.removeListener('error', onUpgradeError); + socket.removeListener("error", onUpgradeError); this.handshake(req._query.transport, req); } - function onUpgradeError () { - debug('websocket error before upgrade'); + function onUpgradeError() { + debug("websocket error before upgrade"); // socket.close() not needed } } @@ -348,34 +359,40 @@ class Server extends EventEmitter { * @param {Object} options * @api public */ - attach (server, options) { + attach(server, options) { const self = this; options = options || {}; - let path = (options.path || '/engine.io').replace(/\/$/, ''); + let path = (options.path || "/engine.io").replace(/\/$/, ""); const destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000; // normalize path - path += '/'; + path += "/"; - function check (req) { - if ('OPTIONS' === req.method && false === options.handlePreflightRequest) { + function check(req) { + if ( + "OPTIONS" === req.method && + false === options.handlePreflightRequest + ) { return false; } return path === req.url.substr(0, path.length); } // 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)); + const listeners = server.listeners("request").slice(0); + server.removeAllListeners("request"); + server.on("close", self.close.bind(self)); + server.on("listening", self.init.bind(self)); // add request handler - server.on('request', function (req, res) { + server.on("request", function(req, res) { if (check(req)) { debug('intercepting request for path "%s"', path); - if ('OPTIONS' === req.method && 'function' === typeof options.handlePreflightRequest) { + if ( + "OPTIONS" === req.method && + "function" === typeof options.handlePreflightRequest + ) { options.handlePreflightRequest.call(server, req, res); } else { self.handleRequest(req, res); @@ -389,8 +406,8 @@ class Server extends EventEmitter { } }); - if (~self.transports.indexOf('websocket')) { - server.on('upgrade', function (req, socket, head) { + if (~self.transports.indexOf("websocket")) { + server.on("upgrade", function(req, socket, head) { if (check(req)) { self.handleUpgrade(req, socket, head); } else if (false !== options.destroyUpgrade) { @@ -398,7 +415,7 @@ class Server extends EventEmitter { // but by adding a handler, we prevent that // and if no eio thing handles the upgrade // then the socket needs to die! - setTimeout(function () { + setTimeout(function() { if (socket.writable && socket.bytesWritten <= 0) { return socket.end(); } @@ -422,11 +439,11 @@ Server.errors = { }; Server.errorMessages = { - 0: 'Transport unknown', - 1: 'Session ID unknown', - 2: 'Bad handshake method', - 3: 'Bad request', - 4: 'Forbidden' + 0: "Transport unknown", + 1: "Session ID unknown", + 2: "Bad handshake method", + 3: "Bad request", + 4: "Forbidden" }; /** @@ -437,30 +454,34 @@ Server.errorMessages = { * @api private */ -function sendErrorMessage (req, res, code) { - const headers = {'Content-Type': 'application/json'}; +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] - })); + res.end( + JSON.stringify({ + code: Server.errors.FORBIDDEN, + message: code || Server.errorMessages[Server.errors.FORBIDDEN] + }) + ); return; } if (req.headers.origin) { - headers['Access-Control-Allow-Credentials'] = 'true'; - headers['Access-Control-Allow-Origin'] = req.headers.origin; + headers["Access-Control-Allow-Credentials"] = "true"; + headers["Access-Control-Allow-Origin"] = req.headers.origin; } else { - headers['Access-Control-Allow-Origin'] = '*'; + headers["Access-Control-Allow-Origin"] = "*"; } if (res !== undefined) { res.writeHead(400, headers); - res.end(JSON.stringify({ - code: code, - message: Server.errorMessages[code] - })); + res.end( + JSON.stringify({ + code: code, + message: Server.errorMessages[code] + }) + ); } } @@ -472,17 +493,21 @@ function sendErrorMessage (req, res, code) { * @api private */ -function abortConnection (socket, code) { +function abortConnection(socket, code) { if (socket.writable) { - const message = Server.errorMessages.hasOwnProperty(code) ? Server.errorMessages[code] : String(code || ''); + const message = Server.errorMessages.hasOwnProperty(code) + ? Server.errorMessages[code] + : String(code || ""); const length = Buffer.byteLength(message); socket.write( - 'HTTP/1.1 400 Bad Request\r\n' + - 'Connection: close\r\n' + - 'Content-type: text/html\r\n' + - 'Content-Length: ' + length + '\r\n' + - '\r\n' + - message + "HTTP/1.1 400 Bad Request\r\n" + + "Connection: close\r\n" + + "Content-type: text/html\r\n" + + "Content-Length: " + + length + + "\r\n" + + "\r\n" + + message ); } socket.destroy(); @@ -504,6 +529,7 @@ module.exports = Server; * so take care when making changes to the implementation so that the source * code size does not exceed v8's default max_inlined_source_size setting. **/ +// prettier-ignore const validHdrChars = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 - 15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 @@ -524,27 +550,23 @@ const validHdrChars = [ ] function checkInvalidHeaderChar(val) { - val += ''; - if (val.length < 1) - return false; + val += ""; + if (val.length < 1) return false; if (!validHdrChars[val.charCodeAt(0)]) { debug('invalid header, index 0, char "%s"', val.charCodeAt(0)); return true; } - if (val.length < 2) - return false; + if (val.length < 2) return false; if (!validHdrChars[val.charCodeAt(1)]) { debug('invalid header, index 1, char "%s"', val.charCodeAt(1)); return true; } - if (val.length < 3) - return false; + if (val.length < 3) return false; if (!validHdrChars[val.charCodeAt(2)]) { debug('invalid header, index 2, char "%s"', val.charCodeAt(2)); return true; } - if (val.length < 4) - return false; + if (val.length < 4) return false; if (!validHdrChars[val.charCodeAt(3)]) { debug('invalid header, index 3, char "%s"', val.charCodeAt(3)); return true; diff --git a/lib/socket.js b/lib/socket.js index 415a65a1d..97de8b6df 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -1,5 +1,5 @@ -const EventEmitter = require('events'); -const debug = require('debug')('engine:socket'); +const EventEmitter = require("events"); +const debug = require("debug")("engine:socket"); class Socket extends EventEmitter { /** @@ -7,13 +7,13 @@ class Socket extends EventEmitter { * * @api private */ - constructor (id, server, transport, req) { + constructor(id, server, transport, req) { super(); this.id = id; this.server = server; this.upgrading = false; this.upgraded = false; - this.readyState = 'opening'; + this.readyState = "opening"; this.writeBuffer = []; this.packetsFn = []; this.sentCallbackFn = []; @@ -40,23 +40,26 @@ class Socket extends EventEmitter { * * @api private */ - onOpen () { - this.readyState = 'open'; + onOpen() { + this.readyState = "open"; // sends an `open` packet this.transport.sid = this.id; - this.sendPacket('open', JSON.stringify({ - sid: this.id, - upgrades: this.getAvailableUpgrades(), - pingInterval: this.server.pingInterval, - pingTimeout: this.server.pingTimeout - })); + this.sendPacket( + "open", + JSON.stringify({ + sid: this.id, + upgrades: this.getAvailableUpgrades(), + pingInterval: this.server.pingInterval, + pingTimeout: this.server.pingTimeout + }) + ); if (this.server.initialPacket) { - this.sendPacket('message', this.server.initialPacket); + this.sendPacket("message", this.server.initialPacket); } - this.emit('open'); + this.emit("open"); this.setPingTimeout(); } @@ -66,34 +69,34 @@ class Socket extends EventEmitter { * @param {Object} packet * @api private */ - onPacket (packet) { - if ('open' === this.readyState) { + onPacket(packet) { + if ("open" === this.readyState) { // export packet event - debug('packet'); - this.emit('packet', packet); + debug("packet"); + this.emit("packet", packet); // Reset ping timeout on any packet, incoming data is a good sign of // other side's liveness this.setPingTimeout(); switch (packet.type) { - case 'ping': - debug('got ping'); - this.sendPacket('pong'); - this.emit('heartbeat'); + case "ping": + debug("got ping"); + this.sendPacket("pong"); + this.emit("heartbeat"); break; - case 'error': - this.onClose('parse error'); + case "error": + this.onClose("parse error"); break; - case 'message': - this.emit('data', packet.data); - this.emit('message', packet.data); + case "message": + this.emit("data", packet.data); + this.emit("message", packet.data); break; } } else { - debug('packet received with closed socket'); + debug("packet received with closed socket"); } } @@ -103,9 +106,9 @@ class Socket extends EventEmitter { * @param {Error} error object * @api private */ - onError (err) { - debug('transport error'); - this.onClose('transport error', err); + onError(err) { + debug("transport error"); + this.onClose("transport error", err); } /** @@ -113,10 +116,10 @@ class Socket extends EventEmitter { * * @api private */ - setPingTimeout () { + setPingTimeout() { clearTimeout(this.pingTimeoutTimer); this.pingTimeoutTimer = setTimeout(() => { - this.onClose('ping timeout'); + this.onClose("ping timeout"); }, this.server.pingInterval + this.server.pingTimeout); } @@ -126,25 +129,25 @@ class Socket extends EventEmitter { * @param {Transport} transport * @api private */ - setTransport (transport) { + setTransport(transport) { const onError = this.onError.bind(this); const onPacket = this.onPacket.bind(this); const flush = this.flush.bind(this); - const onClose = this.onClose.bind(this, 'transport close'); + const onClose = this.onClose.bind(this, "transport close"); this.transport = transport; - this.transport.once('error', onError); - this.transport.on('packet', onPacket); - this.transport.on('drain', flush); - this.transport.once('close', onClose); + this.transport.once("error", onError); + this.transport.on("packet", onPacket); + this.transport.on("drain", flush); + this.transport.once("close", onClose); // this function will manage packet events (also message callbacks) this.setupSendCallback(); - this.cleanupFn.push(function () { - transport.removeListener('error', onError); - transport.removeListener('packet', onPacket); - transport.removeListener('drain', flush); - transport.removeListener('close', onClose); + this.cleanupFn.push(function() { + transport.removeListener("error", onError); + transport.removeListener("packet", onPacket); + transport.removeListener("drain", flush); + transport.removeListener("close", onClose); }); } @@ -154,42 +157,45 @@ class Socket extends EventEmitter { * @param {Transport} transport * @api private */ - maybeUpgrade (transport) { - debug('might upgrade socket transport from "%s" to "%s"' - , this.transport.name, transport.name); + maybeUpgrade(transport) { + debug( + 'might upgrade socket transport from "%s" to "%s"', + this.transport.name, + transport.name + ); this.upgrading = true; const self = this; // set transport upgrade timer - self.upgradeTimeoutTimer = setTimeout(function () { - debug('client did not complete upgrade - closing transport'); + self.upgradeTimeoutTimer = setTimeout(function() { + debug("client did not complete upgrade - closing transport"); cleanup(); - if ('open' === transport.readyState) { + if ("open" === transport.readyState) { transport.close(); } }, this.server.upgradeTimeout); - function onPacket (packet) { - if ('ping' === packet.type && 'probe' === packet.data) { - transport.send([{ type: 'pong', data: 'probe' }]); - self.emit('upgrading', transport); + function 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') { - debug('got upgrade packet - upgrading'); + } else if ("upgrade" === packet.type && self.readyState !== "closed") { + debug("got upgrade packet - upgrading"); cleanup(); self.transport.discard(); self.upgraded = true; self.clearTransport(); self.setTransport(transport); - self.emit('upgrade', transport); + self.emit("upgrade", transport); self.setPingTimeout(); self.flush(); - if (self.readyState === 'closing') { - transport.close(function () { - self.onClose('forced close'); + if (self.readyState === "closing") { + transport.close(function() { + self.onClose("forced close"); }); } } else { @@ -199,14 +205,14 @@ class Socket extends EventEmitter { } // we force a polling cycle to ensure a fast upgrade - function check () { - if ('polling' === self.transport.name && self.transport.writable) { - debug('writing a noop packet to polling for fast upgrade'); - self.transport.send([{ type: 'noop' }]); + function check() { + if ("polling" === self.transport.name && self.transport.writable) { + debug("writing a noop packet to polling for fast upgrade"); + self.transport.send([{ type: "noop" }]); } } - function cleanup () { + function cleanup() { self.upgrading = false; clearInterval(self.checkIntervalTimer); @@ -215,32 +221,32 @@ class Socket extends EventEmitter { clearTimeout(self.upgradeTimeoutTimer); self.upgradeTimeoutTimer = null; - transport.removeListener('packet', onPacket); - transport.removeListener('close', onTransportClose); - transport.removeListener('error', onError); - self.removeListener('close', onClose); + transport.removeListener("packet", onPacket); + transport.removeListener("close", onTransportClose); + transport.removeListener("error", onError); + self.removeListener("close", onClose); } - function onError (err) { - debug('client did not complete upgrade - %s', err); + function onError(err) { + debug("client did not complete upgrade - %s", err); cleanup(); transport.close(); transport = null; } - function onTransportClose () { - onError('transport closed'); + function onTransportClose() { + onError("transport closed"); } - function onClose () { - onError('socket closed'); + function onClose() { + onError("socket closed"); } - transport.on('packet', onPacket); - transport.once('close', onTransportClose); - transport.once('error', onError); + transport.on("packet", onPacket); + transport.once("close", onTransportClose); + transport.once("error", onError); - self.once('close', onClose); + self.once("close", onClose); } /** @@ -248,7 +254,7 @@ class Socket extends EventEmitter { * * @api private */ - clearTransport () { + clearTransport() { let cleanup; const toCleanUp = this.cleanupFn.length; @@ -259,8 +265,8 @@ class Socket extends EventEmitter { } // silence further transport errors and prevent uncaught exceptions - this.transport.on('error', function () { - debug('error triggered by discarded transport'); + this.transport.on("error", function() { + debug("error triggered by discarded transport"); }); // ensure transport won't stay open @@ -274,9 +280,9 @@ class Socket extends EventEmitter { * Possible reasons: `ping timeout`, `client error`, `parse error`, * `transport error`, `server close`, `transport close` */ - onClose (reason, description) { - if ('closed' !== this.readyState) { - this.readyState = 'closed'; + onClose(reason, description) { + if ("closed" !== this.readyState) { + this.readyState = "closed"; clearTimeout(this.pingTimeoutTimer); clearInterval(this.checkIntervalTimer); this.checkIntervalTimer = null; @@ -284,13 +290,13 @@ class Socket extends EventEmitter { const self = this; // clean writeBuffer in next tick, so developers can still // grab the writeBuffer on 'close' event - process.nextTick(function () { + process.nextTick(function() { self.writeBuffer = []; }); this.packetsFn = []; this.sentCallbackFn = []; this.clearTransport(); - this.emit('close', reason, description); + this.emit("close", reason, description); } } @@ -299,27 +305,27 @@ class Socket extends EventEmitter { * * @api private */ - setupSendCallback () { + setupSendCallback() { const self = this; - this.transport.on('drain', onDrain); + this.transport.on("drain", onDrain); - this.cleanupFn.push(function () { - self.transport.removeListener('drain', onDrain); + this.cleanupFn.push(function() { + self.transport.removeListener("drain", onDrain); }); // the message was sent successfully, execute the callback - function onDrain () { + function onDrain() { if (self.sentCallbackFn.length > 0) { const seqFn = self.sentCallbackFn.splice(0, 1)[0]; - if ('function' === typeof seqFn) { - debug('executing send callback'); + if ("function" === typeof seqFn) { + debug("executing send callback"); seqFn(self.transport); } else if (Array.isArray(seqFn)) { - debug('executing batch send callback'); + debug("executing batch send callback"); const l = seqFn.length; let i = 0; for (; i < l; i++) { - if ('function' === typeof seqFn[i]) { + if ("function" === typeof seqFn[i]) { seqFn[i](self.transport); } } @@ -337,13 +343,13 @@ class Socket extends EventEmitter { * @return {Socket} for chaining * @api public */ - send (data, options, callback) { - this.sendPacket('message', data, options, callback); + send(data, options, callback) { + this.sendPacket("message", data, options, callback); return this; } - write (data, options, callback) { - this.sendPacket('message', data, options, callback); + write(data, options, callback) { + this.sendPacket("message", data, options, callback); return this; } @@ -355,8 +361,8 @@ class Socket extends EventEmitter { * @param {Object} options * @api private */ - sendPacket (type, data, options, callback) { - if ('function' === typeof options) { + sendPacket(type, data, options, callback) { + if ("function" === typeof options) { callback = options; options = null; } @@ -364,7 +370,7 @@ class Socket extends EventEmitter { options = options || {}; options.compress = false !== options.compress; - if ('closing' !== this.readyState && 'closed' !== this.readyState) { + if ("closing" !== this.readyState && "closed" !== this.readyState) { debug('sending packet "%s" (%s)', type, data); const packet = { @@ -374,7 +380,7 @@ class Socket extends EventEmitter { if (data) packet.data = data; // exports packetCreate event - this.emit('packetCreate', packet); + this.emit("packetCreate", packet); this.writeBuffer.push(packet); @@ -390,13 +396,15 @@ class Socket extends EventEmitter { * * @api private */ - flush () { - if ('closed' !== this.readyState && + flush() { + if ( + "closed" !== this.readyState && this.transport.writable && - this.writeBuffer.length) { - debug('flushing buffer to transport'); - this.emit('flush', this.writeBuffer); - this.server.emit('flush', this, this.writeBuffer); + this.writeBuffer.length + ) { + debug("flushing buffer to transport"); + this.emit("flush", this.writeBuffer); + this.server.emit("flush", this, this.writeBuffer); const wbuf = this.writeBuffer; this.writeBuffer = []; if (!this.transport.supportsFraming) { @@ -406,8 +414,8 @@ class Socket extends EventEmitter { } this.packetsFn = []; this.transport.send(wbuf); - this.emit('drain'); - this.server.emit('drain', this); + this.emit("drain"); + this.server.emit("drain", this); } } @@ -416,7 +424,7 @@ class Socket extends EventEmitter { * * @api private */ - getAvailableUpgrades () { + getAvailableUpgrades() { const availableUpgrades = []; const allUpgrades = this.server.upgrades(this.transport.name); let i = 0; @@ -437,13 +445,13 @@ class Socket extends EventEmitter { * @return {Socket} for chaining * @api public */ - close (discard) { - if ('open' !== this.readyState) return; + close(discard) { + if ("open" !== this.readyState) return; - this.readyState = 'closing'; + this.readyState = "closing"; if (this.writeBuffer.length) { - this.once('drain', this.closeTransport.bind(this, discard)); + this.once("drain", this.closeTransport.bind(this, discard)); return; } @@ -456,9 +464,9 @@ class Socket extends EventEmitter { * @param {Boolean} discard * @api private */ - closeTransport (discard) { + closeTransport(discard) { if (discard) this.transport.discard(); - this.transport.close(this.onClose.bind(this, 'forced close')); + this.transport.close(this.onClose.bind(this, "forced close")); } } diff --git a/lib/transport.js b/lib/transport.js index 0beed0a04..f45c5911b 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -1,6 +1,6 @@ -const EventEmitter = require('events'); -const parser = require('engine.io-parser'); -const debug = require('debug')('engine:transport'); +const EventEmitter = require("events"); +const parser = require("engine.io-parser"); +const debug = require("debug")("engine:transport"); /** * Noop function. @@ -8,7 +8,7 @@ const debug = require('debug')('engine:transport'); * @api private */ -function noop () {} +function noop() {} class Transport extends EventEmitter { /** @@ -17,9 +17,9 @@ class Transport extends EventEmitter { * @param {http.IncomingMessage} request * @api public */ - constructor (req) { + constructor(req) { super(); - this.readyState = 'open'; + this.readyState = "open"; this.discarded = false; } @@ -28,7 +28,7 @@ class Transport extends EventEmitter { * * @api private */ - discard () { + discard() { this.discarded = true; } @@ -38,8 +38,8 @@ class Transport extends EventEmitter { * @param {http.IncomingMessage} request * @api private */ - onRequest (req) { - debug('setting request'); + onRequest(req) { + debug("setting request"); this.req = req; } @@ -48,10 +48,10 @@ class Transport extends EventEmitter { * * @api private */ - close (fn) { - if ('closed' === this.readyState || 'closing' === this.readyState) return; + close(fn) { + if ("closed" === this.readyState || "closing" === this.readyState) return; - this.readyState = 'closing'; + this.readyState = "closing"; this.doClose(fn || noop); } @@ -62,14 +62,14 @@ class Transport extends EventEmitter { * @param {Object} error description * @api private */ - onError (msg, desc) { - if (this.listeners('error').length) { + onError(msg, desc) { + if (this.listeners("error").length) { const err = new Error(msg); - err.type = 'TransportError'; + err.type = "TransportError"; err.description = desc; - this.emit('error', err); + this.emit("error", err); } else { - debug('ignored transport error %s (%s)', msg, desc); + debug("ignored transport error %s (%s)", msg, desc); } } @@ -79,8 +79,8 @@ class Transport extends EventEmitter { * @param {Object} packet * @api private */ - onPacket (packet) { - this.emit('packet', packet); + onPacket(packet) { + this.emit("packet", packet); } /** @@ -89,7 +89,7 @@ class Transport extends EventEmitter { * @param {String} data * @api private */ - onData (data) { + onData(data) { this.onPacket(parser.decodePacket(data)); } @@ -98,9 +98,9 @@ class Transport extends EventEmitter { * * @api private */ - onClose () { - this.readyState = 'closed'; - this.emit('close'); + onClose() { + this.readyState = "closed"; + this.emit("close"); } } diff --git a/lib/transports/index.js b/lib/transports/index.js index 0b6a40b42..ea106551e 100644 --- a/lib/transports/index.js +++ b/lib/transports/index.js @@ -1,5 +1,5 @@ -const XHR = require('./polling-xhr'); -const JSONP = require('./polling-jsonp'); +const XHR = require("./polling-xhr"); +const JSONP = require("./polling-jsonp"); /** * Export transports. @@ -7,14 +7,14 @@ const JSONP = require('./polling-jsonp'); module.exports = exports = { polling: polling, - websocket: require('./websocket') + websocket: require("./websocket") }; /** * Export upgrades map. */ -exports.polling.upgradesTo = ['websocket']; +exports.polling.upgradesTo = ["websocket"]; /** * Polling polymorphic constructor. @@ -22,8 +22,8 @@ exports.polling.upgradesTo = ['websocket']; * @api private */ -function polling (req) { - if ('string' === typeof req._query.j) { +function polling(req) { + if ("string" === typeof req._query.j) { return new JSONP(req); } else { return new XHR(req); diff --git a/lib/transports/polling-jsonp.js b/lib/transports/polling-jsonp.js index de3de36e7..bb07ce857 100644 --- a/lib/transports/polling-jsonp.js +++ b/lib/transports/polling-jsonp.js @@ -1,5 +1,5 @@ -const Polling = require('./polling'); -const qs = require('querystring'); +const Polling = require("./polling"); +const qs = require("querystring"); const rDoubleSlashes = /\\\\n/g; const rSlashes = /(\\)?\\n/g; @@ -9,11 +9,11 @@ class JSONP extends Polling { * * @api public */ - constructor (req) { + constructor(req) { super(req); - this.head = '___eio[' + (req._query.j || '').replace(/[^0-9]/g, '') + ']('; - this.foot = ');'; + this.head = "___eio[" + (req._query.j || "").replace(/[^0-9]/g, "") + "]("; + this.foot = ");"; } /** @@ -22,17 +22,17 @@ class JSONP extends Polling { * * @api private */ - onData (data) { + onData(data) { // we leverage the qs module so that we get built-in DoS protection // and the fast alternative to decodeURIComponent data = qs.parse(data).d; - if ('string' === typeof data) { + if ("string" === typeof data) { // client will send already escaped newlines as \\\\n and newlines as \\n // \\n must be replaced with \n and \\\\n with \\n - data = data.replace(rSlashes, function (match, slashes) { - return slashes ? match : '\n'; + data = data.replace(rSlashes, function(match, slashes) { + return slashes ? match : "\n"; }); - super.onData(data.replace(rDoubleSlashes, '\\n')); + super.onData(data.replace(rDoubleSlashes, "\\n")); } } @@ -41,12 +41,12 @@ class JSONP extends Polling { * * @api private */ - doWrite (data, options, callback) { + doWrite(data, options, callback) { // we must output valid javascript, not valid json // see: http://timelessrepo.com/json-isnt-a-javascript-subset const js = JSON.stringify(data) - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029'); + .replace(/\u2028/g, "\\u2028") + .replace(/\u2029/g, "\\u2029"); // prepare response data = this.head + js + this.foot; diff --git a/lib/transports/polling-xhr.js b/lib/transports/polling-xhr.js index 977e3c16d..c4a63e26b 100644 --- a/lib/transports/polling-xhr.js +++ b/lib/transports/polling-xhr.js @@ -1,4 +1,4 @@ -const Polling = require('./polling'); +const Polling = require("./polling"); class XHR extends Polling { /** @@ -7,11 +7,11 @@ class XHR extends Polling { * @param {http.IncomingMessage} * @api private */ - onRequest (req) { - if ('OPTIONS' === req.method) { + onRequest(req) { + if ("OPTIONS" === req.method) { const res = req.res; const headers = this.headers(req); - headers['Access-Control-Allow-Headers'] = 'Content-Type'; + headers["Access-Control-Allow-Headers"] = "Content-Type"; res.writeHead(200, headers); res.end(); } else { @@ -26,14 +26,14 @@ class XHR extends Polling { * @param {Object} extra headers * @api private */ - headers (req, headers) { + headers(req, headers) { headers = headers || {}; if (req.headers.origin) { - headers['Access-Control-Allow-Credentials'] = 'true'; - headers['Access-Control-Allow-Origin'] = req.headers.origin; + headers["Access-Control-Allow-Credentials"] = "true"; + headers["Access-Control-Allow-Origin"] = req.headers.origin; } else { - headers['Access-Control-Allow-Origin'] = '*'; + headers["Access-Control-Allow-Origin"] = "*"; } return super.headers(req, headers); diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 063360aaf..680f8642c 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -1,8 +1,8 @@ -const Transport = require('../transport'); -const parser = require('engine.io-parser'); -const zlib = require('zlib'); -const accepts = require('accepts'); -const debug = require('debug')('engine:polling'); +const Transport = require("../transport"); +const parser = require("engine.io-parser"); +const zlib = require("zlib"); +const accepts = require("accepts"); +const debug = require("debug")("engine:polling"); const compressionMethods = { gzip: zlib.createGzip, @@ -15,7 +15,7 @@ class Polling extends Transport { * * @api public. */ - constructor (req) { + constructor(req) { super(req); this.closeTimeout = 30 * 1000; @@ -28,8 +28,8 @@ class Polling extends Transport { * * @api public */ - get name () { - return 'polling'; + get name() { + return "polling"; } /** @@ -38,12 +38,12 @@ class Polling extends Transport { * @param {http.IncomingMessage} * @api private */ - onRequest (req) { + onRequest(req) { const res = req.res; - if ('GET' === req.method) { + if ("GET" === req.method) { this.onPollRequest(req, res); - } else if ('POST' === req.method) { + } else if ("POST" === req.method) { this.onDataRequest(req, res); } else { res.writeHead(500); @@ -56,42 +56,42 @@ class Polling extends Transport { * * @api private */ - onPollRequest (req, res) { + onPollRequest(req, res) { if (this.req) { - debug('request overlap'); + debug("request overlap"); // assert: this.res, '.req and .res should be (un)set together' - this.onError('overlap from client'); + this.onError("overlap from client"); res.writeHead(500); res.end(); return; } - debug('setting request'); + debug("setting request"); this.req = req; this.res = res; const self = this; - function onClose () { - self.onError('poll connection closed prematurely'); + function onClose() { + self.onError("poll connection closed prematurely"); } - function cleanup () { - req.removeListener('close', onClose); + function cleanup() { + req.removeListener("close", onClose); self.req = self.res = null; } req.cleanup = cleanup; - req.on('close', onClose); + req.on("close", onClose); this.writable = true; - this.emit('drain'); + 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' }]); + debug("triggering empty send to append close packet"); + this.send([{ type: "noop" }]); } } @@ -100,36 +100,36 @@ class Polling extends Transport { * * @api private */ - onDataRequest (req, res) { + onDataRequest(req, res) { if (this.dataReq) { // assert: this.dataRes, '.dataReq and .dataRes should be (un)set together' - this.onError('data request overlap from client'); + this.onError("data request overlap from client"); res.writeHead(500); res.end(); return; } - const isBinary = 'application/octet-stream' === req.headers['content-type']; + const isBinary = "application/octet-stream" === req.headers["content-type"]; this.dataReq = req; this.dataRes = res; - let chunks = isBinary ? Buffer.concat([]) : ''; + let chunks = isBinary ? Buffer.concat([]) : ""; const self = this; - function cleanup () { - req.removeListener('data', onData); - req.removeListener('end', onEnd); - req.removeListener('close', onClose); + function cleanup() { + req.removeListener("data", onData); + req.removeListener("end", onEnd); + req.removeListener("close", onClose); self.dataReq = self.dataRes = chunks = null; } - function onClose () { + function onClose() { cleanup(); - self.onError('data request connection closed prematurely'); + self.onError("data request connection closed prematurely"); } - function onData (data) { + function onData(data) { let contentLength; if (isBinary) { chunks = Buffer.concat([chunks, data]); @@ -140,30 +140,30 @@ class Polling extends Transport { } if (contentLength > self.maxHttpBufferSize) { - chunks = isBinary ? Buffer.concat([]) : ''; + chunks = isBinary ? Buffer.concat([]) : ""; req.connection.destroy(); } } - function onEnd () { + function onEnd() { self.onData(chunks); 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', - 'Content-Length': 2 + "Content-Type": "text/html", + "Content-Length": 2 }; res.writeHead(200, self.headers(req, headers)); - res.end('ok'); + res.end("ok"); cleanup(); } - req.on('close', onClose); - if (!isBinary) req.setEncoding('utf8'); - req.on('data', onData); - req.on('end', onEnd); + req.on("close", onClose); + if (!isBinary) req.setEncoding("utf8"); + req.on("data", onData); + req.on("end", onEnd); } /** @@ -172,12 +172,12 @@ class Polling extends Transport { * @param {String} encoded payload * @api private */ - onData (data) { + onData(data) { debug('received "%s"', data); const self = this; - const callback = function (packet) { - if ('close' === packet.type) { - debug('got xhr close packet'); + const callback = function(packet) { + if ("close" === packet.type) { + debug("got xhr close packet"); self.onClose(); return false; } @@ -193,10 +193,10 @@ class Polling extends Transport { * * @api private */ - onClose () { + onClose() { if (this.writable) { // close pending poll request - this.send([{ type: 'noop' }]); + this.send([{ type: "noop" }]); } super.onClose(); } @@ -207,19 +207,19 @@ class Polling extends Transport { * @param {Object} packet * @api private */ - send (packets) { + send(packets) { this.writable = false; if (this.shouldClose) { - debug('appending close packet to payload'); - packets.push({ type: 'close' }); + debug("appending close packet to payload"); + packets.push({ type: "close" }); this.shouldClose(); this.shouldClose = null; } const self = this; - parser.encodePayload(packets, this.supportsBinary, function (data) { - const compress = packets.some(function (packet) { + parser.encodePayload(packets, this.supportsBinary, function(data) { + const compress = packets.some(function(packet) { return packet.options && packet.options.compress; }); self.write(data, { compress: compress }); @@ -233,10 +233,10 @@ class Polling extends Transport { * @param {Object} options * @api private */ - write (data, options) { + write(data, options) { debug('writing "%s"', data); const self = this; - this.doWrite(data, options, function () { + this.doWrite(data, options, function() { self.req.cleanup(); }); } @@ -246,17 +246,17 @@ class Polling extends Transport { * * @api private */ - doWrite (data, options, callback) { + doWrite(data, options, callback) { const self = this; // explicit UTF-8 is required for pages not served under utf - const isString = typeof data === 'string'; + const isString = typeof data === "string"; const contentType = isString - ? 'text/plain; charset=UTF-8' - : 'application/octet-stream'; + ? "text/plain; charset=UTF-8" + : "application/octet-stream"; const headers = { - 'Content-Type': contentType + "Content-Type": contentType }; if (!this.httpCompression || !options.compress) { @@ -270,13 +270,13 @@ class Polling extends Transport { return; } - const encoding = accepts(this.req).encodings(['gzip', 'deflate']); + const encoding = accepts(this.req).encodings(["gzip", "deflate"]); if (!encoding) { respond(data); return; } - this.compress(data, encoding, function (err, data) { + this.compress(data, encoding, function(err, data) { if (err) { self.res.writeHead(500); self.res.end(); @@ -284,12 +284,13 @@ class Polling extends Transport { return; } - headers['Content-Encoding'] = encoding; + headers["Content-Encoding"] = encoding; respond(data); }); - function respond (data) { - headers['Content-Length'] = 'string' === typeof data ? Buffer.byteLength(data) : data.length; + 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(); @@ -301,19 +302,19 @@ class Polling extends Transport { * * @api private */ - compress (data, encoding, callback) { - debug('compressing'); + compress(data, encoding, callback) { + debug("compressing"); const buffers = []; let nread = 0; compressionMethods[encoding](this.httpCompression) - .on('error', callback) - .on('data', function (chunk) { + .on("error", callback) + .on("data", function(chunk) { buffers.push(chunk); nread += chunk.length; }) - .on('end', function () { + .on("end", function() { callback(null, Buffer.concat(buffers, nread)); }) .end(data); @@ -324,31 +325,31 @@ class Polling extends Transport { * * @api private */ - doClose (fn) { - debug('closing'); + doClose(fn) { + debug("closing"); const self = this; let closeTimeoutTimer; if (this.dataReq) { - debug('aborting ongoing data request'); + debug("aborting ongoing data request"); this.dataReq.destroy(); } if (this.writable) { - debug('transport writable - closing right away'); - this.send([{ type: 'close' }]); + debug("transport writable - closing right away"); + this.send([{ type: "close" }]); onClose(); } else if (this.discarded) { - debug('transport discarded - closing right away'); + debug("transport discarded - closing right away"); onClose(); } else { - debug('transport not writable - buffering orderly close'); + debug("transport not writable - buffering orderly close"); this.shouldClose = onClose; closeTimeoutTimer = setTimeout(onClose, this.closeTimeout); } - function onClose () { + function onClose() { clearTimeout(closeTimeoutTimer); fn(); self.onClose(); @@ -362,17 +363,17 @@ class Polling extends Transport { * @param {Object} extra headers * @api private */ - headers (req, headers) { + 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'; + const ua = req.headers["user-agent"]; + if (ua && (~ua.indexOf(";MSIE") || ~ua.indexOf("Trident/"))) { + headers["X-XSS-Protection"] = "0"; } - this.emit('headers', headers); + this.emit("headers", headers); return headers; } } diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index ee228e9ee..7f0f0cb0d 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -1,6 +1,6 @@ -const Transport = require('../transport'); -const parser = require('engine.io-parser'); -const debug = require('debug')('engine:ws'); +const Transport = require("../transport"); +const parser = require("engine.io-parser"); +const debug = require("debug")("engine:ws"); class WebSocket extends Transport { /** @@ -9,14 +9,14 @@ class WebSocket extends Transport { * @param {http.IncomingMessage} * @api public */ - constructor (req) { + constructor(req) { super(req); this.socket = req.websocket; - 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.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; @@ -27,8 +27,8 @@ class WebSocket extends Transport { * * @api public */ - get name () { - return 'websocket'; + get name() { + return "websocket"; } /** @@ -36,7 +36,7 @@ class WebSocket extends Transport { * * @api public */ - get handlesUpgrades () { + get handlesUpgrades() { return true; } @@ -45,7 +45,7 @@ class WebSocket extends Transport { * * @api public */ - get supportsFraming () { + get supportsFraming() { return true; } @@ -55,7 +55,7 @@ class WebSocket extends Transport { * @param {String} encoded packet * @api private */ - onData (data) { + onData(data) { debug('received "%s"', data); super.onData(data); } @@ -66,7 +66,7 @@ class WebSocket extends Transport { * @param {Array} packets * @api private */ - send (packets) { + send(packets) { var self = this; for (var i = 0; i < packets.length; i++) { @@ -74,7 +74,7 @@ class WebSocket extends Transport { parser.encodePacket(packet, self.supportsBinary, send); } - function send (data) { + function send(data) { debug('writing "%s"', data); // always creates a new object since ws modifies it @@ -84,7 +84,8 @@ class WebSocket extends Transport { } if (self.perMessageDeflate) { - var len = 'string' === typeof data ? Buffer.byteLength(data) : data.length; + var len = + "string" === typeof data ? Buffer.byteLength(data) : data.length; if (len < self.perMessageDeflate.threshold) { opts.compress = false; } @@ -94,10 +95,10 @@ class WebSocket extends Transport { self.socket.send(data, opts, onEnd); } - function onEnd (err) { - if (err) return self.onError('write error', err.stack); + function onEnd(err) { + if (err) return self.onError("write error", err.stack); self.writable = true; - self.emit('drain'); + self.emit("drain"); } } @@ -106,8 +107,8 @@ class WebSocket extends Transport { * * @api private */ - doClose (fn) { - debug('closing'); + doClose(fn) { + debug("closing"); this.socket.close(); fn && fn(); } diff --git a/package-lock.json b/package-lock.json index 542c3b12f..58d3b9dd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -203,27 +203,6 @@ "sprintf-js": "~1.0.2" } }, - "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" - } - }, - "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", @@ -292,498 +271,6 @@ "eslint-visitor-keys": "^1.0.0" } }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "^0.10.0" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-preset-es2015": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", - "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.24.1", - "babel-plugin-transform-es2015-classes": "^6.24.1", - "babel-plugin-transform-es2015-computed-properties": "^6.24.1", - "babel-plugin-transform-es2015-destructuring": "^6.22.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", - "babel-plugin-transform-es2015-for-of": "^6.22.0", - "babel-plugin-transform-es2015-function-name": "^6.24.1", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-umd": "^6.24.1", - "babel-plugin-transform-es2015-object-super": "^6.24.1", - "babel-plugin-transform-es2015-parameters": "^6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", - "babel-plugin-transform-regenerator": "^6.24.1" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - }, - "dependencies": { - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - } - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - }, - "dependencies": { - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, - "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - } - } - }, "babylon": { "version": "7.0.0-beta.44", "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", @@ -967,12 +454,6 @@ "typedarray": "^0.0.6" } }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", @@ -984,12 +465,6 @@ "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", "dev": true }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1021,15 +496,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1093,45 +559,6 @@ "has-binary2": "~1.0.2" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz", - "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -1210,151 +637,15 @@ } } }, - "eslint-config-standard": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", - "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", - "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.1.tgz", - "integrity": "sha512-GcNwsYv8MfoEBSbAmV+PSVn2RlhpCShbLImtNviAYa/LE0PgNqxH5tLi1Ld9yeFwdjHsarXK+7G9vsyddmB6dw==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-plugin-import": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.0.tgz", - "integrity": "sha512-NK42oA0mUc8Ngn4kONOPsPB1XhbUvNHqF+g307dPV28aknPoiNnKLFd9em4nkswwepdF5ouieqv5Th/63U7YJQ==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "array.prototype.flat": "^1.2.1", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.1", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.0", - "read-pkg-up": "^2.0.0", - "resolve": "^1.12.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-plugin-node": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.2.1.tgz", - "integrity": "sha512-xhPXrh0Vl/b7870uEbaumb2Q+LxaEcOQ3kS1jtIXanBAwpMre1l5q/l2l/hESYJGEFKuI78bp6Uw50hlpr7B+g==", + "eslint-config-prettier": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.9.0.tgz", + "integrity": "sha512-k4E14HBtcLv0uqThaI6I/n1LEqROp8XaPu6SO9Z32u5NlGRC07Enu1Bh2KEFw4FNHbekH8yzbIU9kUGxbiGmCA==", "dev": true, "requires": { - "ignore": "^3.3.6", - "minimatch": "^3.0.4", - "resolve": "^1.3.3", - "semver": "5.3.0" - }, - "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - } + "get-stdin": "^6.0.0" } }, - "eslint-plugin-promise": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz", - "integrity": "sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==", - "dev": true - }, - "eslint-plugin-standard": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", - "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", - "dev": true - }, "eslint-scope": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", @@ -1477,15 +768,6 @@ "object-assign": "^4.0.1" } }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, "flat-cache": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", @@ -1521,18 +803,18 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "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", @@ -1565,15 +847,6 @@ "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -1603,24 +876,12 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, - "hosted-git-info": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", - "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", - "dev": true - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1712,24 +973,6 @@ "loose-envify": "^1.0.0" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, "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", @@ -1742,36 +985,12 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, "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 }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", @@ -1827,28 +1046,6 @@ "type-check": "~0.3.2" } }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", @@ -2015,60 +1212,12 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2107,39 +1256,6 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", @@ -2158,12 +1274,6 @@ "better-assert": "~1.0.0" } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2176,36 +1286,6 @@ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", @@ -2218,10 +1298,10 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "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 }, "process-nextick-args": { @@ -2248,27 +1328,6 @@ "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==", "dev": true }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -2292,69 +1351,12 @@ } } }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - }, "regexpp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", "dev": true }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", @@ -2365,15 +1367,6 @@ "resolve-from": "^1.0.0" } }, - "resolve": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", - "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, "resolve-from": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", @@ -2483,38 +1476,6 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -2548,26 +1509,6 @@ } } }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2586,12 +1527,6 @@ "ansi-regex": "^2.0.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -2710,16 +1645,6 @@ "integrity": "sha512-HNMztPP5A1sKuVFmdZ6BPVpBQd5bUjNC8EFMFiICK+oho/OQsAJy5hnIx4btMHiOk8j04f/DbIlqnEZ9d72dqg==", "dev": true }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index d99b2c31b..73d87a85b 100644 --- a/package.json +++ b/package.json @@ -34,23 +34,21 @@ }, "devDependencies": { "babel-eslint": "^8.0.2", - "babel-preset-es2015": "^6.24.0", "engine.io-client": "3.4.0", - "eslint": "^4.5.0", - "eslint-config-standard": "^10.2.1", - "eslint-plugin-import": "^2.7.0", - "eslint-plugin-node": "^5.1.1", - "eslint-plugin-promise": "^3.5.0", - "eslint-plugin-standard": "^3.0.1", + "eslint": "^4.19.1", + "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", "mocha": "^4.0.1", + "prettier": "^1.19.1", "s": "0.1.1", "superagent": "^3.8.1", "uws": "~9.14.0" }, "scripts": { "lint": "eslint lib/ test/ *.js", - "test": "npm run lint && mocha && EIO_WS_ENGINE=uws mocha" + "test": "npm run lint && npm run format:check && mocha && EIO_WS_ENGINE=uws mocha", + "format:check": "prettier --check 'lib/**/*.js' 'test/**/*.js'", + "format:fix": "prettier --write 'lib/**/*.js' 'test/**/*.js'" }, "repository": { "type": "git", diff --git a/test/common.js b/test/common.js index 1f7244445..14845c7ff 100644 --- a/test/common.js +++ b/test/common.js @@ -1,21 +1,20 @@ - /** * Module dependencies. */ -var eio = require('..'); +var eio = require(".."); /** * Listen shortcut that fires a callback on an ephemeral port. */ -exports.listen = function (opts, fn) { - if ('function' === typeof opts) { +exports.listen = function(opts, fn) { + if ("function" === typeof opts) { fn = opts; opts = {}; } - var e = eio.listen(0, opts, function () { + var e = eio.listen(0, opts, function() { fn(e.httpServer.address().port); }); @@ -26,4 +25,4 @@ exports.listen = function (opts, fn) { * Sprintf util. */ -require('s').extend(); +require("s").extend(); diff --git a/test/engine.io.js b/test/engine.io.js index 9387094c9..95f0449a1 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -1,41 +1,40 @@ - /** * Test dependencies. */ -var net = require('net'); -var eio = require('..'); -var listen = require('./common').listen; -var expect = require('expect.js'); -var request = require('superagent'); -var http = require('http'); +var net = require("net"); +var eio = require(".."); +var listen = require("./common").listen; +var expect = require("expect.js"); +var request = require("superagent"); +var http = require("http"); /** * Tests. */ -describe('engine', function () { - it('should expose protocol number', function () { - expect(eio.protocol).to.be.a('number'); +describe("engine", function() { + it("should expose protocol number", function() { + expect(eio.protocol).to.be.a("number"); }); - it('should be the same version as client', function () { - var version = require('../package').version; - expect(version).to.be(require('engine.io-client/package').version); + it("should be the same version as client", function() { + var version = require("../package").version; + expect(version).to.be(require("engine.io-client/package").version); }); - describe('engine()', function () { - it('should create a Server when require called with no arguments', function () { + describe("engine()", function() { + it("should create a Server when require called with no arguments", function() { var engine = eio(); expect(engine).to.be.an(eio.Server); expect(engine.ws).to.be.ok(); }); }); - describe('listen', function () { - it('should open a http server that returns 501', function (done) { - listen(function (port) { - request.get('http://localhost:%d/'.s(port), function (err, res) { + describe("listen", function() { + it("should open a http server that returns 501", function(done) { + listen(function(port) { + request.get("http://localhost:%d/".s(port), function(err, res) { expect(err).to.be.an(Error); expect(res.status).to.be(501); done(); @@ -44,189 +43,205 @@ describe('engine', function () { }); }); - describe('attach()', function () { - it('should work from require()', function () { + describe("attach()", function() { + it("should work from require()", function() { var server = http.createServer(); var engine = eio(server); expect(engine).to.be.an(eio.Server); }); - it('should return an engine.Server', function () { + it("should return an engine.Server", function() { var server = http.createServer(); var engine = eio.attach(server); expect(engine).to.be.an(eio.Server); }); - it('should attach engine to an http server', function (done) { + it("should attach engine to an http server", function(done) { var server = http.createServer(); eio.attach(server); - server.listen(function () { - var uri = 'http://localhost:%d/engine.io/default/'.s(server.address().port); - request.get(uri, function (err, res) { + server.listen(function() { + var uri = "http://localhost:%d/engine.io/default/".s( + server.address().port + ); + request.get(uri, function(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'); - server.once('close', done); + expect(res.body.message).to.be("Transport unknown"); + server.once("close", done); server.close(); }); }); }); - it('should destroy upgrades not handled by engine', function (done) { + it("should destroy upgrades not handled by engine", function(done) { var server = http.createServer(); eio.attach(server, { destroyUpgradeTimeout: 50 }); - server.listen(function () { + server.listen(function() { var client = net.createConnection(server.address().port); - client.setEncoding('ascii'); - client.write([ - 'GET / HTTP/1.1', - 'Connection: Upgrade', - 'Upgrade: IRC/6.9', - '', '' - ].join('\r\n')); - - var check = setTimeout(function () { - done(new Error('Client should have ended')); + client.setEncoding("ascii"); + client.write( + [ + "GET / HTTP/1.1", + "Connection: Upgrade", + "Upgrade: IRC/6.9", + "", + "" + ].join("\r\n") + ); + + var check = setTimeout(function() { + done(new Error("Client should have ended")); }, 100); - client.on('end', function () { + client.on("end", function() { clearTimeout(check); done(); }); }); }); - it('should not destroy unhandled upgrades with destroyUpgrade:false', function (done) { + it("should not destroy unhandled upgrades with destroyUpgrade:false", function(done) { var server = http.createServer(); eio.attach(server, { destroyUpgrade: false, destroyUpgradeTimeout: 50 }); - server.listen(function () { + server.listen(function() { var client = net.createConnection(server.address().port); - client.on('connect', function () { - client.setEncoding('ascii'); - client.write([ - 'GET / HTTP/1.1', - 'Connection: Upgrade', - 'Upgrade: IRC/6.9', - '', '' - ].join('\r\n')); - - setTimeout(function () { - client.removeListener('end', onEnd); + client.on("connect", function() { + client.setEncoding("ascii"); + client.write( + [ + "GET / HTTP/1.1", + "Connection: Upgrade", + "Upgrade: IRC/6.9", + "", + "" + ].join("\r\n") + ); + + setTimeout(function() { + client.removeListener("end", onEnd); done(); }, 100); - function onEnd () { - done(new Error('Client should not end')); + function onEnd() { + done(new Error("Client should not end")); } - client.on('end', onEnd); + client.on("end", onEnd); }); }); }); - it('should destroy unhandled upgrades with after a timeout', function (done) { + it("should destroy unhandled upgrades with after a timeout", function(done) { var server = http.createServer(); eio.attach(server, { destroyUpgradeTimeout: 200 }); - server.listen(function () { + server.listen(function() { var client = net.createConnection(server.address().port); - client.on('connect', function () { - client.setEncoding('ascii'); - client.write([ - 'GET / HTTP/1.1', - 'Connection: Upgrade', - 'Upgrade: IRC/6.9', - '', '' - ].join('\r\n')); + client.on("connect", function() { + client.setEncoding("ascii"); + client.write( + [ + "GET / HTTP/1.1", + "Connection: Upgrade", + "Upgrade: IRC/6.9", + "", + "" + ].join("\r\n") + ); // send from client to server // tests that socket is still alive // this will not keep the socket open as the server does not handle it - setTimeout(function () { - client.write('foo'); + setTimeout(function() { + client.write("foo"); }, 100); - function onEnd () { + function onEnd() { done(); } - client.on('end', onEnd); + client.on("end", onEnd); }); }); }); - it('should not destroy handled upgrades with after a timeout', function (done) { + it("should not destroy handled upgrades with after a timeout", function(done) { var server = http.createServer(); eio.attach(server, { destroyUpgradeTimeout: 100 }); // write to the socket to keep engine.io from closing it by writing before the timeout - server.on('upgrade', function (req, socket) { - socket.write('foo'); - socket.on('data', function (chunk) { - expect(chunk.toString()).to.be('foo'); + server.on("upgrade", function(req, socket) { + socket.write("foo"); + socket.on("data", function(chunk) { + expect(chunk.toString()).to.be("foo"); socket.end(); }); }); - server.listen(function () { + server.listen(function() { var client = net.createConnection(server.address().port); - client.on('connect', function () { - client.setEncoding('ascii'); - client.write([ - 'GET / HTTP/1.1', - 'Connection: Upgrade', - 'Upgrade: IRC/6.9', - '', '' - ].join('\r\n')); + client.on("connect", function() { + client.setEncoding("ascii"); + client.write( + [ + "GET / HTTP/1.1", + "Connection: Upgrade", + "Upgrade: IRC/6.9", + "", + "" + ].join("\r\n") + ); // test that socket is still open by writing after the timeout period - setTimeout(function () { - client.write('foo'); + setTimeout(function() { + client.write("foo"); }, 200); - client.on('data', function (data) { - }); + client.on("data", function(data) {}); - client.on('end', done); + client.on("end", done); }); }); }); - it('should preserve original request listeners', function (done) { + it("should preserve original request listeners", function(done) { var listeners = 0; - var server = http.createServer(function (req, res) { + var server = http.createServer(function(req, res) { expect(req && res).to.be.ok(); listeners++; }); - server.on('request', function (req, res) { + server.on("request", function(req, res) { expect(req && res).to.be.ok(); res.writeHead(200); - res.end(''); + res.end(""); listeners++; }); eio.attach(server); - server.listen(function () { + server.listen(function() { var port = server.address().port; - request.get('http://localhost:%d/engine.io/default/'.s(port), function (err, res) { + request.get("http://localhost:%d/engine.io/default/".s(port), function( + 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), function (err, res) { + expect(res.body.message).to.be("Transport unknown"); + request.get("http://localhost:%d/test".s(port), function(err, res) { expect(err).to.be(null); expect(res.status).to.be(200); expect(listeners).to.eql(2); - server.once('close', done); + server.once("close", done); server.close(); }); }); diff --git a/test/fixtures/server-close-upgraded.js b/test/fixtures/server-close-upgraded.js index dc608d30b..df773cd28 100644 --- a/test/fixtures/server-close-upgraded.js +++ b/test/fixtures/server-close-upgraded.js @@ -1,9 +1,9 @@ -var eioc = require('engine.io-client'); -var listen = require('../common').listen; +var eioc = require("engine.io-client"); +var listen = require("../common").listen; -var engine = listen(function (port) { - var socket = new eioc.Socket('ws://localhost:' + port); - socket.on('upgrade', function () { +var engine = listen(function(port) { + var socket = new eioc.Socket("ws://localhost:" + port); + socket.on("upgrade", function() { engine.httpServer.close(); engine.close(); }); diff --git a/test/fixtures/server-close-upgrading.js b/test/fixtures/server-close-upgrading.js index 4ebfcc52b..f47cfba6a 100644 --- a/test/fixtures/server-close-upgrading.js +++ b/test/fixtures/server-close-upgrading.js @@ -1,9 +1,9 @@ -var eioc = require('engine.io-client'); -var listen = require('../common').listen; +var eioc = require("engine.io-client"); +var listen = require("../common").listen; -var engine = listen(function (port) { - var socket = new eioc.Socket('ws://localhost:' + port); - socket.on('upgrading', function () { +var engine = listen(function(port) { + var socket = new eioc.Socket("ws://localhost:" + port); + socket.on("upgrading", function() { engine.httpServer.close(); engine.close(); }); diff --git a/test/fixtures/server-close.js b/test/fixtures/server-close.js index 07c312094..c1fd4537f 100644 --- a/test/fixtures/server-close.js +++ b/test/fixtures/server-close.js @@ -1,9 +1,9 @@ -var eioc = require('engine.io-client'); -var listen = require('../common').listen; +var eioc = require("engine.io-client"); +var listen = require("../common").listen; -var engine = listen(function (port) { - var socket = new eioc.Socket('ws://localhost:' + port); - socket.on('open', function () { +var engine = listen(function(port) { + var socket = new eioc.Socket("ws://localhost:" + port); + socket.on("open", function() { engine.httpServer.close(); engine.close(); }); diff --git a/test/jsonp.js b/test/jsonp.js index 776582818..39407cd32 100644 --- a/test/jsonp.js +++ b/test/jsonp.js @@ -1,68 +1,68 @@ - /** * Module dependencies. */ -var eioc = require('engine.io-client'); -var listen = require('./common').listen; -var expect = require('expect.js'); -var request = require('superagent'); +var eioc = require("engine.io-client"); +var listen = require("./common").listen; +var expect = require("expect.js"); +var request = require("superagent"); -describe('JSONP', function () { - before(function () { +describe("JSONP", function() { + before(function() { // we have to override the browser's functionality for JSONP - document = { // eslint-disable-line no-global-assign + document = { + // eslint-disable-line no-global-assign body: { - appendChild: function () {}, - removeChild: function () {} + appendChild: function() {}, + removeChild: function() {} } }; - document.createElement = function (name) { + document.createElement = function(name) { var self = this; - if ('script' === name) { + if ("script" === name) { var script = {}; - script.__defineGetter__('parentNode', function () { + script.__defineGetter__("parentNode", function() { return document.body; }); - script.__defineSetter__('src', function (uri) { - request.get(uri).end(function (err, res) { + script.__defineSetter__("src", function(uri) { + request.get(uri).end(function(err, res) { expect(err).to.be(null); eval(res.text); // eslint-disable-line no-eval }); }); return script; - } else if ('form' === name) { + } else if ("form" === name) { var form = { style: {}, - action: '', - parentNode: { removeChild: function () {} }, - removeChild: function () {}, - setAttribute: function () {}, - appendChild: function () {}, - submit: function () { + action: "", + parentNode: { removeChild: function() {} }, + removeChild: function() {}, + setAttribute: function() {}, + appendChild: function() {}, + submit: function() { request .post(this.action) - .type('form') + .type("form") .send({ d: self.areaValue }) - .end(function () {}); + .end(function() {}); } }; return form; - } else if ('textarea' === name) { + } else if ("textarea" === name) { var textarea = {}; // a hack to be able to access the area data when form is sent - textarea.__defineSetter__('value', function (data) { + textarea.__defineSetter__("value", function(data) { self.areaValue = data; }); return textarea; - } else if (~name.indexOf('iframe')) { + } else if (~name.indexOf("iframe")) { var iframe = {}; - setTimeout(function () { + setTimeout(function() { if (iframe.onload) iframe.onload(); }, 0); @@ -72,102 +72,116 @@ describe('JSONP', function () { } }; - document.getElementsByTagName = function (name) { - return [{ - parentNode: { - insertBefore: function () {} + document.getElementsByTagName = function(name) { + return [ + { + parentNode: { + insertBefore: function() {} + } } - }]; + ]; }; }); - after(function () { + after(function() { delete document.getElementsByTagName; delete document.createElement; delete global.document; }); - describe('handshake', function () { - it('should open with polling JSONP when requested', function (done) { - var engine = listen({ allowUpgrades: false, transports: ['polling'] }, function (port) { - eioc('ws://localhost:' + port, - { transports: ['polling'], forceJSONP: true, upgrade: false }); - engine.on('connection', function (socket) { - expect(socket.transport.name).to.be('polling'); - expect(socket.transport.head).to.be('___eio[0]('); - done(); - }); - }); + describe("handshake", function() { + it("should open with polling JSONP when requested", function(done) { + var engine = listen( + { allowUpgrades: false, transports: ["polling"] }, + function(port) { + eioc("ws://localhost:" + port, { + transports: ["polling"], + forceJSONP: true, + upgrade: false + }); + engine.on("connection", function(socket) { + expect(socket.transport.name).to.be("polling"); + expect(socket.transport.head).to.be("___eio[0]("); + done(); + }); + } + ); }); }); - describe('messages', function () { + describe("messages", function() { var engine, port, socket; - beforeEach(function (done) { - engine = listen({ allowUpgrades: false, transports: ['polling'] }, function (p) { - port = p; + beforeEach(function(done) { + engine = listen( + { allowUpgrades: false, transports: ["polling"] }, + function(p) { + port = p; - socket = new eioc.Socket('ws://localhost:' + port - , { transports: ['polling'], forceJSONP: true, upgrade: false }); + socket = new eioc.Socket("ws://localhost:" + port, { + transports: ["polling"], + forceJSONP: true, + upgrade: false + }); - done(); - }); + done(); + } + ); }); - it('should arrive from client to server and back (pollingJSONP)', function (done) { - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - conn.send('a'); + it("should arrive from client to server and back (pollingJSONP)", function(done) { + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + conn.send("a"); }); }); - socket.on('open', function () { - socket.send('a'); - socket.on('message', function (msg) { + socket.on("open", function() { + socket.send("a"); + socket.on("message", function(msg) { expect(socket.transport.query.j).to.not.be(undefined); - expect(msg).to.be('a'); + expect(msg).to.be("a"); done(); }); }); }); - it('should not fail JSON.parse for stringified messages', function (done) { - engine.on('connection', function (conn) { - conn.on('message', function (message) { - expect(JSON.parse(message)).to.be.eql({test: 'a\r\nb\n\n\n\nc'}); + it("should not fail JSON.parse for stringified messages", function(done) { + engine.on("connection", function(conn) { + conn.on("message", function(message) { + expect(JSON.parse(message)).to.be.eql({ test: "a\r\nb\n\n\n\nc" }); done(); }); }); - socket.on('open', function () { - socket.send(JSON.stringify({test: 'a\r\nb\n\n\n\nc'})); + socket.on("open", function() { + socket.send(JSON.stringify({ test: "a\r\nb\n\n\n\nc" })); }); }); - it('should parse newlines in message correctly', function (done) { - engine.on('connection', function (conn) { - conn.on('message', function (message) { - expect(message).to.be.equal('a\r\nb\n\n\n\nc'); + it("should parse newlines in message correctly", function(done) { + engine.on("connection", function(conn) { + conn.on("message", function(message) { + expect(message).to.be.equal("a\r\nb\n\n\n\nc"); done(); }); }); - socket.on('open', function () { - socket.send('a\r\nb\n\n\n\nc'); + socket.on("open", function() { + socket.send("a\r\nb\n\n\n\nc"); }); }); - it('should arrive from server to client and back with binary data (pollingJSONP)', function (done) { + it("should arrive from server to client and back with binary data (pollingJSONP)", function(done) { var binaryData = Buffer.allocUnsafe(5); for (var i = 0; i < 5; i++) binaryData[i] = i; - engine.on('connection', function (conn) { - conn.on('message', function (msg) { + engine.on("connection", function(conn) { + conn.on("message", function(msg) { conn.send(msg); }); }); - socket.on('open', function () { + socket.on("open", function() { socket.send(binaryData); - socket.on('message', function (msg) { + socket.on("message", function(msg) { for (var i = 0; i < msg.length; i++) expect(msg[i]).to.be(i); done(); }); @@ -175,57 +189,69 @@ describe('JSONP', function () { }); }); - describe('close', function () { - it('should trigger when server closes a client', function (done) { - var engine = listen({ allowUpgrades: false, transports: ['polling'] }, function (port) { - var socket = new eioc.Socket('ws://localhost:' + port, - { transports: ['polling'], forceJSONP: true, upgrade: false }); - var total = 2; - - engine.on('connection', function (conn) { - conn.on('close', function (reason) { - expect(reason).to.be('forced close'); - --total || done(); + describe("close", function() { + it("should trigger when server closes a client", function(done) { + var engine = listen( + { allowUpgrades: false, transports: ["polling"] }, + function(port) { + var socket = new eioc.Socket("ws://localhost:" + port, { + transports: ["polling"], + forceJSONP: true, + upgrade: false }); - setTimeout(function () { - conn.close(); - }, 10); - }); + var total = 2; - socket.on('open', function () { - socket.on('close', function (reason) { - expect(reason).to.be('transport close'); - --total || done(); + engine.on("connection", function(conn) { + conn.on("close", function(reason) { + expect(reason).to.be("forced close"); + --total || done(); + }); + setTimeout(function() { + conn.close(); + }, 10); }); - }); - }); - }); - it('should trigger when client closes', function (done) { - var engine = listen({ allowUpgrades: false, transports: ['polling'] }, function (port) { - var socket = new eioc.Socket('ws://localhost:' + port - , { transports: ['polling'], forceJSONP: true, upgrade: false }); - var total = 2; + socket.on("open", function() { + socket.on("close", function(reason) { + expect(reason).to.be("transport close"); + --total || done(); + }); + }); + } + ); + }); - engine.on('connection', function (conn) { - conn.on('close', function (reason) { - expect(reason).to.be('transport close'); - --total || done(); + it("should trigger when client closes", function(done) { + var engine = listen( + { allowUpgrades: false, transports: ["polling"] }, + function(port) { + var socket = new eioc.Socket("ws://localhost:" + port, { + transports: ["polling"], + forceJSONP: true, + upgrade: false }); - }); + var total = 2; - socket.on('open', function () { - socket.send('a'); - socket.on('close', function (reason) { - expect(reason).to.be('forced close'); - --total || done(); + engine.on("connection", function(conn) { + conn.on("close", function(reason) { + expect(reason).to.be("transport close"); + --total || done(); + }); }); - setTimeout(function () { - socket.close(); - }, 10); - }); - }); + socket.on("open", function() { + socket.send("a"); + socket.on("close", function(reason) { + expect(reason).to.be("forced close"); + --total || done(); + }); + + setTimeout(function() { + socket.close(); + }, 10); + }); + } + ); }); }); }); diff --git a/test/server.js b/test/server.js index 9da4d109b..0f4c6352f 100644 --- a/test/server.js +++ b/test/server.js @@ -4,235 +4,288 @@ * Tests dependencies. */ -var http = require('http'); -var https = require('https'); -var fs = require('fs'); -var path = require('path'); -var exec = require('child_process').exec; -var zlib = require('zlib'); -var eio = require('..'); -var eioc = require('engine.io-client'); -var listen = require('./common').listen; -var expect = require('expect.js'); -var request = require('superagent'); -var cookieMod = require('cookie'); +var http = require("http"); +var https = require("https"); +var fs = require("fs"); +var path = require("path"); +var exec = require("child_process").exec; +var zlib = require("zlib"); +var eio = require(".."); +var eioc = require("engine.io-client"); +var listen = require("./common").listen; +var expect = require("expect.js"); +var request = require("superagent"); +var cookieMod = require("cookie"); // are we running on node < 4.4.3 ? -var NODE_LT_443 = (function () { - var parts = process.versions.node.split('.'); - return (parts[0] < 4 || parts[1] < 4 || parts[2] < 3); +var NODE_LT_443 = (function() { + var parts = process.versions.node.split("."); + return parts[0] < 4 || parts[1] < 4 || parts[2] < 3; })(); // are we running uws wsEngine ? -var UWS_ENGINE = process.env.EIO_WS_ENGINE === 'uws'; +var UWS_ENGINE = process.env.EIO_WS_ENGINE === "uws"; /** * Tests. */ -describe('server', function () { - describe('verification', function () { - it('should disallow non-existent transports', function (done) { - listen(function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'tobi' }) // no tobi transport - outrageous - .end(function (err, res) { +describe("server", function() { + describe("verification", function() { + it("should disallow non-existent transports", function(done) { + listen(function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "tobi" }) // no tobi transport - outrageous + .end(function(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'); - expect(res.header['access-control-allow-origin']).to.be('*'); + expect(res.body.message).to.be("Transport unknown"); + expect(res.header["access-control-allow-origin"]).to.be("*"); done(); }); }); }); - it('should disallow `constructor` as transports', function (done) { + it("should disallow `constructor` as transports", function(done) { // make sure we check for actual properties - not those present on every {} - listen(function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .set('Origin', 'http://engine.io') - .query({ transport: 'constructor' }) - .end(function (err, res) { + listen(function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "constructor" }) + .end(function(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'); - expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.body.message).to.be("Transport unknown"); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + expect(res.header["access-control-allow-origin"]).to.be( + "http://engine.io" + ); done(); }); }); }); - it('should disallow non-existent sids', function (done) { - listen(function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .set('Origin', 'http://engine.io') - .query({ transport: 'polling', sid: 'test' }) - .end(function (err, res) { + it("should disallow non-existent sids", function(done) { + listen(function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "polling", sid: "test" }) + .end(function(err, res) { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(1); - expect(res.body.message).to.be('Session ID unknown'); - expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.body.message).to.be("Session ID unknown"); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + expect(res.header["access-control-allow-origin"]).to.be( + "http://engine.io" + ); done(); }); }); }); - it('should disallow requests that are rejected by `allowRequest`', function (done) { - listen({ allowRequest: function (req, fn) { fn('Thou shall not pass', false); } }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .set('Origin', 'http://engine.io') - .query({ transport: 'polling' }) - .end(function (err, res) { - expect(err).to.be.an(Error); - expect(res.status).to.be(403); - expect(res.body.code).to.be(4); - expect(res.body.message).to.be('Thou shall not pass'); - expect(res.header['access-control-allow-credentials']).to.be(undefined); - expect(res.header['access-control-allow-origin']).to.be(undefined); - done(); - }); - }); + it("should disallow requests that are rejected by `allowRequest`", function(done) { + listen( + { + allowRequest: function(req, fn) { + fn("Thou shall not pass", false); + } + }, + function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "polling" }) + .end(function(err, res) { + expect(err).to.be.an(Error); + expect(res.status).to.be(403); + expect(res.body.code).to.be(4); + expect(res.body.message).to.be("Thou shall not pass"); + expect(res.header["access-control-allow-credentials"]).to.be( + undefined + ); + expect(res.header["access-control-allow-origin"]).to.be( + undefined + ); + done(); + }); + } + ); }); - it('should disallow connection that are rejected by `allowRequest`', function (done) { - listen({ allowRequest: function (req, fn) { fn(null, false); } }, function (port) { - var client = eioc('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - client.on('error', function () { - done(); - }); - }); + it("should disallow connection that are rejected by `allowRequest`", function(done) { + listen( + { + allowRequest: function(req, fn) { + fn(null, false); + } + }, + function(port) { + var client = eioc("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + client.on("error", function() { + done(); + }); + } + ); }); }); - describe('handshake', function () { - it('should send the io cookie', function (done) { - listen(function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling', b64: 1 }) - .end(function (err, res) { + describe("handshake", function() { + it("should send the io cookie", function(done) { + listen(function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling", b64: 1 }) + .end(function(err, res) { expect(err).to.be(null); // hack-obtain sid var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers['set-cookie'][0]).to.be('io=' + sid + '; Path=/; HttpOnly'); + expect(res.headers["set-cookie"][0]).to.be( + "io=" + sid + "; Path=/; HttpOnly" + ); done(); }); }); }); - it('should send the io cookie custom name', function (done) { - listen({ cookie: 'woot' }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling', b64: 1 }) - .end(function (err, res) { + it("should send the io cookie custom name", function(done) { + listen({ cookie: "woot" }, function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling", b64: 1 }) + .end(function(err, res) { expect(err).to.be(null); var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers['set-cookie'][0]).to.be('woot=' + sid + '; Path=/; HttpOnly'); + expect(res.headers["set-cookie"][0]).to.be( + "woot=" + sid + "; Path=/; HttpOnly" + ); done(); }); }); }); - it('should send the cookie with custom path', function (done) { - listen({ cookiePath: '/custom' }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling', b64: 1 }) - .end(function (err, res) { + it("should send the cookie with custom path", function(done) { + listen({ cookiePath: "/custom" }, function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling", b64: 1 }) + .end(function(err, res) { expect(err).to.be(null); var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers['set-cookie'][0]).to.be('io=' + sid + '; Path=/custom; HttpOnly'); + expect(res.headers["set-cookie"][0]).to.be( + "io=" + sid + "; Path=/custom; HttpOnly" + ); done(); }); }); }); - it('should send the cookie with path=false', function (done) { - listen({ cookiePath: false }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling', b64: 1 }) - .end(function (err, res) { + it("should send the cookie with path=false", function(done) { + listen({ cookiePath: false }, function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling", b64: 1 }) + .end(function(err, res) { expect(err).to.be(null); var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers['set-cookie'][0]).to.be('io=' + sid); + expect(res.headers["set-cookie"][0]).to.be("io=" + sid); done(); }); }); }); - it('should send the io cookie with httpOnly=true', function (done) { - listen({ cookieHttpOnly: true }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling', b64: 1 }) - .end(function (err, res) { + it("should send the io cookie with httpOnly=true", function(done) { + listen({ cookieHttpOnly: true }, function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling", b64: 1 }) + .end(function(err, res) { expect(err).to.be(null); var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers['set-cookie'][0]).to.be('io=' + sid + '; Path=/; HttpOnly'); + expect(res.headers["set-cookie"][0]).to.be( + "io=" + sid + "; Path=/; HttpOnly" + ); done(); }); }); }); - it('should send the io cookie with httpOnly=true and path=false', function (done) { - listen({ cookieHttpOnly: true, cookiePath: false }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling', b64: 1 }) - .end(function (err, res) { + it("should send the io cookie with httpOnly=true and path=false", function(done) { + listen({ cookieHttpOnly: true, cookiePath: false }, function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling", b64: 1 }) + .end(function(err, res) { expect(err).to.be(null); var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers['set-cookie'][0]).to.be('io=' + sid); + expect(res.headers["set-cookie"][0]).to.be("io=" + sid); done(); }); }); }); - it('should send the io cookie with httpOnly=false', function (done) { - listen({ cookieHttpOnly: false }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling', b64: 1 }) - .end(function (err, res) { + it("should send the io cookie with httpOnly=false", function(done) { + listen({ cookieHttpOnly: false }, function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling", b64: 1 }) + .end(function(err, res) { expect(err).to.be(null); var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers['set-cookie'][0]).to.be('io=' + sid + '; Path=/'); + expect(res.headers["set-cookie"][0]).to.be( + "io=" + sid + "; Path=/" + ); done(); }); }); }); - it('should send the io cookie with httpOnly not boolean', function (done) { - listen({ cookieHttpOnly: 'no' }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling', b64: 1 }) - .end(function (err, res) { + it("should send the io cookie with httpOnly not boolean", function(done) { + listen({ cookieHttpOnly: "no" }, function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling", b64: 1 }) + .end(function(err, res) { expect(err).to.be(null); var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers['set-cookie'][0]).to.be('io=' + sid + '; Path=/; HttpOnly'); + expect(res.headers["set-cookie"][0]).to.be( + "io=" + sid + "; Path=/; HttpOnly" + ); done(); }); }); }); - it('should not send the io cookie', function (done) { - listen({ cookie: false }, function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .query({ transport: 'polling' }) - .end(function (err, res) { + it("should not send the io cookie", function(done) { + listen({ cookie: false }, function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .query({ transport: "polling" }) + .end(function(err, res) { expect(err).to.be(null); - expect(res.headers['set-cookie']).to.be(undefined); + expect(res.headers["set-cookie"]).to.be(undefined); done(); }); }); }); - it('should register a new client', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { + it("should register a new client", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { expect(Object.keys(engine.clients)).to.have.length(0); expect(engine.clientsCount).to.be(0); - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { expect(Object.keys(engine.clients)).to.have.length(1); expect(engine.clientsCount).to.be(1); done(); @@ -240,19 +293,19 @@ describe('server', function () { }); }); - it('should register a new client with custom id', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { + it("should register a new client with custom id", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { expect(Object.keys(engine.clients)).to.have.length(0); expect(engine.clientsCount).to.be(0); - var customId = 'CustomId' + Date.now(); + var customId = "CustomId" + Date.now(); - engine.generateId = function (req) { + engine.generateId = function(req) { return customId; }; - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.once('open', function () { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.once("open", function() { expect(Object.keys(engine.clients)).to.have.length(1); expect(engine.clientsCount).to.be(1); expect(socket.id).to.be(customId); @@ -262,119 +315,122 @@ describe('server', function () { }); }); - it('should exchange handshake data', function (done) { - listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('handshake', function (obj) { - expect(obj.sid).to.be.a('string'); - expect(obj.pingTimeout).to.be.a('number'); - expect(obj.upgrades).to.be.an('array'); + it("should exchange handshake data", function(done) { + listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("handshake", function(obj) { + expect(obj.sid).to.be.a("string"); + expect(obj.pingTimeout).to.be.a("number"); + expect(obj.upgrades).to.be.an("array"); done(); }); }); }); - it('should allow custom ping timeouts', function (done) { - listen({ allowUpgrades: false, pingTimeout: 123 }, function (port) { - var socket = new eioc.Socket('http://localhost:%d'.s(port)); - socket.on('handshake', function (obj) { + it("should allow custom ping timeouts", function(done) { + listen({ allowUpgrades: false, pingTimeout: 123 }, function(port) { + var socket = new eioc.Socket("http://localhost:%d".s(port)); + socket.on("handshake", function(obj) { expect(obj.pingTimeout).to.be(123); done(); }); }); }); - it('should trigger a connection event with a Socket', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - eioc('ws://localhost:%d'.s(port)); - engine.on('connection', function (socket) { + it("should trigger a connection event with a Socket", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + eioc("ws://localhost:%d".s(port)); + engine.on("connection", function(socket) { expect(socket).to.be.an(eio.Socket); done(); }); }); }); - it('should open with polling by default', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - eioc('ws://localhost:%d'.s(port)); - engine.on('connection', function (socket) { - expect(socket.transport.name).to.be('polling'); + it("should open with polling by default", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + eioc("ws://localhost:%d".s(port)); + engine.on("connection", function(socket) { + expect(socket.transport.name).to.be("polling"); done(); }); }); }); - it('should be able to open with ws directly', function (done) { - var engine = listen({ transports: ['websocket'] }, function (port) { - eioc('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - engine.on('connection', function (socket) { - expect(socket.transport.name).to.be('websocket'); + it("should be able to open with ws directly", function(done) { + var engine = listen({ transports: ["websocket"] }, function(port) { + eioc("ws://localhost:%d".s(port), { transports: ["websocket"] }); + engine.on("connection", function(socket) { + expect(socket.transport.name).to.be("websocket"); done(); }); }); }); - it('should not suggest any upgrades for websocket', function (done) { - listen({ transports: ['websocket'] }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - socket.on('handshake', function (obj) { + it("should not suggest any upgrades for websocket", function(done) { + listen({ transports: ["websocket"] }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + socket.on("handshake", function(obj) { expect(obj.upgrades).to.have.length(0); done(); }); }); }); - it('should not suggest upgrades when none are availble', function (done) { - listen({ transports: ['polling'] }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { }); - socket.on('handshake', function (obj) { + it("should not suggest upgrades when none are availble", function(done) { + listen({ transports: ["polling"] }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + socket.on("handshake", function(obj) { expect(obj.upgrades).to.have.length(0); done(); }); }); }); - it('should only suggest available upgrades', function (done) { - listen({ transports: ['polling'] }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { }); - socket.on('handshake', function (obj) { + it("should only suggest available upgrades", function(done) { + listen({ transports: ["polling"] }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + socket.on("handshake", function(obj) { expect(obj.upgrades).to.have.length(0); done(); }); }); }); - it('should suggest all upgrades when no transports are disabled', function (done) { - listen({}, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { }); - socket.on('handshake', function (obj) { + it("should suggest all upgrades when no transports are disabled", function(done) { + listen({}, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + socket.on("handshake", function(obj) { expect(obj.upgrades).to.have.length(1); - expect(obj.upgrades).to.have.contain('websocket'); + expect(obj.upgrades).to.have.contain("websocket"); done(); }); }); }); - it('default to polling when proxy doesn\'t support websocket', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - engine.on('connection', function (socket) { - socket.on('message', function (msg) { - if ('echo' === msg) socket.send(msg); + it("default to polling when proxy doesn't support websocket", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + engine.on("connection", function(socket) { + socket.on("message", function(msg) { + if ("echo" === msg) socket.send(msg); }); }); - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { - request.get('http://localhost:%d/engine.io/'.s(port)) - .set({ connection: 'close' }) - .query({ transport: 'websocket', sid: socket.id }) - .end(function (err, res) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { + request + .get("http://localhost:%d/engine.io/".s(port)) + .set({ connection: "close" }) + .query({ transport: "websocket", sid: socket.id }) + .end(function(err, res) { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(3); - socket.send('echo'); - socket.on('message', function (msg) { - expect(msg).to.be('echo'); + socket.send("echo"); + socket.on("message", function(msg) { + expect(msg).to.be("echo"); done(); }); }); @@ -382,52 +438,57 @@ describe('server', function () { }); }); - it('should allow arbitrary data through query string', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - eioc('ws://localhost:%d'.s(port), { query: { a: 'b' } }); - engine.on('connection', function (conn) { - expect(conn.request._query).to.have.keys('transport', 'a'); - expect(conn.request._query.a).to.be('b'); + it("should allow arbitrary data through query string", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + eioc("ws://localhost:%d".s(port), { query: { a: "b" } }); + engine.on("connection", function(conn) { + expect(conn.request._query).to.have.keys("transport", "a"); + expect(conn.request._query.a).to.be("b"); done(); }); }); }); - it('should allow data through query string in uri', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - eioc('ws://localhost:%d?a=b&c=d'.s(port)); - engine.on('connection', function (conn) { - expect(conn.request._query.EIO).to.be.a('string'); - expect(conn.request._query.a).to.be('b'); - expect(conn.request._query.c).to.be('d'); + it("should allow data through query string in uri", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + eioc("ws://localhost:%d?a=b&c=d".s(port)); + engine.on("connection", function(conn) { + expect(conn.request._query.EIO).to.be.a("string"); + expect(conn.request._query.a).to.be("b"); + expect(conn.request._query.c).to.be("d"); done(); }); }); }); - it('should disallow bad requests', function (done) { - listen(function (port) { - request.get('http://localhost:%d/engine.io/default/'.s(port)) - .set('Origin', 'http://engine.io') - .query({ transport: 'websocket' }) - .end(function (err, res) { + it("should disallow bad requests", function(done) { + listen(function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "websocket" }) + .end(function(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'); - expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.body.message).to.be("Bad request"); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + expect(res.header["access-control-allow-origin"]).to.be( + "http://engine.io" + ); done(); }); }); }); - it('should send a packet along with the handshake', function (done) { - listen({ initialPacket: 'faster!' }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { - socket.on('message', function (msg) { - expect(msg).to.be('faster!'); + it("should send a packet along with the handshake", function(done) { + listen({ initialPacket: "faster!" }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { + socket.on("message", function(msg) { + expect(msg).to.be("faster!"); done(); }); }); @@ -435,68 +496,68 @@ describe('server', function () { }); }); - describe('close', function () { - it('should be able to access non-empty writeBuffer at closing (server)', function (done) { - var opts = {allowUpgrades: false}; - var engine = listen(opts, function (port) { - eioc('http://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.on('close', function (reason) { + describe("close", function() { + it("should be able to access non-empty writeBuffer at closing (server)", function(done) { + var opts = { allowUpgrades: false }; + var engine = listen(opts, function(port) { + eioc("http://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.on("close", function(reason) { expect(conn.writeBuffer.length).to.be(1); - setTimeout(function () { + setTimeout(function() { expect(conn.writeBuffer.length).to.be(0); // writeBuffer has been cleared }, 10); done(); }); - conn.writeBuffer.push({ type: 'message', data: 'foo' }); - conn.onError(''); + conn.writeBuffer.push({ type: "message", data: "foo" }); + conn.onError(""); }); }); }); - it('should be able to access non-empty writeBuffer at closing (client)', function (done) { - var opts = {allowUpgrades: false}; - listen(opts, function (port) { - var socket = new eioc.Socket('http://localhost:%d'.s(port)); - socket.on('open', function () { - socket.on('close', function (reason) { + it("should be able to access non-empty writeBuffer at closing (client)", function(done) { + var opts = { allowUpgrades: false }; + listen(opts, function(port) { + var socket = new eioc.Socket("http://localhost:%d".s(port)); + socket.on("open", function() { + socket.on("close", function(reason) { expect(socket.writeBuffer.length).to.be(1); - setTimeout(function () { + setTimeout(function() { expect(socket.writeBuffer.length).to.be(0); }, 10); done(); }); - socket.writeBuffer.push({ type: 'message', data: 'foo' }); - socket.onError(''); + socket.writeBuffer.push({ type: "message", data: "foo" }); + socket.onError(""); }); }); }); - it('should trigger on server if the client does not pong', function (done) { + it("should trigger on server if the client does not pong", function(done) { var opts = { allowUpgrades: false, pingInterval: 5, pingTimeout: 5 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('http://localhost:%d'.s(port)); - socket.sendPacket = function () {}; - engine.on('connection', function (conn) { - conn.on('close', function (reason) { - expect(reason).to.be('ping timeout'); + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("http://localhost:%d".s(port)); + socket.sendPacket = function() {}; + engine.on("connection", function(conn) { + conn.on("close", function(reason) { + expect(reason).to.be("ping timeout"); done(); }); }); }); }); - it('should trigger on server even when there is no outstanding polling request (GH-198)', function (done) { + it("should trigger on server even when there is no outstanding polling request (GH-198)", function(done) { var opts = { allowUpgrades: false, pingInterval: 500, pingTimeout: 500 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('http://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.on('close', function (reason) { - expect(reason).to.be('ping timeout'); + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("http://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.on("close", function(reason) { + expect(reason).to.be("ping timeout"); done(); }); // client abruptly disconnects, no polling request on this tick since we've just connected - socket.sendPacket = socket.onPacket = function () {}; + socket.sendPacket = socket.onPacket = function() {}; socket.close(); // then server app tries to close the socket, since client disappeared conn.close(); @@ -504,441 +565,474 @@ describe('server', function () { }); }); - it('should trigger on client if server does not meet ping timeout', function (done) { + it("should trigger on client if server does not meet ping timeout", function(done) { var opts = { allowUpgrades: false, pingInterval: 50, pingTimeout: 30 }; - listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { + listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { // override onPacket and Transport#onClose to simulate an inactive server after handshake - socket.onPacket = function () {}; - socket.transport.onClose = function () {}; - socket.on('close', function (reason, err) { - expect(reason).to.be('ping timeout'); + socket.onPacket = function() {}; + socket.transport.onClose = function() {}; + socket.on("close", function(reason, err) { + expect(reason).to.be("ping timeout"); done(); }); }); }); }); - it('should trigger on both ends upon ping timeout', function (done) { + it("should trigger on both ends upon ping timeout", function(done) { var opts = { allowUpgrades: false, pingTimeout: 50, pingInterval: 50 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); var total = 2; - function onClose (reason, err) { - expect(reason).to.be('ping timeout'); + function onClose(reason, err) { + expect(reason).to.be("ping timeout"); --total || done(); } - engine.on('connection', function (conn) { - conn.on('close', onClose); + engine.on("connection", function(conn) { + conn.on("close", onClose); }); - socket.on('open', function () { + socket.on("open", function() { // override onPacket and Transport#onClose to simulate an inactive server after handshake - socket.onPacket = socket.sendPacket = function () {}; - socket.transport.onClose = function () {}; - socket.on('close', onClose); + socket.onPacket = socket.sendPacket = function() {}; + socket.transport.onClose = function() {}; + socket.on("close", onClose); }); }); }); - it('should trigger when server closes a client', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); + it("should trigger when server closes a client", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); var total = 2; - engine.on('connection', function (conn) { - conn.on('close', function (reason) { - expect(reason).to.be('forced close'); + engine.on("connection", function(conn) { + conn.on("close", function(reason) { + expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function () { + setTimeout(function() { conn.close(); }, 10); }); - socket.on('open', function () { - socket.on('close', function (reason) { - expect(reason).to.be('transport close'); + socket.on("open", function() { + socket.on("close", function(reason) { + expect(reason).to.be("transport close"); --total || done(); }); }); }); }); - it('should trigger when server closes a client (ws)', function (done) { - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + it("should trigger when server closes a client (ws)", function(done) { + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); var total = 2; - engine.on('connection', function (conn) { - conn.on('close', function (reason) { - expect(reason).to.be('forced close'); + engine.on("connection", function(conn) { + conn.on("close", function(reason) { + expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function () { + setTimeout(function() { conn.close(); }, 10); }); - socket.on('open', function () { - socket.on('close', function (reason) { - expect(reason).to.be('transport close'); + socket.on("open", function() { + socket.on("close", function(reason) { + expect(reason).to.be("transport close"); --total || done(); }); }); }); }); - it('should allow client reconnect after restarting (ws)', function (done) { - var opts = { transports: ['websocket'] }; - var engine = listen(opts, function (port) { + it("should allow client reconnect after restarting (ws)", function(done) { + var opts = { transports: ["websocket"] }; + var engine = listen(opts, function(port) { engine.httpServer.close(); engine.httpServer.listen(port); - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); - engine.once('connection', function (conn) { - setTimeout(function () { + engine.once("connection", function(conn) { + setTimeout(function() { conn.close(); }, 10); }); - socket.once('close', function (reason) { - expect(reason).to.be('transport close'); + socket.once("close", function(reason) { + expect(reason).to.be("transport close"); done(); }); }); }); - it('should trigger when client closes', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); + it("should trigger when client closes", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); var total = 2; - engine.on('connection', function (conn) { - conn.on('close', function (reason) { - expect(reason).to.be('transport close'); + engine.on("connection", function(conn) { + conn.on("close", function(reason) { + expect(reason).to.be("transport close"); --total || done(); }); }); - socket.on('open', function () { - socket.on('close', function (reason) { - expect(reason).to.be('forced close'); + socket.on("open", function() { + socket.on("close", function(reason) { + expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function () { + setTimeout(function() { socket.close(); }, 10); }); }); }); - it('should trigger when client closes (ws)', function (done) { - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + it("should trigger when client closes (ws)", function(done) { + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); var total = 2; - engine.on('connection', function (conn) { - conn.on('close', function (reason) { - expect(reason).to.be('transport close'); + engine.on("connection", function(conn) { + conn.on("close", function(reason) { + expect(reason).to.be("transport close"); --total || done(); }); }); - socket.on('open', function () { - socket.on('close', function (reason) { - expect(reason).to.be('forced close'); + socket.on("open", function() { + socket.on("close", function(reason) { + expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function () { + setTimeout(function() { socket.close(); }, 10); }); }); }); - it('should trigger when calling socket.close() in payload', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); + it("should trigger when calling socket.close() in payload", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); - engine.on('connection', function (conn) { - conn.send(null, function () { socket.close(); }); - conn.send('this should not be handled'); + engine.on("connection", function(conn) { + conn.send(null, function() { + socket.close(); + }); + conn.send("this should not be handled"); - conn.on('close', function (reason) { - expect(reason).to.be('transport close'); + conn.on("close", function(reason) { + expect(reason).to.be("transport close"); done(); }); }); - socket.on('open', function () { - socket.on('message', function (msg) { - expect(msg).to.not.be('this should not be handled'); + socket.on("open", function() { + socket.on("message", function(msg) { + expect(msg).to.not.be("this should not be handled"); }); - socket.on('close', function (reason) { - expect(reason).to.be('forced close'); + socket.on("close", function(reason) { + expect(reason).to.be("forced close"); }); }); }); }); - it('should abort upgrade if socket is closed (GH-35)', function (done) { - listen({ allowUpgrades: true }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { + it("should abort upgrade if socket is closed (GH-35)", function(done) { + listen({ allowUpgrades: true }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { socket.close(); // we wait until complete to see if we get an uncaught EPIPE - setTimeout(function () { + setTimeout(function() { done(); }, 100); }); }); }); - it('should trigger if a poll request is ongoing and the underlying ' + - 'socket closes, as in a browser tab close', function ($done) { - var engine = listen({ allowUpgrades: false }, function (port) { - // hack to access the sockets created by node-xmlhttprequest - // see: https://github.com/driverdan/node-XMLHttpRequest/issues/44 - var request = require('http').request; - var sockets = []; - http.request = function (opts) { - var req = request.apply(null, arguments); - req.on('socket', function (socket) { - sockets.push(socket); - }); - return req; - }; + it( + "should trigger if a poll request is ongoing and the underlying " + + "socket closes, as in a browser tab close", + function($done) { + var engine = listen({ allowUpgrades: false }, function(port) { + // hack to access the sockets created by node-xmlhttprequest + // see: https://github.com/driverdan/node-XMLHttpRequest/issues/44 + var request = require("http").request; + var sockets = []; + http.request = function(opts) { + var req = request.apply(null, arguments); + req.on("socket", function(socket) { + sockets.push(socket); + }); + return req; + }; - function done () { - http.request = request; - $done(); - } + function done() { + http.request = request; + $done(); + } - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - var serverSocket; + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var serverSocket; - engine.on('connection', function (s) { - serverSocket = s; - }); + engine.on("connection", function(s) { + serverSocket = s; + }); - socket.transport.on('poll', function () { - // we set a timer to wait for the request to actually reach - setTimeout(function () { - // at this time server's `connection` should have been fired - expect(serverSocket).to.be.an('object'); + socket.transport.on("poll", function() { + // we set a timer to wait for the request to actually reach + setTimeout(function() { + // at this time server's `connection` should have been fired + expect(serverSocket).to.be.an("object"); - // OPENED readyState is expected - we are actually polling - expect(socket.transport.pollXhr.xhr.readyState).to.be(1); + // OPENED readyState is expected - we are actually polling + expect(socket.transport.pollXhr.xhr.readyState).to.be(1); - // 2 requests sent to the server over an unique port means - // we should have been assigned 2 sockets - expect(sockets.length).to.be(2); + // 2 requests sent to the server over an unique port means + // we should have been assigned 2 sockets + expect(sockets.length).to.be(2); - // expect the socket to be open at this point - expect(serverSocket.readyState).to.be('open'); + // expect the socket to be open at this point + expect(serverSocket.readyState).to.be("open"); - // kill the underlying connection - sockets[1].end(); - serverSocket.on('close', function (reason, err) { - expect(reason).to.be('transport error'); - expect(err.message).to.be('poll connection closed prematurely'); - done(); - }); - }, 50); + // kill the underlying connection + sockets[1].end(); + serverSocket.on("close", function(reason, err) { + expect(reason).to.be("transport error"); + expect(err.message).to.be("poll connection closed prematurely"); + done(); + }); + }, 50); + }); }); - }); - }); + } + ); - it('should not trigger with connection: close header', function ($done) { - var engine = listen({ allowUpgrades: false }, function (port) { + it("should not trigger with connection: close header", function($done) { + var engine = listen({ allowUpgrades: false }, function(port) { // intercept requests to add connection: close var request = http.request; - http.request = function () { + http.request = function() { var opts = arguments[0]; opts.headers = opts.headers || {}; - opts.headers.Connection = 'close'; + opts.headers.Connection = "close"; return request.apply(this, arguments); }; - function done () { + function done() { http.request = request; $done(); } - engine.on('connection', function (socket) { - socket.on('message', function (msg) { - expect(msg).to.equal('test'); - socket.send('woot'); + engine.on("connection", function(socket) { + socket.on("message", function(msg) { + expect(msg).to.equal("test"); + socket.send("woot"); }); }); - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { - socket.send('test'); + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { + socket.send("test"); }); - socket.on('message', function (msg) { - expect(msg).to.be('woot'); + socket.on("message", function(msg) { + expect(msg).to.be("woot"); done(); }); }); }); - it('should not trigger early with connection `ping timeout`' + - 'after post handshake timeout', function (done) { - // first timeout should trigger after `pingInterval + pingTimeout`, - // not just `pingTimeout`. - var opts = { allowUpgrades: false, pingInterval: 300, pingTimeout: 100 }; - listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - var clientCloseReason = null; + it( + "should not trigger early with connection `ping timeout`" + + "after post handshake timeout", + function(done) { + // first timeout should trigger after `pingInterval + pingTimeout`, + // not just `pingTimeout`. + var opts = { + allowUpgrades: false, + pingInterval: 300, + pingTimeout: 100 + }; + listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var clientCloseReason = null; - socket.on('handshake', function () { - socket.onPacket = function () {}; - }); - socket.on('open', function () { - socket.on('close', function (reason) { - clientCloseReason = reason; + socket.on("handshake", function() { + socket.onPacket = function() {}; }); - }); - - setTimeout(function () { - expect(clientCloseReason).to.be(null); - done(); - }, 200); - }); - }); - - it('should not trigger early with connection `ping timeout` ' + - 'after post ping timeout', function (done) { - // ping timeout should trigger after `pingInterval + pingTimeout`, - // not just `pingTimeout`. - var opts = { allowUpgrades: false, pingInterval: 80, pingTimeout: 50 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - var clientCloseReason = null; - - engine.on('connection', function (conn) { - conn.on('heartbeat', function () { - conn.onPacket = function () {}; + socket.on("open", function() { + socket.on("close", function(reason) { + clientCloseReason = reason; + }); }); - }); - socket.on('open', function () { - socket.on('close', function (reason) { - clientCloseReason = reason; - }); + setTimeout(function() { + expect(clientCloseReason).to.be(null); + done(); + }, 200); }); - - setTimeout(function () { - expect(clientCloseReason).to.be(null); - done(); - }, 100); - }); - }); - - it('should trigger early with connection `transport close` ' + - 'after missing pong', function (done) { - // ping timeout should trigger after `pingInterval + pingTimeout`, - // not just `pingTimeout`. - var opts = { allowUpgrades: false, pingInterval: 80, pingTimeout: 50 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - var clientCloseReason = null; - - socket.on('open', function () { - socket.on('close', function (reason) { - clientCloseReason = reason; + } + ); + + it( + "should not trigger early with connection `ping timeout` " + + "after post ping timeout", + function(done) { + // ping timeout should trigger after `pingInterval + pingTimeout`, + // not just `pingTimeout`. + var opts = { allowUpgrades: false, pingInterval: 80, pingTimeout: 50 }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var clientCloseReason = null; + + engine.on("connection", function(conn) { + conn.on("heartbeat", function() { + conn.onPacket = function() {}; + }); }); - }); - engine.on('connection', function (conn) { - conn.on('heartbeat', function () { - setTimeout(function () { - conn.close(); - }, 20); - setTimeout(function () { - expect(clientCloseReason).to.be('transport close'); - done(); - }, 100); + socket.on("open", function() { + socket.on("close", function(reason) { + clientCloseReason = reason; + }); }); - }); - }); - }); - it('should trigger with connection `ping timeout` ' + - 'after `pingInterval + pingTimeout`', function (done) { - var opts = { allowUpgrades: false, pingInterval: 300, pingTimeout: 100 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - var clientCloseReason = null; - - socket.on('open', function () { - socket.on('close', function (reason) { - clientCloseReason = reason; - }); + setTimeout(function() { + expect(clientCloseReason).to.be(null); + done(); + }, 100); }); + } + ); + + it( + "should trigger early with connection `transport close` " + + "after missing pong", + function(done) { + // ping timeout should trigger after `pingInterval + pingTimeout`, + // not just `pingTimeout`. + var opts = { allowUpgrades: false, pingInterval: 80, pingTimeout: 50 }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var clientCloseReason = null; + + socket.on("open", function() { + socket.on("close", function(reason) { + clientCloseReason = reason; + }); + }); - engine.on('connection', function (conn) { - conn.once('heartbeat', function () { - setTimeout(function () { - socket.onPacket = function () {}; - expect(clientCloseReason).to.be(null); - }, 150); - setTimeout(function () { - expect(clientCloseReason).to.be(null); - }, 350); - setTimeout(function () { - expect(clientCloseReason).to.be('ping timeout'); - done(); - }, 500); + engine.on("connection", function(conn) { + conn.on("heartbeat", function() { + setTimeout(function() { + conn.close(); + }, 20); + setTimeout(function() { + expect(clientCloseReason).to.be("transport close"); + done(); + }, 100); + }); }); }); - }); - }); + } + ); + + it( + "should trigger with connection `ping timeout` " + + "after `pingInterval + pingTimeout`", + function(done) { + var opts = { + allowUpgrades: false, + pingInterval: 300, + pingTimeout: 100 + }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var clientCloseReason = null; - it('should abort the polling data request if it is ' + - 'in progress', function (done) { - var engine = listen({ transports: [ 'polling' ] }, function (port) { - var socket = new eioc.Socket('http://localhost:%d'.s(port)); + socket.on("open", function() { + socket.on("close", function(reason) { + clientCloseReason = reason; + }); + }); - engine.on('connection', function (conn) { - var onDataRequest = conn.transport.onDataRequest; - conn.transport.onDataRequest = function (req, res) { - engine.httpServer.close(done); - onDataRequest.call(conn.transport, req, res); - req.removeAllListeners(); - conn.close(); - }; + engine.on("connection", function(conn) { + conn.once("heartbeat", function() { + setTimeout(function() { + socket.onPacket = function() {}; + expect(clientCloseReason).to.be(null); + }, 150); + setTimeout(function() { + expect(clientCloseReason).to.be(null); + }, 350); + setTimeout(function() { + expect(clientCloseReason).to.be("ping timeout"); + done(); + }, 500); + }); + }); }); + } + ); + + it( + "should abort the polling data request if it is " + "in progress", + function(done) { + var engine = listen({ transports: ["polling"] }, function(port) { + var socket = new eioc.Socket("http://localhost:%d".s(port)); + + engine.on("connection", function(conn) { + var onDataRequest = conn.transport.onDataRequest; + conn.transport.onDataRequest = function(req, res) { + engine.httpServer.close(done); + onDataRequest.call(conn.transport, req, res); + req.removeAllListeners(); + conn.close(); + }; + }); - socket.on('open', function () { - socket.send('test'); + socket.on("open", function() { + socket.send("test"); + }); }); - }); - }); + } + ); // tests https://github.com/LearnBoost/engine.io-client/issues/207 // websocket test, transport error - it('should trigger transport close before open for ws', function (done) { - var opts = { transports: ['websocket'] }; - listen(opts, function (port) { - var url = 'ws://%s:%d'.s('0.0.0.50', port); + it("should trigger transport close before open for ws", function(done) { + var opts = { transports: ["websocket"] }; + listen(opts, function(port) { + var url = "ws://%s:%d".s("0.0.0.50", port); var socket = new eioc.Socket(url); - socket.on('open', function () { - done(new Error('Test invalidation')); + socket.on("open", function() { + done(new Error("Test invalidation")); }); - socket.on('close', function (reason) { - expect(reason).to.be('transport error'); + socket.on("close", function(reason) { + expect(reason).to.be("transport error"); done(); }); }); @@ -946,15 +1040,15 @@ describe('server', function () { // tests https://github.com/LearnBoost/engine.io-client/issues/207 // polling test, transport error - it('should trigger transport close before open for xhr', function (done) { - var opts = { transports: ['polling'] }; - listen(opts, function (port) { - var socket = new eioc.Socket('http://invalidserver:%d'.s(port)); - socket.on('open', function () { - done(new Error('Test invalidation')); - }); - socket.on('close', function (reason) { - expect(reason).to.be('transport error'); + it("should trigger transport close before open for xhr", function(done) { + var opts = { transports: ["polling"] }; + listen(opts, function(port) { + var socket = new eioc.Socket("http://invalidserver:%d".s(port)); + socket.on("open", function() { + done(new Error("Test invalidation")); + }); + socket.on("close", function(reason) { + expect(reason).to.be("transport error"); done(); }); }); @@ -962,15 +1056,15 @@ describe('server', function () { // tests https://github.com/LearnBoost/engine.io-client/issues/207 // websocket test, force close - it('should trigger force close before open for ws', function (done) { - var opts = { transports: ['websocket'] }; - listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { - done(new Error('Test invalidation')); - }); - socket.on('close', function (reason) { - expect(reason).to.be('forced close'); + it("should trigger force close before open for ws", function(done) { + var opts = { transports: ["websocket"] }; + listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { + done(new Error("Test invalidation")); + }); + socket.on("close", function(reason) { + expect(reason).to.be("forced close"); done(); }); socket.close(); @@ -979,332 +1073,373 @@ describe('server', function () { // tests https://github.com/LearnBoost/engine.io-client/issues/207 // polling test, force close - it('should trigger force close before open for xhr', function (done) { - var opts = { transports: ['polling'] }; - listen(opts, function (port) { - var socket = new eioc.Socket('http://localhost:%d'.s(port)); - socket.on('open', function () { - done(new Error('Test invalidation')); - }); - socket.on('close', function (reason) { - expect(reason).to.be('forced close'); + it("should trigger force close before open for xhr", function(done) { + var opts = { transports: ["polling"] }; + listen(opts, function(port) { + var socket = new eioc.Socket("http://localhost:%d".s(port)); + socket.on("open", function() { + done(new Error("Test invalidation")); + }); + socket.on("close", function(reason) { + expect(reason).to.be("forced close"); done(); }); socket.close(); }); }); - it('should close transport upon ping timeout (ws)', function (done) { - var opts = { allowUpgrades: false, transports: ['websocket'], pingInterval: 50, pingTimeout: 30 }; - var engine = listen(opts, function (port) { - engine.on('connection', function (conn) { - conn.transport.on('close', done); + it("should close transport upon ping timeout (ws)", function(done) { + var opts = { + allowUpgrades: false, + transports: ["websocket"], + pingInterval: 50, + pingTimeout: 30 + }; + var engine = listen(opts, function(port) { + engine.on("connection", function(conn) { + conn.transport.on("close", done); + }); + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] }); - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); // override to simulate an inactive client - socket.sendPacket = socket.onHeartbeat = function () {}; + socket.sendPacket = socket.onHeartbeat = function() {}; }); }); - it('should close transport upon ping timeout (polling)', function (done) { - var opts = { allowUpgrades: false, transports: ['polling'], pingInterval: 50, pingTimeout: 30 }; - var engine = listen(opts, function (port) { - engine.on('connection', function (conn) { - conn.transport.on('close', done); + it("should close transport upon ping timeout (polling)", function(done) { + var opts = { + allowUpgrades: false, + transports: ["polling"], + pingInterval: 50, + pingTimeout: 30 + }; + var engine = listen(opts, function(port) { + engine.on("connection", function(conn) { + conn.transport.on("close", done); + }); + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] }); - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); // override to simulate an inactive client - socket.sendPacket = socket.onHeartbeat = function () {}; + socket.sendPacket = socket.onHeartbeat = function() {}; }); }); - it('should close transport upon parse error (ws)', function (done) { - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - engine.on('connection', function (conn) { - conn.transport.on('close', done); + it("should close transport upon parse error (ws)", function(done) { + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + engine.on("connection", function(conn) { + conn.transport.on("close", done); + }); + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] }); - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - socket.on('open', function () { - socket.transport.ws.send('invalid'); + socket.on("open", function() { + socket.transport.ws.send("invalid"); }); }); }); - it('should close transport upon parse error (polling)', function (done) { - var opts = { allowUpgrades: false, transports: ['polling'] }; - var engine = listen(opts, function (port) { - engine.on('connection', function (conn) { + it("should close transport upon parse error (polling)", function(done) { + var opts = { allowUpgrades: false, transports: ["polling"] }; + var engine = listen(opts, function(port) { + engine.on("connection", function(conn) { conn.transport.closeTimeout = 100; - conn.transport.on('close', done); + conn.transport.on("close", done); + }); + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] }); - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); - socket.on('open', function () { - socket.transport.doWrite('invalid', function () {}); + socket.on("open", function() { + socket.transport.doWrite("invalid", function() {}); }); }); }); - it('should close upgrading transport upon socket close', function (done) { - var engine = listen(function (port) { - engine.on('connection', function (conn) { - conn.on('upgrading', function (transport) { - transport.on('close', done); + it("should close upgrading transport upon socket close", function(done) { + var engine = listen(function(port) { + engine.on("connection", function(conn) { + conn.on("upgrading", function(transport) { + transport.on("close", done); conn.close(); }); }); - eioc('ws://localhost:%d'.s(port)); + eioc("ws://localhost:%d".s(port)); }); }); - it('should close upgrading transport upon upgrade timeout', function (done) { + it("should close upgrading transport upon upgrade timeout", function(done) { var opts = { upgradeTimeout: 100 }; - var engine = listen(opts, function (port) { - engine.on('connection', function (conn) { - conn.on('upgrading', function (transport) { - transport.on('close', done); + var engine = listen(opts, function(port) { + engine.on("connection", function(conn) { + conn.on("upgrading", function(transport) { + transport.on("close", done); }); }); - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('upgrading', function (transport) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("upgrading", function(transport) { // override not to complete upgrading - transport.send = function () {}; + transport.send = function() {}; }); }); }); - it('should not crash when messing with Object prototype', function (done) { - Object.prototype.foo = 'bar'; // eslint-disable-line no-extend-native - var engine = listen({ allowUpgrades: true }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { + it("should not crash when messing with Object prototype", function(done) { + Object.prototype.foo = "bar"; // eslint-disable-line no-extend-native + var engine = listen({ allowUpgrades: true }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { engine.close(); - setTimeout(function () { + setTimeout(function() { done(); }, 100); }); }); }); - describe('graceful close', function () { - function fixture (filename) { - return process.execPath + ' ' + - path.join(__dirname, 'fixtures', filename); + describe("graceful close", function() { + function fixture(filename) { + return ( + process.execPath + " " + path.join(__dirname, "fixtures", filename) + ); } - it('should stop socket and timers', function (done) { - exec(fixture('server-close.js'), done); + it("should stop socket and timers", function(done) { + exec(fixture("server-close.js"), done); }); - it('should stop upgraded socket and timers', function (done) { - exec(fixture('server-close-upgraded.js'), done); + it("should stop upgraded socket and timers", function(done) { + exec(fixture("server-close-upgraded.js"), done); }); - it('should stop upgrading socket and timers', function (done) { - exec(fixture('server-close-upgrading.js'), done); + it("should stop upgrading socket and timers", function(done) { + exec(fixture("server-close-upgrading.js"), done); }); }); }); - describe('messages', function () { + describe("messages", function() { this.timeout(5000); - it('should arrive from server to client', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.send('a'); + it("should arrive from server to client", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.send("a"); }); - socket.on('open', function () { - socket.on('message', function (msg) { - expect(msg).to.be('a'); + socket.on("open", function() { + socket.on("message", function(msg) { + expect(msg).to.be("a"); done(); }); }); }); }); - it('should arrive from server to client (multiple)', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - var expected = ['a', 'b', 'c']; + it("should arrive from server to client (multiple)", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var expected = ["a", "b", "c"]; var i = 0; - engine.on('connection', function (conn) { - conn.send('a'); + engine.on("connection", function(conn) { + conn.send("a"); // we use set timeouts to ensure the messages are delivered as part // of different. - setTimeout(function () { - conn.send('b'); + setTimeout(function() { + conn.send("b"); - setTimeout(function () { + setTimeout(function() { // here we make sure we buffer both the close packet and // a regular packet - conn.send('c'); + conn.send("c"); conn.close(); }, 50); }, 50); - conn.on('close', function () { + conn.on("close", function() { // since close fires right after the buffer is drained - setTimeout(function () { + setTimeout(function() { expect(i).to.be(3); done(); }, 50); }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { expect(msg).to.be(expected[i++]); }); }); }); }); - it('should not be receiving data when getting a message longer than maxHttpBufferSize when polling', function (done) { - var opts = { allowUpgrades: false, transports: ['polling'], maxHttpBufferSize: 5 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - done(new Error('Test invalidation (message is longer than allowed)')); + it("should not be receiving data when getting a message longer than maxHttpBufferSize when polling", function(done) { + var opts = { + allowUpgrades: false, + transports: ["polling"], + maxHttpBufferSize: 5 + }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + done( + new Error("Test invalidation (message is longer than allowed)") + ); }); }); - socket.on('open', function () { - socket.send('aasdasdakjhasdkjhasdkjhasdkjhasdkjhasdkjhasdkjha'); + socket.on("open", function() { + socket.send("aasdasdakjhasdkjhasdkjhasdkjhasdkjhasdkjhasdkjha"); }); - socket.on('close', function () { + socket.on("close", function() { done(); }); }); }); - it('should not be receiving data when getting a message longer than maxHttpBufferSize (websocket)', function (done) { + it("should not be receiving data when getting a message longer than maxHttpBufferSize (websocket)", function(done) { var opts = { maxHttpBufferSize: 5 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - done(new Error('Test invalidation (message is longer than allowed)')); + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + done( + new Error("Test invalidation (message is longer than allowed)") + ); }); }); - socket.on('open', function () { - socket.send('aasdasdakjhasdkjhasdkjhasdkjhasdkjhasdkjhasdkjha'); + socket.on("open", function() { + socket.send("aasdasdakjhasdkjhasdkjhasdkjhasdkjhasdkjhasdkjha"); }); - socket.on('close', function () { + socket.on("close", function() { done(); }); }); }); - it('should receive data when getting a message shorter than maxHttpBufferSize when polling', function (done) { - var opts = { allowUpgrades: false, transports: ['polling'], maxHttpBufferSize: 5 }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - expect(msg).to.be('a'); + it("should receive data when getting a message shorter than maxHttpBufferSize when polling", function(done) { + var opts = { + allowUpgrades: false, + transports: ["polling"], + maxHttpBufferSize: 5 + }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + expect(msg).to.be("a"); done(); }); }); - socket.on('open', function () { - socket.send('a'); + socket.on("open", function() { + socket.send("a"); }); }); }); - it('should arrive from server to client (ws)', function (done) { - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - engine.on('connection', function (conn) { - conn.send('a'); + it("should arrive from server to client (ws)", function(done) { + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] }); - socket.on('open', function () { - socket.on('message', function (msg) { - expect(msg).to.be('a'); + engine.on("connection", function(conn) { + conn.send("a"); + }); + socket.on("open", function() { + socket.on("message", function(msg) { + expect(msg).to.be("a"); done(); }); }); }); }); - it('should arrive from server to client (multiple, ws)', function (done) { - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - var expected = ['a', 'b', 'c']; + it("should arrive from server to client (multiple, ws)", function(done) { + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + var expected = ["a", "b", "c"]; var i = 0; - engine.on('connection', function (conn) { - conn.send('a'); - setTimeout(function () { - conn.send('b'); - setTimeout(function () { - conn.send('c'); + engine.on("connection", function(conn) { + conn.send("a"); + setTimeout(function() { + conn.send("b"); + setTimeout(function() { + conn.send("c"); conn.close(); }, 50); }, 50); - conn.on('close', function () { - setTimeout(function () { + conn.on("close", function() { + setTimeout(function() { expect(i).to.be(3); done(); }, 50); }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { expect(msg).to.be(expected[i++]); }); }); }); }); - it('should arrive from server to client (multiple, no delay, ws)', function (done) { - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - var expected = ['a', 'b', 'c']; + it("should arrive from server to client (multiple, no delay, ws)", function(done) { + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + var expected = ["a", "b", "c"]; var i = 0; - engine.on('connection', function (conn) { - conn.on('close', function () { - setTimeout(function () { + engine.on("connection", function(conn) { + conn.on("close", function() { + setTimeout(function() { expect(i).to.be(3); done(); }, 50); }); - conn.send('a'); - conn.send('b'); - conn.send('c'); + conn.send("a"); + conn.send("b"); + conn.send("c"); conn.close(); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { expect(msg).to.be(expected[i++]); }); }); }); }); - it('should arrive when binary data is sent as Int8Array (ws)', function (done) { + it("should arrive when binary data is sent as Int8Array (ws)", function(done) { var binaryData = new Int8Array(5); for (var i = 0; i < binaryData.length; i++) { binaryData[i] = i; } - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { conn.send(binaryData); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { for (var i = 0; i < binaryData.length; i++) { var num = msg.readInt8(i); expect(num).to.be(i); @@ -1315,22 +1450,24 @@ describe('server', function () { }); }); - it('should arrive when binary data is sent as Int32Array (ws)', function (done) { + it("should arrive when binary data is sent as Int32Array (ws)", function(done) { var binaryData = new Int32Array(5); for (var i = 0; i < binaryData.length; i++) { binaryData[i] = (i + 100) * 9823; } - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { conn.send(binaryData); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { for (var i = 0, ii = 0; ii < binaryData.length; i += 4, ii++) { var num = msg.readInt32LE(i); expect(num).to.be((ii + 100) * 9823); @@ -1341,22 +1478,24 @@ describe('server', function () { }); }); - it('should arrive when binary data is sent as Int32Array, given as ArrayBuffer(ws)', function (done) { + it("should arrive when binary data is sent as Int32Array, given as ArrayBuffer(ws)", function(done) { var binaryData = new Int32Array(5); for (var i = 0; i < binaryData.length; i++) { binaryData[i] = (i + 100) * 9823; } - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { conn.send(binaryData.buffer); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { for (var i = 0, ii = 0; ii < binaryData.length; i += 4, ii++) { var num = msg.readInt32LE(i); expect(num).to.be((ii + 100) * 9823); @@ -1367,22 +1506,24 @@ describe('server', function () { }); }); - it('should arrive when binary data is sent as Buffer (ws)', function (done) { + it("should arrive when binary data is sent as Buffer (ws)", function(done) { var binaryData = Buffer.allocUnsafe(5); for (var i = 0; i < binaryData.length; i++) { binaryData.writeInt8(i, i); } - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { conn.send(binaryData); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { for (var i = 0; i < binaryData.length; i++) { var num = msg.readInt8(i); expect(num).to.be(i); @@ -1393,22 +1534,24 @@ describe('server', function () { }); }); - it('should arrive when binary data sent as Buffer (polling)', function (done) { + it("should arrive when binary data sent as Buffer (polling)", function(done) { var binaryData = Buffer.allocUnsafe(5); for (var i = 0; i < binaryData.length; i++) { binaryData.writeInt8(i, i); } - var opts = { allowUpgrades: false, transports: ['polling'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); + var opts = { allowUpgrades: false, transports: ["polling"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { conn.send(binaryData); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { for (var i = 0; i < binaryData.length; i++) { var num = msg.readInt8(i); expect(num).to.be(i); @@ -1420,23 +1563,25 @@ describe('server', function () { }); }); - it('should arrive as ArrayBuffer if requested when binary data sent as Buffer (ws)', function (done) { + it("should arrive as ArrayBuffer if requested when binary data sent as Buffer (ws)", function(done) { var binaryData = Buffer.allocUnsafe(5); for (var i = 0; i < binaryData.length; i++) { binaryData.writeInt8(i, i); } - var opts = { allowUpgrades: false, transports: ['websocket'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - socket.binaryType = 'arraybuffer'; + var opts = { allowUpgrades: false, transports: ["websocket"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + socket.binaryType = "arraybuffer"; - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { conn.send(binaryData); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { expect(msg instanceof ArrayBuffer).to.be(true); var intArray = new Int8Array(msg); for (var i = 0; i < binaryData.length; i++) { @@ -1449,23 +1594,25 @@ describe('server', function () { }); }); - it('should arrive as ArrayBuffer if requested when binary data sent as Buffer (polling)', function (done) { + it("should arrive as ArrayBuffer if requested when binary data sent as Buffer (polling)", function(done) { var binaryData = Buffer.allocUnsafe(5); for (var i = 0; i < binaryData.length; i++) { binaryData.writeInt8(i, i); } - var opts = { allowUpgrades: false, transports: ['polling'] }; - var engine = listen(opts, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); - socket.binaryType = 'arraybuffer'; + var opts = { allowUpgrades: false, transports: ["polling"] }; + var engine = listen(opts, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); + socket.binaryType = "arraybuffer"; - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { conn.send(binaryData); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { expect(msg instanceof ArrayBuffer).to.be(true); var intArray = new Int8Array(msg); for (var i = 0; i < binaryData.length; i++) { @@ -1478,101 +1625,106 @@ describe('server', function () { }); }); - it('should trigger a flush/drain event', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - engine.on('connection', function (socket) { + it("should trigger a flush/drain event", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + engine.on("connection", function(socket) { var totalEvents = 4; - engine.on('flush', function (sock, buf) { + engine.on("flush", function(sock, buf) { expect(sock).to.be(socket); - expect(buf).to.be.an('array'); + expect(buf).to.be.an("array"); --totalEvents || done(); }); - socket.on('flush', function (buf) { - expect(buf).to.be.an('array'); + socket.on("flush", function(buf) { + expect(buf).to.be.an("array"); --totalEvents || done(); }); - engine.on('drain', function (sock) { + engine.on("drain", function(sock) { expect(sock).to.be(socket); expect(socket.writeBuffer.length).to.be(0); --totalEvents || done(); }); - socket.on('drain', function () { + socket.on("drain", function() { expect(socket.writeBuffer.length).to.be(0); --totalEvents || done(); }); - socket.send('aaaa'); + socket.send("aaaa"); }); - eioc('ws://localhost:%d'.s(port)); + eioc("ws://localhost:%d".s(port)); }); }); - it('should interleave with pongs if many messages buffered ' + - 'after connection open', function (done) { - this.slow(4000); - this.timeout(8000); + it( + "should interleave with pongs if many messages buffered " + + "after connection open", + function(done) { + this.slow(4000); + this.timeout(8000); - var opts = { - transports: ['websocket'], - pingInterval: 200, - pingTimeout: 100 - }; + var opts = { + transports: ["websocket"], + pingInterval: 200, + pingTimeout: 100 + }; - var engine = listen(opts, function (port) { - var messageCount = 100; - var messagePayload = new Array(256 * 256).join('a'); - var connection = null; - engine.on('connection', function (conn) { - connection = conn; - }); - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - socket.on('open', function () { - for (var i = 0; i < messageCount; i++) { - // connection.send('message: ' + i); // works - connection.send(messagePayload + '|message: ' + i); // does not work - } - var receivedCount = 0; - socket.on('message', function (msg) { - receivedCount += 1; - if (receivedCount === messageCount) { - done(); + var engine = listen(opts, function(port) { + var messageCount = 100; + var messagePayload = new Array(256 * 256).join("a"); + var connection = null; + engine.on("connection", function(conn) { + connection = conn; + }); + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); + socket.on("open", function() { + for (var i = 0; i < messageCount; i++) { + // connection.send('message: ' + i); // works + connection.send(messagePayload + "|message: " + i); // does not work } + var receivedCount = 0; + socket.on("message", function(msg) { + receivedCount += 1; + if (receivedCount === messageCount) { + done(); + } + }); }); }); - }); - }); - - it('should support chinese', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - var shi = '石室詩士施氏,嗜獅,誓食十獅。'; - var shi2 = '氏時時適市視獅。'; - engine.on('connection', function (conn) { - conn.send('.'); + } + ); + + it("should support chinese", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + var shi = "石室詩士施氏,嗜獅,誓食十獅。"; + var shi2 = "氏時時適市視獅。"; + engine.on("connection", function(conn) { + conn.send("."); conn.send(shi); conn.send(shi2); - conn.once('message', function (msg0) { - expect(msg0).to.be('.'); - conn.once('message', function (msg) { + conn.once("message", function(msg0) { + expect(msg0).to.be("."); + conn.once("message", function(msg) { expect(msg).to.be(shi); - conn.once('message', function (msg2) { + conn.once("message", function(msg2) { expect(msg2).to.be(shi2); done(); }); }); }); }); - socket.on('open', function () { - socket.once('message', function (msg0) { - expect(msg0).to.be('.'); - socket.once('message', function (msg) { + socket.on("open", function() { + socket.once("message", function(msg0) { + expect(msg0).to.be("."); + socket.once("message", function(msg) { expect(msg).to.be(shi); - socket.once('message', function (msg2) { + socket.once("message", function(msg2) { expect(msg2).to.be(shi2); - socket.send('.'); + socket.send("."); socket.send(shi); socket.send(shi2); }); @@ -1582,237 +1734,241 @@ describe('server', function () { }); }); - it('should send and receive data with key and cert (polling)', function (done) { + it("should send and receive data with key and cert (polling)", function(done) { if (UWS_ENGINE && NODE_LT_443) return done(); var srvOpts = { - key: fs.readFileSync('test/fixtures/server.key'), - cert: fs.readFileSync('test/fixtures/server.crt'), - ca: fs.readFileSync('test/fixtures/ca.crt'), + key: fs.readFileSync("test/fixtures/server.key"), + cert: fs.readFileSync("test/fixtures/server.crt"), + ca: fs.readFileSync("test/fixtures/ca.crt"), requestCert: true, rejectUnauthorized: true }; var opts = { - key: fs.readFileSync('test/fixtures/client.key'), - cert: fs.readFileSync('test/fixtures/client.crt'), - ca: fs.readFileSync('test/fixtures/ca.crt'), - transports: ['polling'] + key: fs.readFileSync("test/fixtures/client.key"), + cert: fs.readFileSync("test/fixtures/client.crt"), + ca: fs.readFileSync("test/fixtures/ca.crt"), + transports: ["polling"] }; - var srv = https.createServer(srvOpts, function (req, res) { + var srv = https.createServer(srvOpts, function(req, res) { res.writeHead(200); - res.end('hello world\n'); + res.end("hello world\n"); }); - var engine = eio({ transports: ['polling'], allowUpgrades: false }); + var engine = eio({ transports: ["polling"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function () { + srv.listen(function() { var port = srv.address().port; - var socket = new eioc.Socket('https://localhost:%d'.s(port), opts); + var socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - expect(msg).to.be('hello'); + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + expect(msg).to.be("hello"); done(); }); }); - socket.on('open', function () { - socket.send('hello'); + socket.on("open", function() { + socket.send("hello"); }); }); }); - it('should send and receive data with ca when not requiring auth (polling)', function (done) { + it("should send and receive data with ca when not requiring auth (polling)", function(done) { if (UWS_ENGINE && NODE_LT_443) return done(); var srvOpts = { - key: fs.readFileSync('test/fixtures/server.key'), - cert: fs.readFileSync('test/fixtures/server.crt'), - ca: fs.readFileSync('test/fixtures/ca.crt'), + key: fs.readFileSync("test/fixtures/server.key"), + cert: fs.readFileSync("test/fixtures/server.crt"), + ca: fs.readFileSync("test/fixtures/ca.crt"), requestCert: true, rejectUnauthorized: false }; var opts = { - ca: fs.readFileSync('test/fixtures/ca.crt'), - transports: ['polling'] + ca: fs.readFileSync("test/fixtures/ca.crt"), + transports: ["polling"] }; - var srv = https.createServer(srvOpts, function (req, res) { + var srv = https.createServer(srvOpts, function(req, res) { res.writeHead(200); - res.end('hello world\n'); + res.end("hello world\n"); }); - var engine = eio({ transports: ['polling'], allowUpgrades: false }); + var engine = eio({ transports: ["polling"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function () { + srv.listen(function() { var port = srv.address().port; - var socket = new eioc.Socket('https://localhost:%d'.s(port), opts); + var socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - expect(msg).to.be('hello'); + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + expect(msg).to.be("hello"); done(); }); }); - socket.on('open', function () { - socket.send('hello'); + socket.on("open", function() { + socket.send("hello"); }); }); }); - it('should send and receive data with key and cert (ws)', function (done) { + it("should send and receive data with key and cert (ws)", function(done) { var srvOpts = { - key: fs.readFileSync('test/fixtures/server.key'), - cert: fs.readFileSync('test/fixtures/server.crt'), - ca: fs.readFileSync('test/fixtures/ca.crt'), + key: fs.readFileSync("test/fixtures/server.key"), + cert: fs.readFileSync("test/fixtures/server.crt"), + ca: fs.readFileSync("test/fixtures/ca.crt"), requestCert: true, rejectUnauthorized: true }; var opts = { - key: fs.readFileSync('test/fixtures/client.key'), - cert: fs.readFileSync('test/fixtures/client.crt'), - ca: fs.readFileSync('test/fixtures/ca.crt'), - transports: ['websocket'] + key: fs.readFileSync("test/fixtures/client.key"), + cert: fs.readFileSync("test/fixtures/client.crt"), + ca: fs.readFileSync("test/fixtures/ca.crt"), + transports: ["websocket"] }; - var srv = https.createServer(srvOpts, function (req, res) { + var srv = https.createServer(srvOpts, function(req, res) { res.writeHead(200); - res.end('hello world\n'); + res.end("hello world\n"); }); - var engine = eio({ transports: ['websocket'], allowUpgrades: false }); + var engine = eio({ transports: ["websocket"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function () { + srv.listen(function() { var port = srv.address().port; - var socket = new eioc.Socket('https://localhost:%d'.s(port), opts); + var socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - expect(msg).to.be('hello'); + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + expect(msg).to.be("hello"); done(); }); }); - socket.on('open', function () { - socket.send('hello'); + socket.on("open", function() { + socket.send("hello"); }); }); }); - it('should send and receive data with pfx (polling)', function (done) { + it("should send and receive data with pfx (polling)", function(done) { if (UWS_ENGINE && NODE_LT_443) return done(); var srvOpts = { - key: fs.readFileSync('test/fixtures/server.key'), - cert: fs.readFileSync('test/fixtures/server.crt'), - ca: fs.readFileSync('test/fixtures/ca.crt'), + key: fs.readFileSync("test/fixtures/server.key"), + cert: fs.readFileSync("test/fixtures/server.crt"), + ca: fs.readFileSync("test/fixtures/ca.crt"), requestCert: true, rejectUnauthorized: true }; var opts = { - pfx: fs.readFileSync('test/fixtures/client.pfx'), - ca: fs.readFileSync('test/fixtures/ca.crt'), - transports: ['polling'] + pfx: fs.readFileSync("test/fixtures/client.pfx"), + ca: fs.readFileSync("test/fixtures/ca.crt"), + transports: ["polling"] }; - var srv = https.createServer(srvOpts, function (req, res) { + var srv = https.createServer(srvOpts, function(req, res) { res.writeHead(200); - res.end('hello world\n'); + res.end("hello world\n"); }); - var engine = eio({ transports: ['polling'], allowUpgrades: false }); + var engine = eio({ transports: ["polling"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function () { + srv.listen(function() { var port = srv.address().port; - var socket = new eioc.Socket('https://localhost:%d'.s(port), opts); + var socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - expect(msg).to.be('hello'); + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + expect(msg).to.be("hello"); done(); }); }); - socket.on('open', function () { - socket.send('hello'); + socket.on("open", function() { + socket.send("hello"); }); }); }); - it('should send and receive data with pfx (ws)', function (done) { + it("should send and receive data with pfx (ws)", function(done) { if (UWS_ENGINE && NODE_LT_443) return done(); var srvOpts = { - key: fs.readFileSync('test/fixtures/server.key'), - cert: fs.readFileSync('test/fixtures/server.crt'), - ca: fs.readFileSync('test/fixtures/ca.crt'), + key: fs.readFileSync("test/fixtures/server.key"), + cert: fs.readFileSync("test/fixtures/server.crt"), + ca: fs.readFileSync("test/fixtures/ca.crt"), requestCert: true, rejectUnauthorized: true }; var opts = { - pfx: fs.readFileSync('test/fixtures/client.pfx'), - ca: fs.readFileSync('test/fixtures/ca.crt'), - transports: ['websocket'] + pfx: fs.readFileSync("test/fixtures/client.pfx"), + ca: fs.readFileSync("test/fixtures/ca.crt"), + transports: ["websocket"] }; - var srv = https.createServer(srvOpts, function (req, res) { + var srv = https.createServer(srvOpts, function(req, res) { res.writeHead(200); - res.end('hello world\n'); + res.end("hello world\n"); }); - var engine = eio({ transports: ['websocket'], allowUpgrades: false }); + var engine = eio({ transports: ["websocket"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function () { + srv.listen(function() { var port = srv.address().port; - var socket = new eioc.Socket('https://localhost:%d'.s(port), opts); + var socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on('connection', function (conn) { - conn.on('message', function (msg) { - expect(msg).to.be('hello'); + engine.on("connection", function(conn) { + conn.on("message", function(msg) { + expect(msg).to.be("hello"); done(); }); }); - socket.on('open', function () { - socket.send('hello'); + socket.on("open", function() { + socket.send("hello"); }); }); }); }); - describe('send', function () { - describe('writeBuffer', function () { - it('should not empty until `drain` event (polling)', function (done) { - listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); + describe("send", function() { + describe("writeBuffer", function() { + it("should not empty until `drain` event (polling)", function(done) { + listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); var totalEvents = 2; - socket.on('open', function () { - socket.send('a'); - socket.send('b'); + socket.on("open", function() { + socket.send("a"); + socket.send("b"); // writeBuffer should be nonempty, with 'a' still in it expect(socket.writeBuffer.length).to.eql(2); }); - socket.transport.on('drain', function () { + socket.transport.on("drain", function() { expect(socket.writeBuffer.length).to.eql(--totalEvents); totalEvents || done(); }); }); }); - it('should not empty until `drain` event (websocket)', function (done) { - listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + it("should not empty until `drain` event (websocket)", function(done) { + listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); var totalEvents = 2; - socket.on('open', function () { - socket.send('a'); - socket.send('b'); + socket.on("open", function() { + socket.send("a"); + socket.send("b"); // writeBuffer should be nonempty, with 'a' still in it expect(socket.writeBuffer.length).to.eql(2); }); - socket.transport.on('drain', function () { + socket.transport.on("drain", function() { expect(socket.writeBuffer.length).to.eql(--totalEvents); totalEvents || done(); }); @@ -1820,21 +1976,23 @@ describe('server', function () { }); }); - describe('callback', function () { - it('should execute in order when message sent (client) (polling)', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); + describe("callback", function() { + it("should execute in order when message sent (client) (polling)", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); var i = 0; var j = 0; - engine.on('connection', function (conn) { - conn.on('message', function (msg) { + engine.on("connection", function(conn) { + conn.on("message", function(msg) { conn.send(msg); }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { // send another packet until we've sent 3 total if (++i < 3) { expect(i).to.eql(j); @@ -1844,10 +2002,13 @@ describe('server', function () { } }); - function sendFn () { - socket.send(j, (function (value) { - j++; - })(j)); + function sendFn() { + socket.send( + j, + (function(value) { + j++; + })(j) + ); } sendFn(); @@ -1855,20 +2016,22 @@ describe('server', function () { }); }); - it('should execute in order when message sent (client) (websocket)', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + it("should execute in order when message sent (client) (websocket)", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); var i = 0; var j = 0; - engine.on('connection', function (conn) { - conn.on('message', function (msg) { + engine.on("connection", function(conn) { + conn.on("message", function(msg) { conn.send(msg); }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { // send another packet until we've sent 3 total if (++i < 3) { expect(i).to.eql(j); @@ -1878,10 +2041,13 @@ describe('server', function () { } }); - function sendFn () { - socket.send(j, (function (value) { - j++; - })(j)); + function sendFn() { + socket.send( + j, + (function(value) { + j++; + })(j) + ); } sendFn(); @@ -1889,25 +2055,27 @@ describe('server', function () { }); }); - it('should execute in order with payloads (client) (polling)', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); + it("should execute in order with payloads (client) (polling)", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); var i = 0; var lastCbFired = 0; - engine.on('connection', function (conn) { - conn.on('message', function (msg) { + engine.on("connection", function(conn) { + conn.on("message", function(msg) { conn.send(msg); }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { expect(msg).to.eql(i + 1); i++; }); - function cb (value) { + function cb(value) { expect(value).to.eql(lastCbFired + 1); lastCbFired = value; if (value === 3) { @@ -1916,35 +2084,43 @@ describe('server', function () { } // 2 and 3 will be in the same payload - socket.once('flush', function () { - socket.send(2, function () { cb(2); }); - socket.send(3, function () { cb(3); }); + socket.once("flush", function() { + socket.send(2, function() { + cb(2); + }); + socket.send(3, function() { + cb(3); + }); }); - socket.send(1, function () { cb(1); }); + socket.send(1, function() { + cb(1); + }); }); }); }); - it('should execute in order with payloads (client) (websocket)', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + it("should execute in order with payloads (client) (websocket)", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); var i = 0; var lastCbFired = 0; - engine.on('connection', function (conn) { - conn.on('message', function (msg) { + engine.on("connection", function(conn) { + conn.on("message", function(msg) { conn.send(msg); }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { expect(msg).to.eql(i + 1); i++; }); - function cb (value) { + function cb(value) { expect(value).to.eql(lastCbFired + 1); lastCbFired = value; if (value === 3) { @@ -1953,84 +2129,94 @@ describe('server', function () { } // 2 and 3 will be in the same payload - socket.once('flush', function () { - socket.send(2, function () { cb(2); }); - socket.send(3, function () { cb(3); }); + socket.once("flush", function() { + socket.send(2, function() { + cb(2); + }); + socket.send(3, function() { + cb(3); + }); }); - socket.send(1, function () { cb(1); }); + socket.send(1, function() { + cb(1); + }); }); }); }); - it('should execute when message sent (polling)', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); + it("should execute when message sent (polling)", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); var i = 0; var j = 0; - engine.on('connection', function (conn) { - conn.send('a', function (transport) { + engine.on("connection", function(conn) { + conn.send("a", function(transport) { i++; }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { j++; }); }); - setTimeout(function () { + setTimeout(function() { expect(i).to.be(j); done(); }, 100); }); }); - it('should execute when message sent (websocket)', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }); + it("should execute when message sent (websocket)", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["websocket"] + }); var i = 0; var j = 0; - engine.on('connection', function (conn) { - conn.send('a', function (transport) { + engine.on("connection", function(conn) { + conn.send("a", function(transport) { i++; }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { j++; }); }); - setTimeout(function () { + setTimeout(function() { expect(i).to.be(j); done(); }, 100); }); }); - it('should execute once for each send', function (done) { - var engine = listen(function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); + it("should execute once for each send", function(done) { + var engine = listen(function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); var a = 0; var b = 0; var c = 0; var all = 0; - engine.on('connection', function (conn) { - conn.send('a'); - conn.send('b'); - conn.send('c'); + engine.on("connection", function(conn) { + conn.send("a"); + conn.send("b"); + conn.send("c"); }); - socket.on('open', function () { - socket.on('message', function (msg) { - if (msg === 'a') a++; - if (msg === 'b') b++; - if (msg === 'c') c++; + socket.on("open", function() { + socket.on("message", function(msg) { + if (msg === "a") a++; + if (msg === "b") b++; + if (msg === "c") c++; if (++all === 3) { expect(a).to.be(1); @@ -2043,89 +2229,93 @@ describe('server', function () { }); }); - it('should execute in multipart packet', function (done) { - var engine = listen(function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); + it("should execute in multipart packet", function(done) { + var engine = listen(function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); var i = 0; var j = 0; - engine.on('connection', function (conn) { - conn.send('b', function (transport) { + engine.on("connection", function(conn) { + conn.send("b", function(transport) { i++; }); - conn.send('a', function (transport) { + conn.send("a", function(transport) { i++; }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { j++; }); }); - setTimeout(function () { + setTimeout(function() { expect(i).to.be(j); done(); }, 200); }); }); - it('should execute in multipart packet (polling)', function (done) { - var engine = listen(function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); + it("should execute in multipart packet (polling)", function(done) { + var engine = listen(function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); var i = 0; var j = 0; - engine.on('connection', function (conn) { - conn.send('d', function (transport) { + engine.on("connection", function(conn) { + conn.send("d", function(transport) { i++; }); - conn.send('c', function (transport) { + conn.send("c", function(transport) { i++; }); - conn.send('b', function (transport) { + conn.send("b", function(transport) { i++; }); - conn.send('a', function (transport) { + conn.send("a", function(transport) { i++; }); }); - socket.on('open', function () { - socket.on('message', function (msg) { + socket.on("open", function() { + socket.on("message", function(msg) { j++; }); }); - setTimeout(function () { + setTimeout(function() { expect(i).to.be(j); done(); }, 200); }); }); - it('should clean callback references when socket gets closed with pending callbacks', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); + it("should clean callback references when socket gets closed with pending callbacks", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); - engine.on('connection', function (conn) { - socket.transport.on('pollComplete', function () { - conn.send('a', function (transport) { - done(new Error('Test invalidation')); + engine.on("connection", function(conn) { + socket.transport.on("pollComplete", function() { + conn.send("a", function(transport) { + done(new Error("Test invalidation")); }); if (!conn.writeBuffer.length) { - done(new Error('Test invalidation')); + done(new Error("Test invalidation")); } // force to close the socket when we have one or more packet(s) in buffer socket.close(); }); - conn.on('close', function (reason) { + conn.on("close", function(reason) { expect(conn.packetsFn).to.be.empty(); expect(conn.sentCallbackFn).to.be.empty(); done(); @@ -2134,21 +2324,23 @@ describe('server', function () { }); }); - it('should not execute when it is not actually sent (polling)', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['polling'] }); + it("should not execute when it is not actually sent (polling)", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { + transports: ["polling"] + }); - socket.transport.on('pollComplete', function (msg) { + socket.transport.on("pollComplete", function(msg) { socket.close(); }); - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { var err; - conn.send('a'); - conn.send('b', function (transport) { - err = new Error('Test invalidation'); + conn.send("a"); + conn.send("b", function(transport) { + err = new Error("Test invalidation"); }); - conn.on('close', function (reason) { + conn.on("close", function(reason) { done(err); }); }); @@ -2157,30 +2349,32 @@ describe('server', function () { }); }); - describe('packet', function () { - it('should emit when socket receives packet', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.on('packet', function (packet) { - expect(packet.type).to.be('message'); - expect(packet.data).to.be('a'); + describe("packet", function() { + it("should emit when socket receives packet", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.on("packet", function(packet) { + expect(packet.type).to.be("message"); + expect(packet.data).to.be("a"); done(); }); }); - socket.on('open', function () { - socket.send('a'); + socket.on("open", function() { + socket.send("a"); }); }); }); - it('should emit when receives ping', function (done) { - var engine = listen({ allowUpgrades: false, pingInterval: 4 }, function (port) { - eioc('ws://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.on('packet', function (packet) { + it("should emit when receives ping", function(done) { + var engine = listen({ allowUpgrades: false, pingInterval: 4 }, function( + port + ) { + eioc("ws://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.on("packet", function(packet) { conn.close(); - expect(packet.type).to.be('ping'); + expect(packet.type).to.be("ping"); done(); }); }); @@ -2188,28 +2382,30 @@ describe('server', function () { }); }); - describe('packetCreate', function () { - it('should emit before socket send message', function (done) { - var engine = listen({ allowUpgrades: false }, function (port) { - eioc('ws://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.on('packetCreate', function (packet) { - expect(packet.type).to.be('message'); - expect(packet.data).to.be('a'); + describe("packetCreate", function() { + it("should emit before socket send message", function(done) { + var engine = listen({ allowUpgrades: false }, function(port) { + eioc("ws://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.on("packetCreate", function(packet) { + expect(packet.type).to.be("message"); + expect(packet.data).to.be("a"); done(); }); - conn.send('a'); + conn.send("a"); }); }); }); - it('should emit before send pong', function (done) { - var engine = listen({ allowUpgrades: false, pingInterval: 4 }, function (port) { - eioc('ws://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.on('packetCreate', function (packet) { + it("should emit before send pong", function(done) { + var engine = listen({ allowUpgrades: false, pingInterval: 4 }, function( + port + ) { + eioc("ws://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.on("packetCreate", function(packet) { conn.close(); - expect(packet.type).to.be('pong'); + expect(packet.type).to.be("pong"); done(); }); }); @@ -2217,24 +2413,24 @@ describe('server', function () { }); }); - describe('upgrade', function () { - it('should upgrade', function (done) { - var engine = listen(function (port) { + describe("upgrade", function() { + it("should upgrade", function(done) { + var engine = listen(function(port) { // it takes both to send 50 to verify var ready = 2; var closed = 2; - function finish () { - setTimeout(function () { + function finish() { + setTimeout(function() { socket.close(); }, 10); } // server - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { var lastSent = 0; var lastReceived = 0; var upgraded = false; - var interval = setInterval(function () { + var interval = setInterval(function() { lastSent++; conn.send(lastSent); if (50 === lastSent) { @@ -2243,23 +2439,23 @@ describe('server', function () { } }, 2); - expect(conn.request._query.transport).to.be('polling'); + expect(conn.request._query.transport).to.be("polling"); - conn.on('message', function (msg) { - expect(conn.request._query).to.be.an('object'); + conn.on("message", function(msg) { + expect(conn.request._query).to.be.an("object"); lastReceived++; expect(msg).to.eql(lastReceived); }); - conn.on('upgrade', function (to) { - expect(conn.request._query.transport).to.be('polling'); + conn.on("upgrade", function(to) { + expect(conn.request._query.transport).to.be("polling"); upgraded = true; - expect(to.name).to.be('websocket'); - expect(conn.transport.name).to.be('websocket'); + expect(to.name).to.be("websocket"); + expect(conn.transport.name).to.be("websocket"); }); - conn.on('close', function (reason) { - expect(reason).to.be('transport close'); + conn.on("close", function(reason) { + expect(reason).to.be("transport close"); expect(lastSent).to.be(50); expect(lastReceived).to.be(50); expect(upgraded).to.be(true); @@ -2268,12 +2464,12 @@ describe('server', function () { }); // client - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - socket.on('open', function () { + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", function() { var lastSent = 0; var lastReceived = 0; var upgrades = 0; - var interval = setInterval(function () { + var interval = setInterval(function() { lastSent++; socket.send(lastSent); if (50 === lastSent) { @@ -2281,9 +2477,9 @@ describe('server', function () { --ready || finish(); } }, 2); - socket.on('upgrading', function (to) { + socket.on("upgrading", function(to) { // we want to make sure for the sake of this test that we have a buffer - expect(to.name).to.equal('websocket'); + expect(to.name).to.equal("websocket"); upgrades++; // force send a few packets to ensure we test buffer transfer @@ -2294,16 +2490,16 @@ describe('server', function () { expect(socket.writeBuffer).to.not.be.empty(); }); - socket.on('upgrade', function (to) { - expect(to.name).to.equal('websocket'); + socket.on("upgrade", function(to) { + expect(to.name).to.equal("websocket"); upgrades++; }); - socket.on('message', function (msg) { + socket.on("message", function(msg) { lastReceived++; expect(lastReceived).to.eql(msg); }); - socket.on('close', function (reason) { - expect(reason).to.be('forced close'); + socket.on("close", function(reason) { + expect(reason).to.be("forced close"); expect(lastSent).to.be(50); expect(upgrades).to.be(2); --closed || done(); @@ -2312,202 +2508,249 @@ describe('server', function () { }); // attach another engine to make sure it doesn't break upgrades - eio.attach(engine.httpServer, { path: '/foo' }); + eio.attach(engine.httpServer, { path: "/foo" }); }); }); - describe('http compression', function () { - function getSidFromResponse (res) { - var c = cookieMod.parse(res.headers['set-cookie'][0]); + describe("http compression", function() { + function getSidFromResponse(res) { + var c = cookieMod.parse(res.headers["set-cookie"][0]); return c[Object.keys(c)[0]]; } - it('should compress by default', function (done) { - var engine = listen({ transports: ['polling'] }, function (port) { - engine.on('connection', function (conn) { + it("should compress by default", function(done) { + var engine = listen({ transports: ["polling"] }, function(port) { + engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(1024); for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf); }); - http.get({ - port: port, - path: '/engine.io/default/?transport=polling' - }, function (res) { - var sid = getSidFromResponse(res); - http.get({ - port: port, - path: '/engine.io/default/?transport=polling&sid=' + sid, - headers: { 'Accept-Encoding': 'gzip, deflate' } - }, function (res) { - expect(res.headers['content-encoding']).to.equal('gzip'); - res.pipe(zlib.createGunzip()) - .on('error', done) - .on('end', done) - .resume(); - }); - }); - }); - }); - - it('should compress using deflate', function (done) { - var engine = listen({ transports: ['polling'] }, function (port) { - engine.on('connection', function (conn) { - var buf = Buffer.allocUnsafe(1024); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; - conn.send(buf); - }); - - http.get({ - port: port, - path: '/engine.io/default/?transport=polling' - }, function (res) { - var sid = getSidFromResponse(res); - http.get({ - port: port, - path: '/engine.io/default/?transport=polling&sid=' + sid, - headers: { 'Accept-Encoding': 'deflate' } - }, function (res) { - expect(res.headers['content-encoding']).to.equal('deflate'); - res.pipe(zlib.createDeflate()) - .on('error', done) - .on('end', done) - .resume(); - }); - }); - }); - }); - - it('should set threshold', function (done) { - var engine = listen({ transports: ['polling'], httpCompression: { threshold: 0 } }, function (port) { - engine.on('connection', function (conn) { - var buf = Buffer.allocUnsafe(10); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; - conn.send(buf); - }); - - http.get({ - port: port, - path: '/engine.io/default/?transport=polling' - }, function (res) { - var sid = getSidFromResponse(res); - http.get({ + http.get( + { port: port, - path: '/engine.io/default/?transport=polling&sid=' + sid, - headers: { 'Accept-Encoding': 'gzip, deflate' } - }, function (res) { - expect(res.headers['content-encoding']).to.equal('gzip'); - done(); - }); - }); + path: "/engine.io/default/?transport=polling" + }, + function(res) { + var sid = getSidFromResponse(res); + http.get( + { + port: port, + path: "/engine.io/default/?transport=polling&sid=" + sid, + headers: { "Accept-Encoding": "gzip, deflate" } + }, + function(res) { + expect(res.headers["content-encoding"]).to.equal("gzip"); + res + .pipe(zlib.createGunzip()) + .on("error", done) + .on("end", done) + .resume(); + } + ); + } + ); }); }); - it('should disable compression', function (done) { - var engine = listen({ transports: ['polling'], httpCompression: false }, function (port) { - engine.on('connection', function (conn) { + it("should compress using deflate", function(done) { + var engine = listen({ transports: ["polling"] }, function(port) { + engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(1024); for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf); }); - http.get({ - port: port, - path: '/engine.io/default/?transport=polling' - }, function (res) { - var sid = getSidFromResponse(res); - http.get({ + http.get( + { port: port, - path: '/engine.io/default/?transport=polling&sid=' + sid, - headers: { 'Accept-Encoding': 'gzip, deflate' } - }, function (res) { - expect(res.headers['content-encoding']).to.be(undefined); - done(); - }); - }); - }); + path: "/engine.io/default/?transport=polling" + }, + function(res) { + var sid = getSidFromResponse(res); + http.get( + { + port: port, + path: "/engine.io/default/?transport=polling&sid=" + sid, + headers: { "Accept-Encoding": "deflate" } + }, + function(res) { + expect(res.headers["content-encoding"]).to.equal("deflate"); + res + .pipe(zlib.createDeflate()) + .on("error", done) + .on("end", done) + .resume(); + } + ); + } + ); + }); + }); + + it("should set threshold", function(done) { + var engine = listen( + { transports: ["polling"], httpCompression: { threshold: 0 } }, + function(port) { + engine.on("connection", function(conn) { + var buf = Buffer.allocUnsafe(10); + for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + conn.send(buf); + }); + + http.get( + { + port: port, + path: "/engine.io/default/?transport=polling" + }, + function(res) { + var sid = getSidFromResponse(res); + http.get( + { + port: port, + path: "/engine.io/default/?transport=polling&sid=" + sid, + headers: { "Accept-Encoding": "gzip, deflate" } + }, + function(res) { + expect(res.headers["content-encoding"]).to.equal("gzip"); + done(); + } + ); + } + ); + } + ); + }); + + it("should disable compression", function(done) { + var engine = listen( + { transports: ["polling"], httpCompression: false }, + function(port) { + engine.on("connection", function(conn) { + var buf = Buffer.allocUnsafe(1024); + for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + conn.send(buf); + }); + + http.get( + { + port: port, + path: "/engine.io/default/?transport=polling" + }, + function(res) { + var sid = getSidFromResponse(res); + http.get( + { + port: port, + path: "/engine.io/default/?transport=polling&sid=" + sid, + headers: { "Accept-Encoding": "gzip, deflate" } + }, + function(res) { + expect(res.headers["content-encoding"]).to.be(undefined); + done(); + } + ); + } + ); + } + ); }); - it('should disable compression per message', function (done) { - var engine = listen({ transports: ['polling'] }, function (port) { - engine.on('connection', function (conn) { + it("should disable compression per message", function(done) { + var engine = listen({ transports: ["polling"] }, function(port) { + engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(1024); for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf, { compress: false }); }); - http.get({ - port: port, - path: '/engine.io/default/?transport=polling' - }, function (res) { - var sid = getSidFromResponse(res); - http.get({ + http.get( + { port: port, - path: '/engine.io/default/?transport=polling&sid=' + sid, - headers: { 'Accept-Encoding': 'gzip, deflate' } - }, function (res) { - expect(res.headers['content-encoding']).to.be(undefined); - done(); - }); - }); + path: "/engine.io/default/?transport=polling" + }, + function(res) { + var sid = getSidFromResponse(res); + http.get( + { + port: port, + path: "/engine.io/default/?transport=polling&sid=" + sid, + headers: { "Accept-Encoding": "gzip, deflate" } + }, + function(res) { + expect(res.headers["content-encoding"]).to.be(undefined); + done(); + } + ); + } + ); }); }); - it('should not compress when the byte size is below threshold', function (done) { - var engine = listen({ transports: ['polling'] }, function (port) { - engine.on('connection', function (conn) { + it("should not compress when the byte size is below threshold", function(done) { + var engine = listen({ transports: ["polling"] }, function(port) { + engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(100); for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf); }); - http.get({ - port: port, - path: '/engine.io/default/?transport=polling' - }, function (res) { - var sid = getSidFromResponse(res); - http.get({ + http.get( + { port: port, - path: '/engine.io/default/?transport=polling&sid=' + sid, - headers: { 'Accept-Encoding': 'gzip, deflate' } - }, function (res) { - expect(res.headers['content-encoding']).to.be(undefined); - done(); - }); - }); + path: "/engine.io/default/?transport=polling" + }, + function(res) { + var sid = getSidFromResponse(res); + http.get( + { + port: port, + path: "/engine.io/default/?transport=polling&sid=" + sid, + headers: { "Accept-Encoding": "gzip, deflate" } + }, + function(res) { + expect(res.headers["content-encoding"]).to.be(undefined); + done(); + } + ); + } + ); }); }); }); - describe('permessage-deflate', function () { - it('should set threshold', function (done) { - var engine = listen({ transports: ['websocket'], perMessageDeflate: { threshold: 0 } }, function (port) { - engine.on('connection', function (conn) { - var socket = conn.transport.socket; - var send = socket.send; - socket.send = function (data, opts, callback) { - socket.send = send; - socket.send(data, opts, callback); - - expect(opts.compress).to.be(true); - conn.close(); - done(); - }; + describe("permessage-deflate", function() { + it("should set threshold", function(done) { + var engine = listen( + { transports: ["websocket"], perMessageDeflate: { threshold: 0 } }, + function(port) { + engine.on("connection", function(conn) { + var socket = conn.transport.socket; + var send = socket.send; + socket.send = function(data, opts, callback) { + socket.send = send; + socket.send(data, opts, callback); + + expect(opts.compress).to.be(true); + conn.close(); + done(); + }; - var buf = Buffer.allocUnsafe(100); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; - conn.send(buf, { compress: true }); - }); - eioc('http://localhost:%d'.s(port), { transports: ['websocket'] }); - }); + var buf = Buffer.allocUnsafe(100); + for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + conn.send(buf, { compress: true }); + }); + eioc("http://localhost:%d".s(port), { transports: ["websocket"] }); + } + ); }); - it('should not compress when the byte size is below threshold', function (done) { - var engine = listen({ transports: ['websocket'] }, function (port) { - engine.on('connection', function (conn) { + it("should not compress when the byte size is below threshold", function(done) { + var engine = listen({ transports: ["websocket"] }, function(port) { + engine.on("connection", function(conn) { var socket = conn.transport.socket; var send = socket.send; - socket.send = function (data, opts, callback) { + socket.send = function(data, opts, callback) { socket.send = send; socket.send(data, opts, callback); @@ -2520,97 +2763,110 @@ describe('server', function () { for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf, { compress: true }); }); - eioc('http://localhost:%d'.s(port), { transports: ['websocket'] }); + eioc("http://localhost:%d".s(port), { transports: ["websocket"] }); }); }); }); - describe('extraHeaders', function () { + describe("extraHeaders", function() { this.timeout(5000); var headers = { - 'x-custom-header-for-my-project': 'my-secret-access-token', - 'cookie': 'user_session=NI2JlCKF90aE0sJZD9ZzujtdsUqNYSBYxzlTsvdSUe35ZzdtVRGqYFr0kdGxbfc5gUOkR9RGp20GVKza; path=/; expires=Tue, 07-Apr-2015 18:18:08 GMT; secure; HttpOnly' + "x-custom-header-for-my-project": "my-secret-access-token", + cookie: + "user_session=NI2JlCKF90aE0sJZD9ZzujtdsUqNYSBYxzlTsvdSUe35ZzdtVRGqYFr0kdGxbfc5gUOkR9RGp20GVKza; path=/; expires=Tue, 07-Apr-2015 18:18:08 GMT; secure; HttpOnly" }; - function testForTransport (transport, done) { - var engine = listen(function (port) { - var socket = new eioc.Socket('ws://localhost:%d'.s(port), { + function testForTransport(transport, done) { + var engine = listen(function(port) { + var socket = new eioc.Socket("ws://localhost:%d".s(port), { extraHeaders: headers, transports: [transport] }); - engine.on('connection', function (conn) { + engine.on("connection", function(conn) { for (var h in headers) { expect(conn.request.headers[h]).to.equal(headers[h]); } done(); }); - socket.on('open', function () {}); + socket.on("open", function() {}); }); } - it('should arrive from client to server via WebSockets', function (done) { - testForTransport('websocket', done); + it("should arrive from client to server via WebSockets", function(done) { + testForTransport("websocket", done); }); - it('should arrive from client to server via XMLHttpRequest', function (done) { - testForTransport('polling', done); + it("should arrive from client to server via XMLHttpRequest", function(done) { + testForTransport("polling", done); }); }); - describe('response headers', function () { - function testForHeaders (headers, done) { - var engine = listen(function (port) { - engine.on('connection', function (conn) { - conn.transport.once('headers', function (headers) { - expect(headers['X-XSS-Protection']).to.be('0'); + describe("response headers", function() { + function testForHeaders(headers, done) { + var engine = listen(function(port) { + engine.on("connection", function(conn) { + conn.transport.once("headers", function(headers) { + expect(headers["X-XSS-Protection"]).to.be("0"); conn.close(); done(); }); - conn.send('hi'); + conn.send("hi"); }); - eioc('ws://localhost:%d'.s(port), { + eioc("ws://localhost:%d".s(port), { extraHeaders: headers, - transports: ['polling'] + transports: ["polling"] }); }); } - it('should contain X-XSS-Protection: 0 for IE8', function (done) { - var headers = { 'user-agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)' }; + it("should contain X-XSS-Protection: 0 for IE8", function(done) { + var headers = { + "user-agent": + "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)" + }; testForHeaders(headers, done); }); - it('should contain X-XSS-Protection: 0 for IE11', function (done) { - var headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko' }; + it("should contain X-XSS-Protection: 0 for IE11", function(done) { + var headers = { + "user-agent": + "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko" + }; testForHeaders(headers, done); }); }); - describe('cors', function () { - it('should handle OPTIONS requests', function (done) { - listen({handlePreflightRequest: true}, function (port) { - request.options('http://localhost:%d/engine.io/default/'.s(port)) - .set('Origin', 'http://engine.io') - .query({ transport: 'polling' }) - .end(function (err, res) { + describe("cors", function() { + it("should handle OPTIONS requests", function(done) { + listen({ handlePreflightRequest: true }, function(port) { + request + .options("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "polling" }) + .end(function(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'); - expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); + expect(res.body.message).to.be("Bad handshake method"); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + expect(res.header["access-control-allow-origin"]).to.be( + "http://engine.io" + ); done(); }); }); }); - it('should not handle OPTIONS requests', function (done) { - listen({handlePreflightRequest: false}, function (port) { - request.options('http://localhost:%d/engine.io/default/'.s(port)) - .set('Origin', 'http://engine.io') - .query({ transport: 'polling' }) - .end(function (err, res) { + it("should not handle OPTIONS requests", function(done) { + listen({ handlePreflightRequest: false }, function(port) { + request + .options("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "polling" }) + .end(function(err, res) { expect(err).to.be.an(Error); expect(res.status).to.be(501); expect(res.body.code).to.be(undefined); @@ -2619,49 +2875,64 @@ describe('server', function () { }); }); - it('should handle OPTIONS requests with the given function', function (done) { - var handlePreflightRequest = function (req, res) { + it("should handle OPTIONS requests with the given function", function(done) { + var handlePreflightRequest = function(req, res) { var headers = {}; if (req.headers.origin) { - headers['Access-Control-Allow-Credentials'] = 'true'; - headers['Access-Control-Allow-Origin'] = req.headers.origin; + headers["Access-Control-Allow-Credentials"] = "true"; + headers["Access-Control-Allow-Origin"] = req.headers.origin; } else { - headers['Access-Control-Allow-Origin'] = '*'; + headers["Access-Control-Allow-Origin"] = "*"; } - headers['Access-Control-Allow-Methods'] = 'GET,HEAD,PUT,PATCH,POST,DELETE'; - headers['Access-Control-Allow-Headers'] = 'origin, content-type, accept'; + headers["Access-Control-Allow-Methods"] = + "GET,HEAD,PUT,PATCH,POST,DELETE"; + headers["Access-Control-Allow-Headers"] = + "origin, content-type, accept"; res.writeHead(200, headers); res.end(); }; - listen({handlePreflightRequest: handlePreflightRequest}, function (port) { - request.options('http://localhost:%d/engine.io/default/'.s(port)) - .set('Origin', 'http://engine.io') - .query({ transport: 'polling' }) - .end(function (err, res) { + listen({ handlePreflightRequest: handlePreflightRequest }, function( + port + ) { + request + .options("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "polling" }) + .end(function(err, res) { expect(err).to.be(null); expect(res.status).to.be(200); expect(res.body).to.be.empty(); - expect(res.header['access-control-allow-credentials']).to.be('true'); - expect(res.header['access-control-allow-origin']).to.be('http://engine.io'); - expect(res.header['access-control-allow-methods']).to.be('GET,HEAD,PUT,PATCH,POST,DELETE'); - expect(res.header['access-control-allow-headers']).to.be('origin, content-type, accept'); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + expect(res.header["access-control-allow-origin"]).to.be( + "http://engine.io" + ); + expect(res.header["access-control-allow-methods"]).to.be( + "GET,HEAD,PUT,PATCH,POST,DELETE" + ); + expect(res.header["access-control-allow-headers"]).to.be( + "origin, content-type, accept" + ); done(); }); }); }); }); - describe('wsEngine option', function () { - it('should allow loading of other websocket server implementation like uws', function (done) { - var engine = listen({ allowUpgrades: false, wsEngine: 'uws' }, function (port) { - expect(engine.ws instanceof require('uws').Server).to.be.ok(); - var socket = new eioc.Socket('ws://localhost:%d'.s(port)); - engine.on('connection', function (conn) { - conn.send('a'); - }); - socket.on('open', function () { - socket.on('message', function (msg) { - expect(msg).to.be('a'); + describe("wsEngine option", function() { + it("should allow loading of other websocket server implementation like uws", function(done) { + var engine = listen({ allowUpgrades: false, wsEngine: "uws" }, function( + port + ) { + expect(engine.ws instanceof require("uws").Server).to.be.ok(); + var socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", function(conn) { + conn.send("a"); + }); + socket.on("open", function() { + socket.on("message", function(msg) { + expect(msg).to.be("a"); done(); }); }); @@ -2669,22 +2940,22 @@ describe('server', function () { }); }); - describe('remoteAddress', function () { - it('should be defined (polling)', function (done) { - var engine = listen({ transports: ['polling'] }, port => { - eioc('ws://localhost:%d'.s(port), { transports: ['polling'] }); - engine.on('connection', socket => { - expect(socket.remoteAddress).to.be('::ffff:127.0.0.1'); + describe("remoteAddress", function() { + it("should be defined (polling)", function(done) { + var engine = listen({ transports: ["polling"] }, port => { + eioc("ws://localhost:%d".s(port), { transports: ["polling"] }); + engine.on("connection", socket => { + expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); done(); }); }); }); - it('should be defined (ws)', function (done) { - var engine = listen({ transports: ['websocket'] }, port => { - eioc('ws://localhost:%d'.s(port), { transports: ['websocket'] }); - engine.on('connection', socket => { - expect(socket.remoteAddress).to.be('::ffff:127.0.0.1'); + it("should be defined (ws)", function(done) { + var engine = listen({ transports: ["websocket"] }, port => { + eioc("ws://localhost:%d".s(port), { transports: ["websocket"] }); + engine.on("connection", socket => { + expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); done(); }); }); From f3c291fa613a9d50c924d74293035737fdace4f2 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sun, 12 Jan 2020 23:03:49 +0100 Subject: [PATCH 018/124] feat: generateId method can now return a Promise Related: - https://github.com/socketio/engine.io/issues/538 - https://github.com/socketio/engine.io/pull/535 --- lib/server.js | 11 +++++++++-- test/server.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index 89eaf606d..cc1959db7 100644 --- a/lib/server.js +++ b/lib/server.js @@ -215,8 +215,15 @@ class Server extends EventEmitter { * @param {Object} request object * @api private */ - handshake(transportName, req) { - const id = this.generateId(req); + async handshake(transportName, req) { + let id; + try { + id = await this.generateId(req); + } catch (e) { + debug("error while generating an id"); + sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); + return; + } debug('handshaking client "%s"', id); diff --git a/test/server.js b/test/server.js index 0f4c6352f..8d66b30ff 100644 --- a/test/server.js +++ b/test/server.js @@ -315,6 +315,36 @@ describe("server", function() { }); }); + it("should register a new client with custom id (with a Promise)", function(done) { + const engine = listen({ allowUpgrades: false }, port => { + const customId = "CustomId" + Date.now(); + + engine.generateId = function() { + return Promise.resolve(customId); + }; + + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.once("open", () => { + expect(socket.id).to.be(customId); + expect(engine.clients[customId].id).to.be(customId); + done(); + }); + }); + }); + + it("should disallow connection that are rejected by `generateId`", function(done) { + const engine = listen({ allowUpgrades: false }, port => { + engine.generateId = () => { + return Promise.reject(new Error("nope")); + }; + + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("error", () => { + done(); + }); + }); + }); + it("should exchange handshake data", function(done) { listen({ allowUpgrades: false }, function(port) { var socket = new eioc.Socket("ws://localhost:%d".s(port)); From 2ae2520a342ad3130826c117f1bb81f3841f81b3 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 14 Jan 2020 23:03:12 +0100 Subject: [PATCH 019/124] chore: point towards the v4 branch --- package-lock.json | 19 +++++-------------- package.json | 4 ++-- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 58d3b9dd4..1e8b4cf5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -430,12 +430,6 @@ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, - "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 - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -518,15 +512,13 @@ } }, "engine.io-client": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz", - "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==", + "version": "git+https://github.com/socketio/engine.io-client.git#eb88094a0e85b2bb0114eb0055c60cf12cf298af", + "from": "git+https://github.com/socketio/engine.io-client.git#v4", "dev": true, "requires": { "component-emitter": "1.2.1", - "component-inherit": "0.0.3", "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", + "engine.io-parser": "git+https://github.com/socketio/engine.io-parser.git#v4", "has-cors": "1.1.0", "indexof": "0.0.1", "parseqs": "0.0.5", @@ -548,9 +540,8 @@ } }, "engine.io-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", - "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", + "version": "git+https://github.com/socketio/engine.io-parser.git#a947ae59a2844e4041db58ff36b270d1528b3bee", + "from": "git+https://github.com/socketio/engine.io-parser.git#v4", "requires": { "after": "0.8.2", "arraybuffer.slice": "~0.0.7", diff --git a/package.json b/package.json index 73d87a85b..9e2fc5d49 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,12 @@ "base64id": "2.0.0", "cookie": "0.3.1", "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", + "engine.io-parser": "git+https://github.com/socketio/engine.io-parser.git#v4", "ws": "^7.1.2" }, "devDependencies": { "babel-eslint": "^8.0.2", - "engine.io-client": "3.4.0", + "engine.io-client": "git+https://github.com/socketio/engine.io-client.git#v4", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From 31ff87593f231b86dc47ec5761936439ebd53c20 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 3 Feb 2020 12:52:46 +0100 Subject: [PATCH 020/124] feat: reverse the ping-pong mechanism The ping packets will now be sent by the server, because the timers set in the browsers are not reliable enough. We suspect that a lot of timeout problems came from timers being delayed on the client-side. Breaking change: v3.x clients will not be able to connect anymore (they will send a ping packet and timeout while waiting for a pong packet). Related: https://github.com/socketio/engine.io/issues/312 --- README.md | 2 +- lib/socket.js | 42 +++++++++++++++++++++++++++++++++--------- package-lock.json | 2 +- test/server.js | 8 ++++---- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7c632064a..073ececa1 100644 --- a/README.md +++ b/README.md @@ -321,7 +321,7 @@ A representation of a client. _Inherits from EventEmitter_. - `type`: packet type - `data`: packet data (if type is message) - `packetCreate` - - Called before a socket sends a packet (`message`, `pong`) + - Called before a socket sends a packet (`message`, `ping`) - **Arguments** - `type`: packet type - `data`: packet data (if type is message) diff --git a/lib/socket.js b/lib/socket.js index 97de8b6df..4618b2b80 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -30,6 +30,7 @@ class Socket extends EventEmitter { this.checkIntervalTimer = null; this.upgradeTimeoutTimer = null; this.pingTimeoutTimer = null; + this.pingIntervalTimer = null; this.setTransport(transport); this.onOpen(); @@ -60,7 +61,7 @@ class Socket extends EventEmitter { } this.emit("open"); - this.setPingTimeout(); + this.schedulePing(); } /** @@ -77,12 +78,12 @@ class Socket extends EventEmitter { // Reset ping timeout on any packet, incoming data is a good sign of // other side's liveness - this.setPingTimeout(); + this.resetPingTimeout(this.server.pingInterval + this.server.pingTimeout); switch (packet.type) { - case "ping": - debug("got ping"); - this.sendPacket("pong"); + case "pong": + debug("got pong"); + this.schedulePing(); this.emit("heartbeat"); break; @@ -112,15 +113,34 @@ class Socket extends EventEmitter { } /** - * Sets and resets ping timeout timer based on client pings. + * Pings client every `this.pingInterval` and expects response + * within `this.pingTimeout` or closes connection. * * @api private */ - setPingTimeout() { + schedulePing() { + clearTimeout(this.pingIntervalTimer); + this.pingIntervalTimer = setTimeout(() => { + debug( + "writing ping packet - expecting pong within %sms", + this.server.pingTimeout + ); + this.sendPacket("ping"); + this.resetPingTimeout(this.server.pingTimeout); + }, this.server.pingInterval); + } + + /** + * Resets ping timeout. + * + * @api private + */ + resetPingTimeout(timeout) { clearTimeout(this.pingTimeoutTimer); this.pingTimeoutTimer = setTimeout(() => { + if (this.readyState === "closed") return; this.onClose("ping timeout"); - }, this.server.pingInterval + this.server.pingTimeout); + }, timeout); } /** @@ -191,7 +211,7 @@ class Socket extends EventEmitter { self.clearTransport(); self.setTransport(transport); self.emit("upgrade", transport); - self.setPingTimeout(); + self.schedulePing(); self.flush(); if (self.readyState === "closing") { transport.close(function() { @@ -283,7 +303,11 @@ class Socket extends EventEmitter { onClose(reason, description) { if ("closed" !== this.readyState) { this.readyState = "closed"; + + // clear timers + clearTimeout(this.pingIntervalTimer); clearTimeout(this.pingTimeoutTimer); + clearInterval(this.checkIntervalTimer); this.checkIntervalTimer = null; clearTimeout(this.upgradeTimeoutTimer); diff --git a/package-lock.json b/package-lock.json index 1e8b4cf5c..19b8c3b3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -512,7 +512,7 @@ } }, "engine.io-client": { - "version": "git+https://github.com/socketio/engine.io-client.git#eb88094a0e85b2bb0114eb0055c60cf12cf298af", + "version": "git+https://github.com/socketio/engine.io-client.git#81d7171c6bb4053c802e3cc4b29a0e42dcf9c065", "from": "git+https://github.com/socketio/engine.io-client.git#v4", "dev": true, "requires": { diff --git a/test/server.js b/test/server.js index 8d66b30ff..ef778b502 100644 --- a/test/server.js +++ b/test/server.js @@ -1011,8 +1011,8 @@ describe("server", function() { engine.on("connection", function(conn) { conn.once("heartbeat", function() { + socket.onPacket = function() {}; setTimeout(function() { - socket.onPacket = function() {}; expect(clientCloseReason).to.be(null); }, 150); setTimeout(function() { @@ -2396,7 +2396,7 @@ describe("server", function() { }); }); - it("should emit when receives ping", function(done) { + it("should emit when receives pong", function(done) { var engine = listen({ allowUpgrades: false, pingInterval: 4 }, function( port ) { @@ -2404,7 +2404,7 @@ describe("server", function() { engine.on("connection", function(conn) { conn.on("packet", function(packet) { conn.close(); - expect(packet.type).to.be("ping"); + expect(packet.type).to.be("pong"); done(); }); }); @@ -2435,7 +2435,7 @@ describe("server", function() { engine.on("connection", function(conn) { conn.on("packetCreate", function(packet) { conn.close(); - expect(packet.type).to.be("pong"); + expect(packet.type).to.be("ping"); done(); }); }); From a374471d06e3681a769766a1d068898182f9305f Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 5 Feb 2020 08:28:44 +0100 Subject: [PATCH 021/124] feat: disable cookie by default and add sameSite attribute The cookie might be used for sticky-session, but is not mandatory so it makes sense to disable it by default. The change also add a SameSite=Lax attribute by default. Breaking change: the syntax has changed from ``` new Server({ cookieName: "test", cookieHttpOnly: false, cookiePath: "/custom" }) ``` to ``` new Server({ cookie: { name: "test", httpOnly: false, path: "/custom" } }) ``` All other options (domain, maxAge, sameSite, ...) are now supported. Reference: https://github.com/jshttp/cookie#options-1 --- README.md | 9 ++--- lib/server.js | 31 ++++++++++------ test/server.js | 96 +++++++++++++++++++++++++++++--------------------- 3 files changed, 80 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 073ececa1..dfd6332ac 100644 --- a/README.md +++ b/README.md @@ -244,13 +244,10 @@ to a single process. - `httpCompression` (`Object|Boolean`): parameters of the http compression for the polling transports (see [zlib](http://nodejs.org/api/zlib.html#zlib_options) api docs). Set to `false` to disable. (`true`) - `threshold` (`Number`): data is compressed only if the byte size is above this value (`1024`) - - `cookie` (`String|Boolean`): name of the HTTP cookie that + - `cookie` (`Object|Boolean`): configuration of the cookie that contains the client sid to send as part of handshake response - headers. Set to `false` to not send one. (`io`) - - `cookiePath` (`String|Boolean`): path of the above `cookie` - option. If false, no path will be sent, which means browsers will only send the cookie on the engine.io attached path (`/engine.io`). - Set false to not save io cookie on all requests. (`/`) - - `cookieHttpOnly` (`Boolean`): If `true` HttpOnly io cookie cannot be accessed by client-side APIs, such as JavaScript. (`true`) _This option has no effect if `cookie` or `cookiePath` is set to `false`._ + 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. - `initialPacket` (`Object`): an optional packet which will be concatenated to the handshake packet emitted by Engine.IO. - `close` diff --git a/lib/server.js b/lib/server.js index cc1959db7..7cfb05b84 100644 --- a/lib/server.js +++ b/lib/server.js @@ -30,16 +30,26 @@ class Server extends EventEmitter { this.transports = opts.transports || Object.keys(transports); this.allowUpgrades = false !== opts.allowUpgrades; this.allowRequest = opts.allowRequest; - this.cookie = false !== opts.cookie ? opts.cookie || "io" : false; - this.cookiePath = - false !== opts.cookiePath ? opts.cookiePath || "/" : false; - this.cookieHttpOnly = false !== opts.cookieHttpOnly; this.perMessageDeflate = false !== opts.perMessageDeflate ? opts.perMessageDeflate || true : false; this.httpCompression = false !== opts.httpCompression ? opts.httpCompression || {} : false; this.initialPacket = opts.initialPacket; + this.opts = Object.assign({}, opts); + + if (opts.cookie) { + this.opts.cookie = Object.assign( + { + name: "io", + path: "/", + httpOnly: opts.cookie.path !== false, + sameSite: "lax" + }, + opts.cookie + ); + } + // initialize compression options ["perMessageDeflate", "httpCompression"].forEach(type => { let compression = this[type]; @@ -249,12 +259,13 @@ class Server extends EventEmitter { const socket = new Socket(id, this, transport, req); const self = this; - if (false !== this.cookie) { - transport.on("headers", function(headers) { - headers["Set-Cookie"] = cookieMod.serialize(self.cookie, id, { - path: self.cookiePath, - httpOnly: self.cookiePath ? self.cookieHttpOnly : false - }); + if (this.opts.cookie) { + transport.on("headers", headers => { + headers["Set-Cookie"] = cookieMod.serialize( + this.opts.cookie.name, + id, + this.opts.cookie + ); }); } diff --git a/test/server.js b/test/server.js index ef778b502..d36e50dd4 100644 --- a/test/server.js +++ b/test/server.js @@ -141,133 +141,137 @@ describe("server", function() { }); describe("handshake", function() { - it("should send the io cookie", function(done) { - listen(function(port) { + 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 }) .end(function(err, res) { expect(err).to.be(null); // hack-obtain sid - var sid = res.text.match(/"sid":"([^"]+)"/)[1]; + const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( - "io=" + sid + "; Path=/; HttpOnly" + `io=${sid}; Path=/; HttpOnly; SameSite=Lax` ); done(); }); }); }); - it("should send the io cookie custom name", function(done) { - listen({ cookie: "woot" }, function(port) { + it("should send the io cookie custom name", done => { + listen({ cookie: { name: "woot" } }, port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) .end(function(err, res) { expect(err).to.be(null); - var sid = res.text.match(/"sid":"([^"]+)"/)[1]; + const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( - "woot=" + sid + "; Path=/; HttpOnly" + `woot=${sid}; Path=/; HttpOnly; SameSite=Lax` ); done(); }); }); }); - it("should send the cookie with custom path", function(done) { - listen({ cookiePath: "/custom" }, function(port) { + it("should send the cookie with custom path", done => { + listen({ cookie: { path: "/custom" } }, port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) .end(function(err, res) { expect(err).to.be(null); - var sid = res.text.match(/"sid":"([^"]+)"/)[1]; + const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( - "io=" + sid + "; Path=/custom; HttpOnly" + `io=${sid}; Path=/custom; HttpOnly; SameSite=Lax` ); done(); }); }); }); - it("should send the cookie with path=false", function(done) { - listen({ cookiePath: false }, function(port) { + it("should send the cookie with path=false", done => { + listen({ cookie: { path: false } }, port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) .end(function(err, res) { expect(err).to.be(null); - var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers["set-cookie"][0]).to.be("io=" + sid); + const sid = res.text.match(/"sid":"([^"]+)"/)[1]; + expect(res.headers["set-cookie"][0]).to.be( + `io=${sid}; SameSite=Lax` + ); done(); }); }); }); - it("should send the io cookie with httpOnly=true", function(done) { - listen({ cookieHttpOnly: true }, function(port) { + 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)) .query({ transport: "polling", b64: 1 }) .end(function(err, res) { expect(err).to.be(null); - var sid = res.text.match(/"sid":"([^"]+)"/)[1]; + const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( - "io=" + sid + "; Path=/; HttpOnly" + `io=${sid}; Path=/; HttpOnly; SameSite=Lax` ); done(); }); }); }); - it("should send the io cookie with httpOnly=true and path=false", function(done) { - listen({ cookieHttpOnly: true, cookiePath: false }, function(port) { + 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)) .query({ transport: "polling", b64: 1 }) .end(function(err, res) { expect(err).to.be(null); - var sid = res.text.match(/"sid":"([^"]+)"/)[1]; - expect(res.headers["set-cookie"][0]).to.be("io=" + sid); + const sid = res.text.match(/"sid":"([^"]+)"/)[1]; + expect(res.headers["set-cookie"][0]).to.be( + `io=${sid}; Path=/; HttpOnly; SameSite=Strict` + ); done(); }); }); }); - it("should send the io cookie with httpOnly=false", function(done) { - listen({ cookieHttpOnly: false }, function(port) { + 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)) .query({ transport: "polling", b64: 1 }) .end(function(err, res) { expect(err).to.be(null); - var sid = res.text.match(/"sid":"([^"]+)"/)[1]; + const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( - "io=" + sid + "; Path=/" + `io=${sid}; Path=/; SameSite=Lax` ); done(); }); }); }); - it("should send the io cookie with httpOnly not boolean", function(done) { - listen({ cookieHttpOnly: "no" }, function(port) { + 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)) .query({ transport: "polling", b64: 1 }) .end(function(err, res) { expect(err).to.be(null); - var sid = res.text.match(/"sid":"([^"]+)"/)[1]; + const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( - "io=" + sid + "; Path=/; HttpOnly" + `io=${sid}; Path=/; HttpOnly; SameSite=Lax` ); done(); }); }); }); - it("should not send the io cookie", function(done) { - listen({ cookie: false }, function(port) { + it("should not send the io cookie", done => { + listen({ cookie: false }, port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling" }) @@ -2549,7 +2553,9 @@ describe("server", function() { } it("should compress by default", function(done) { - var engine = listen({ transports: ["polling"] }, function(port) { + var engine = listen({ cookie: true, transports: ["polling"] }, function( + port + ) { engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(1024); for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; @@ -2584,7 +2590,9 @@ describe("server", function() { }); it("should compress using deflate", function(done) { - var engine = listen({ transports: ["polling"] }, function(port) { + var engine = listen({ cookie: true, transports: ["polling"] }, function( + port + ) { engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(1024); for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; @@ -2620,7 +2628,11 @@ describe("server", function() { it("should set threshold", function(done) { var engine = listen( - { transports: ["polling"], httpCompression: { threshold: 0 } }, + { + cookie: true, + transports: ["polling"], + httpCompression: { threshold: 0 } + }, function(port) { engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(10); @@ -2654,7 +2666,7 @@ describe("server", function() { it("should disable compression", function(done) { var engine = listen( - { transports: ["polling"], httpCompression: false }, + { cookie: true, transports: ["polling"], httpCompression: false }, function(port) { engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(1024); @@ -2687,7 +2699,9 @@ describe("server", function() { }); it("should disable compression per message", function(done) { - var engine = listen({ transports: ["polling"] }, function(port) { + var engine = listen({ cookie: true, transports: ["polling"] }, function( + port + ) { engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(1024); for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; @@ -2718,7 +2732,9 @@ describe("server", function() { }); it("should not compress when the byte size is below threshold", function(done) { - var engine = listen({ transports: ["polling"] }, function(port) { + var engine = listen({ cookie: true, transports: ["polling"] }, function( + port + ) { engine.on("connection", function(conn) { var buf = Buffer.allocUnsafe(100); for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; From 61e639b257c91f88200276122816b0514fb396df Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 14 Jan 2020 23:17:55 +0100 Subject: [PATCH 022/124] test: add Node.js 10, 12 and 13 in the test matrix uws is no longer maintained, and did not support Node.js >= 10. We now use a fork in order to support the newer versions of Node.js. It is maintained there: https://github.com/mmdevries/uws And can be installed with: `npm i github:mmdevries/uws#2.4.1` We could also try to support uWebSockets.js, but it does not have the same API as ws. The "engines" attribute has also been added in the package.json file, since we broke the support for Node.js 6 in the latest minor release. Source: https://github.com/mmdevries/uws Related: https://github.com/socketio/engine.io/pull/583 --- .travis.yml | 4 +++- package-lock.json | 5 ++--- package.json | 7 +++++-- test/server.js | 15 ++++++--------- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index fbda89be8..a93a3f47e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,9 @@ sudo: false language: node_js node_js: - "8" - - "9" + - "10" + - "12" + - "13" git: depth: 1 notifications: diff --git a/package-lock.json b/package-lock.json index 19b8c3b3d..1706ad680 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1631,9 +1631,8 @@ "dev": true }, "uws": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/uws/-/uws-9.14.0.tgz", - "integrity": "sha512-HNMztPP5A1sKuVFmdZ6BPVpBQd5bUjNC8EFMFiICK+oho/OQsAJy5hnIx4btMHiOk8j04f/DbIlqnEZ9d72dqg==", + "version": "github:mmdevries/uws#0ca2e41cb9337c5bf6bd11e61f9a1fdf3156d56f", + "from": "github:mmdevries/uws#2.4.1", "dev": true }, "which": { diff --git a/package.json b/package.json index 9e2fc5d49..3aef222b6 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "prettier": "^1.19.1", "s": "0.1.1", "superagent": "^3.8.1", - "uws": "~9.14.0" + "uws": "github:mmdevries/uws#2.4.1" }, "scripts": { "lint": "eslint lib/ test/ *.js", @@ -56,5 +56,8 @@ }, "files": [ "lib/" - ] + ], + "engines" : { + "node" : ">=8.0.0" + } } diff --git a/test/server.js b/test/server.js index d36e50dd4..56ac84d9b 100644 --- a/test/server.js +++ b/test/server.js @@ -17,11 +17,6 @@ var expect = require("expect.js"); var request = require("superagent"); var cookieMod = require("cookie"); -// are we running on node < 4.4.3 ? -var NODE_LT_443 = (function() { - var parts = process.versions.node.split("."); - return parts[0] < 4 || parts[1] < 4 || parts[2] < 3; -})(); // are we running uws wsEngine ? var UWS_ENGINE = process.env.EIO_WS_ENGINE === "uws"; @@ -1769,7 +1764,6 @@ describe("server", function() { }); it("should send and receive data with key and cert (polling)", function(done) { - if (UWS_ENGINE && NODE_LT_443) return done(); var srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -1810,7 +1804,6 @@ describe("server", function() { }); it("should send and receive data with ca when not requiring auth (polling)", function(done) { - if (UWS_ENGINE && NODE_LT_443) return done(); var srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -1849,6 +1842,9 @@ describe("server", function() { }); it("should send and receive data with key and cert (ws)", function(done) { + if (UWS_ENGINE) { + return this.skip(); + } var srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -1889,7 +1885,6 @@ describe("server", function() { }); it("should send and receive data with pfx (polling)", function(done) { - if (UWS_ENGINE && NODE_LT_443) return done(); var srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -1929,7 +1924,9 @@ describe("server", function() { }); it("should send and receive data with pfx (ws)", function(done) { - if (UWS_ENGINE && NODE_LT_443) return done(); + if (UWS_ENGINE) { + return this.skip(); + } var srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), From bafe684a193e7ad9e2af9aff82bb8b555f627f11 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 6 Feb 2020 13:54:39 +0100 Subject: [PATCH 023/124] refactor: refactor the handling of the options --- lib/server.js | 70 +++++++++++++++++++++++---------------------------- lib/socket.js | 22 ++++++++-------- 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/lib/server.js b/lib/server.js index 7cfb05b84..1632de0dc 100644 --- a/lib/server.js +++ b/lib/server.js @@ -14,29 +14,30 @@ class Server extends EventEmitter { * @param {Object} options * @api public */ - constructor(opts) { + constructor(opts = {}) { super(); this.clients = {}; this.clientsCount = 0; - opts = opts || {}; - - this.wsEngine = opts.wsEngine || process.env.EIO_WS_ENGINE || "ws"; - this.pingTimeout = opts.pingTimeout || 5000; - this.pingInterval = opts.pingInterval || 25000; - this.upgradeTimeout = opts.upgradeTimeout || 10000; - this.maxHttpBufferSize = opts.maxHttpBufferSize || 10e7; - this.transports = opts.transports || Object.keys(transports); - this.allowUpgrades = false !== opts.allowUpgrades; - this.allowRequest = opts.allowRequest; - this.perMessageDeflate = - false !== opts.perMessageDeflate ? opts.perMessageDeflate || true : false; - this.httpCompression = - false !== opts.httpCompression ? opts.httpCompression || {} : false; - this.initialPacket = opts.initialPacket; - - this.opts = Object.assign({}, opts); + this.opts = Object.assign( + { + wsEngine: process.env.EIO_WS_ENGINE || "ws", + pingTimeout: 5000, + pingInterval: 25000, + upgradeTimeout: 10000, + maxHttpBufferSize: 10e7, + transports: Object.keys(transports), + allowUpgrades: true, + perMessageDeflate: { + threshold: 1024 + }, + httpCompression: { + threshold: 1024 + } + }, + opts + ); if (opts.cookie) { this.opts.cookie = Object.assign( @@ -50,15 +51,6 @@ class Server extends EventEmitter { ); } - // initialize compression options - ["perMessageDeflate", "httpCompression"].forEach(type => { - let compression = this[type]; - if (true === compression) this[type] = compression = {}; - if (compression && null == compression.threshold) { - compression.threshold = 1024; - } - }); - this.init(); } @@ -68,12 +60,12 @@ class Server extends EventEmitter { * @api private */ init() { - if (!~this.transports.indexOf("websocket")) return; + if (!~this.opts.transports.indexOf("websocket")) return; if (this.ws) this.ws.close(); let wsModule; - switch (this.wsEngine) { + switch (this.opts.wsEngine) { case "uws": wsModule = require("uws"); break; @@ -86,8 +78,8 @@ class Server extends EventEmitter { this.ws = new wsModule.Server({ noServer: true, clientTracking: false, - perMessageDeflate: this.perMessageDeflate, - maxPayload: this.maxHttpBufferSize + perMessageDeflate: this.opts.perMessageDeflate, + maxPayload: this.opts.maxHttpBufferSize }); } @@ -98,7 +90,7 @@ class Server extends EventEmitter { * @api public */ upgrades(transport) { - if (!this.allowUpgrades) return []; + if (!this.opts.allowUpgrades) return []; return transports[transport].upgradesTo || []; } @@ -112,7 +104,7 @@ class Server extends EventEmitter { verify(req, upgrade, fn) { // transport check const transport = req._query.transport; - if (!~this.transports.indexOf(transport)) { + if (!~this.opts.transports.indexOf(transport)) { debug('unknown transport "%s"', transport); return fn(Server.errors.UNKNOWN_TRANSPORT, false); } @@ -140,8 +132,8 @@ class Server extends EventEmitter { // handshake is GET only if ("GET" !== req.method) return fn(Server.errors.BAD_HANDSHAKE_METHOD, false); - if (!this.allowRequest) return fn(null, true); - return this.allowRequest(req, fn); + if (!this.opts.allowRequest) return fn(null, true); + return this.opts.allowRequest(req, fn); } fn(null, true); @@ -240,10 +232,10 @@ class Server extends EventEmitter { try { var transport = new transports[transportName](req); if ("polling" === transportName) { - transport.maxHttpBufferSize = this.maxHttpBufferSize; - transport.httpCompression = this.httpCompression; + transport.maxHttpBufferSize = this.opts.maxHttpBufferSize; + transport.httpCompression = this.opts.httpCompression; } else if ("websocket" === transportName) { - transport.perMessageDeflate = this.perMessageDeflate; + transport.perMessageDeflate = this.opts.perMessageDeflate; } if (req._query && req._query.b64) { @@ -424,7 +416,7 @@ class Server extends EventEmitter { } }); - if (~self.transports.indexOf("websocket")) { + if (~self.opts.transports.indexOf("websocket")) { server.on("upgrade", function(req, socket, head) { if (check(req)) { self.handleUpgrade(req, socket, head); diff --git a/lib/socket.js b/lib/socket.js index 4618b2b80..f9236cdde 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -51,13 +51,13 @@ class Socket extends EventEmitter { JSON.stringify({ sid: this.id, upgrades: this.getAvailableUpgrades(), - pingInterval: this.server.pingInterval, - pingTimeout: this.server.pingTimeout + pingInterval: this.server.opts.pingInterval, + pingTimeout: this.server.opts.pingTimeout }) ); - if (this.server.initialPacket) { - this.sendPacket("message", this.server.initialPacket); + if (this.server.opts.initialPacket) { + this.sendPacket("message", this.server.opts.initialPacket); } this.emit("open"); @@ -78,7 +78,9 @@ class Socket extends EventEmitter { // Reset ping timeout on any packet, incoming data is a good sign of // other side's liveness - this.resetPingTimeout(this.server.pingInterval + this.server.pingTimeout); + this.resetPingTimeout( + this.server.opts.pingInterval + this.server.opts.pingTimeout + ); switch (packet.type) { case "pong": @@ -123,11 +125,11 @@ class Socket extends EventEmitter { this.pingIntervalTimer = setTimeout(() => { debug( "writing ping packet - expecting pong within %sms", - this.server.pingTimeout + this.server.opts.pingTimeout ); this.sendPacket("ping"); - this.resetPingTimeout(this.server.pingTimeout); - }, this.server.pingInterval); + this.resetPingTimeout(this.server.opts.pingTimeout); + }, this.server.opts.pingInterval); } /** @@ -195,7 +197,7 @@ class Socket extends EventEmitter { if ("open" === transport.readyState) { transport.close(); } - }, this.server.upgradeTimeout); + }, this.server.opts.upgradeTimeout); function onPacket(packet) { if ("ping" === packet.type && "probe" === packet.data) { @@ -455,7 +457,7 @@ class Socket extends EventEmitter { const l = allUpgrades.length; for (; i < l; ++i) { const upg = allUpgrades[i]; - if (this.server.transports.indexOf(upg) !== -1) { + if (this.server.opts.transports.indexOf(upg) !== -1) { availableUpgrades.push(upg); } } From 61b949259ed966ef6fc8bfd61f14d1a2ef06d319 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 10 Feb 2020 13:24:04 +0100 Subject: [PATCH 024/124] feat: use the cors module to handle cross-origin requests We'll now rely on the standard cors module (https://github.com/expressjs/cors), instead of the custom implementation that is error-prone and not really user-friendly. Breaking change: the handlePreflightRequest option is removed by the change. Before: ``` new Server({ handlePreflightRequest: (req, res) => { res.writeHead(200, { "Access-Control-Allow-Origin": 'https://example.com', "Access-Control-Allow-Methods": 'GET', "Access-Control-Allow-Headers": 'Authorization', "Access-Control-Allow-Credentials": true }); res.end(); } }) ``` After: ``` new Server({ cors: { origin: "https://example.com", methods: ["GET"], allowedHeaders: ["Authorization"], credentials: true } }) ``` --- README.md | 2 +- lib/server.js | 39 ++++--- lib/transports/index.js | 2 +- lib/transports/polling-xhr.js | 43 -------- package-lock.json | 17 ++- package.json | 5 +- test/server.js | 199 +++++++++++++++++++++------------- 7 files changed, 164 insertions(+), 143 deletions(-) delete mode 100644 lib/transports/polling-xhr.js diff --git a/README.md b/README.md index dfd6332ac..e77973510 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,7 @@ to a single process. 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. + - `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. - `close` - Closes all clients @@ -277,7 +278,6 @@ to a single process. - `path` (`String`): name of the path to capture (`/engine.io`). - `destroyUpgrade` (`Boolean`): destroy unhandled upgrade requests (`true`) - `destroyUpgradeTimeout` (`Number`): milliseconds after which unhandled requests are ended (`1000`) - - `handlePreflightRequest` (`Boolean|Function`): whether to let engine.io handle the OPTIONS requests. You can also pass a custom function to handle the requests (`true`) - `generateId` - Generate a socket id. - Overwrite this method to generate your custom socket id. diff --git a/lib/server.js b/lib/server.js index 1632de0dc..527c7a0f9 100644 --- a/lib/server.js +++ b/lib/server.js @@ -34,7 +34,8 @@ class Server extends EventEmitter { }, httpCompression: { threshold: 1024 - } + }, + cors: false }, opts ); @@ -51,6 +52,10 @@ class Server extends EventEmitter { ); } + if (this.opts.cors) { + this.corsMiddleware = require("cors")(this.opts.cors); + } + this.init(); } @@ -183,8 +188,7 @@ class Server extends EventEmitter { this.prepare(req); req.res = res; - const self = this; - this.verify(req, false, function(err, success) { + const callback = (err, success) => { if (!success) { sendErrorMessage(req, res, err); return; @@ -192,11 +196,19 @@ class Server extends EventEmitter { if (req._query.sid) { debug("setting new request for existing client"); - self.clients[req._query.sid].transport.onRequest(req); + this.clients[req._query.sid].transport.onRequest(req); } else { - self.handshake(req._query.transport, req); + this.handshake(req._query.transport, req); } - }); + }; + + if (this.corsMiddleware) { + this.corsMiddleware.call(null, req, res, () => { + this.verify(req, false, callback); + }); + } else { + this.verify(req, false, callback); + } } /** @@ -380,12 +392,6 @@ class Server extends EventEmitter { path += "/"; function check(req) { - if ( - "OPTIONS" === req.method && - false === options.handlePreflightRequest - ) { - return false; - } return path === req.url.substr(0, path.length); } @@ -399,14 +405,7 @@ class Server extends EventEmitter { server.on("request", function(req, res) { if (check(req)) { debug('intercepting request for path "%s"', path); - if ( - "OPTIONS" === req.method && - "function" === typeof options.handlePreflightRequest - ) { - options.handlePreflightRequest.call(server, req, res); - } else { - self.handleRequest(req, res); - } + self.handleRequest(req, res); } else { let i = 0; const l = listeners.length; diff --git a/lib/transports/index.js b/lib/transports/index.js index ea106551e..98e0ab537 100644 --- a/lib/transports/index.js +++ b/lib/transports/index.js @@ -1,4 +1,4 @@ -const XHR = require("./polling-xhr"); +const XHR = require("./polling"); const JSONP = require("./polling-jsonp"); /** diff --git a/lib/transports/polling-xhr.js b/lib/transports/polling-xhr.js deleted file mode 100644 index c4a63e26b..000000000 --- a/lib/transports/polling-xhr.js +++ /dev/null @@ -1,43 +0,0 @@ -const Polling = require("./polling"); - -class XHR extends Polling { - /** - * Overrides `onRequest` to handle `OPTIONS`.. - * - * @param {http.IncomingMessage} - * @api private - */ - onRequest(req) { - if ("OPTIONS" === req.method) { - const res = req.res; - const headers = this.headers(req); - headers["Access-Control-Allow-Headers"] = "Content-Type"; - res.writeHead(200, headers); - res.end(); - } else { - super.onRequest(req); - } - } - - /** - * Returns headers for a response. - * - * @param {http.IncomingMessage} request - * @param {Object} extra headers - * @api private - */ - headers(req, headers) { - headers = headers || {}; - - if (req.headers.origin) { - headers["Access-Control-Allow-Credentials"] = "true"; - headers["Access-Control-Allow-Origin"] = req.headers.origin; - } else { - headers["Access-Control-Allow-Origin"] = "*"; - } - - return super.headers(req, headers); - } -} - -module.exports = XHR; diff --git a/package-lock.json b/package-lock.json index 1706ad680..917a71cbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -465,6 +465,15 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -1206,8 +1215,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "once": { "version": "1.4.0", @@ -1635,6 +1643,11 @@ "from": "github:mmdevries/uws#2.4.1", "dev": true }, + "vary": { + "version": "1.1.2", + "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", diff --git a/package.json b/package.json index 3aef222b6..83e7b766e 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "0.3.1", + "cors": "~2.8.5", "debug": "~4.1.0", "engine.io-parser": "git+https://github.com/socketio/engine.io-parser.git#v4", "ws": "^7.1.2" @@ -57,7 +58,7 @@ "files": [ "lib/" ], - "engines" : { - "node" : ">=8.0.0" + "engines": { + "node": ">=8.0.0" } } diff --git a/test/server.js b/test/server.js index 56ac84d9b..a770d7169 100644 --- a/test/server.js +++ b/test/server.js @@ -2881,85 +2881,136 @@ describe("server", function() { }); describe("cors", function() { - it("should handle OPTIONS requests", function(done) { - listen({ handlePreflightRequest: true }, function(port) { - request - .options("http://localhost:%d/engine.io/default/".s(port)) - .set("Origin", "http://engine.io") - .query({ transport: "polling" }) - .end(function(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"); - expect(res.header["access-control-allow-credentials"]).to.be( - "true" - ); - expect(res.header["access-control-allow-origin"]).to.be( - "http://engine.io" - ); - done(); - }); - }); + it("should allow CORS from the current origin (preflight request)", done => { + listen( + { cors: { origin: true, headers: ["my-header"], credentials: true } }, + port => { + request + .options("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "polling" }) + .end(function(err, res) { + expect(err).to.be(null); + expect(res.status).to.be(204); + expect(res.body).to.be.empty(); + expect(res.header["access-control-allow-origin"]).to.be( + "http://engine.io" + ); + expect(res.header["access-control-allow-methods"]).to.be( + "GET,HEAD,PUT,PATCH,POST,DELETE" + ); + expect(res.header["access-control-allow-headers"]).to.be( + "my-header" + ); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + done(); + }); + } + ); }); - it("should not handle OPTIONS requests", function(done) { - listen({ handlePreflightRequest: false }, function(port) { - request - .options("http://localhost:%d/engine.io/default/".s(port)) - .set("Origin", "http://engine.io") - .query({ transport: "polling" }) - .end(function(err, res) { - expect(err).to.be.an(Error); - expect(res.status).to.be(501); - expect(res.body.code).to.be(undefined); - done(); - }); - }); + it("should allow CORS from the current origin (actual request)", done => { + listen( + { cors: { origin: true, headers: ["my-header"], credentials: true } }, + port => { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "polling" }) + .end(function(err, res) { + expect(err).to.be(null); + expect(res.status).to.be(200); + expect(res.body).to.be.empty(); + expect(res.header["access-control-allow-origin"]).to.be( + "http://engine.io" + ); + expect(res.header["access-control-allow-methods"]).to.be( + undefined + ); + expect(res.header["access-control-allow-headers"]).to.be( + undefined + ); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + done(); + }); + } + ); }); - it("should handle OPTIONS requests with the given function", function(done) { - var handlePreflightRequest = function(req, res) { - var headers = {}; - if (req.headers.origin) { - headers["Access-Control-Allow-Credentials"] = "true"; - headers["Access-Control-Allow-Origin"] = req.headers.origin; - } else { - headers["Access-Control-Allow-Origin"] = "*"; + it("should disallow CORS from a bad origin", done => { + listen( + { + cors: { + origin: ["http://good-domain.com"] + } + }, + port => { + request + .options("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://bad-domain.com") + .query({ transport: "polling" }) + .end(function(err, res) { + expect(err).to.be(null); + expect(res.status).to.be(204); + expect(res.body).to.be.empty(); + expect(res.header["access-control-allow-origin"]).to.be( + undefined + ); + expect(res.header["access-control-allow-credentials"]).to.be( + undefined + ); + done(); + }); } - headers["Access-Control-Allow-Methods"] = - "GET,HEAD,PUT,PATCH,POST,DELETE"; - headers["Access-Control-Allow-Headers"] = - "origin, content-type, accept"; - res.writeHead(200, headers); - res.end(); - }; - listen({ handlePreflightRequest: handlePreflightRequest }, function( - port - ) { - request - .options("http://localhost:%d/engine.io/default/".s(port)) - .set("Origin", "http://engine.io") - .query({ transport: "polling" }) - .end(function(err, res) { - expect(err).to.be(null); - expect(res.status).to.be(200); - expect(res.body).to.be.empty(); - expect(res.header["access-control-allow-credentials"]).to.be( - "true" - ); - expect(res.header["access-control-allow-origin"]).to.be( - "http://engine.io" - ); - expect(res.header["access-control-allow-methods"]).to.be( - "GET,HEAD,PUT,PATCH,POST,DELETE" - ); - expect(res.header["access-control-allow-headers"]).to.be( - "origin, content-type, accept" - ); - done(); - }); - }); + ); + }); + + it("should forward the configuration to the cors module", done => { + listen( + { + cors: { + origin: "http://good-domain.com", + methods: ["GET", "PUT", "POST"], + allowedHeaders: ["my-header"], + exposedHeaders: ["my-exposed-header"], + credentials: true, + maxAge: 123, + optionsSuccessStatus: 200 + } + }, + port => { + request + .options("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://good-domain.com") + .query({ transport: "polling" }) + .end(function(err, res) { + expect(err).to.be(null); + expect(res.status).to.be(200); + expect(res.body).to.be.empty(); + expect(res.header["access-control-allow-origin"]).to.be( + "http://good-domain.com" + ); + expect(res.header["access-control-allow-methods"]).to.be( + "GET,PUT,POST" + ); + expect(res.header["access-control-allow-headers"]).to.be( + "my-header" + ); + expect(res.header["access-control-expose-headers"]).to.be( + "my-exposed-header" + ); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + expect(res.header["access-control-max-age"]).to.be("123"); + done(); + }); + } + ); }); }); From 734f9d1268840722c41219e69eb58318e0b2ac6b Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 11 Feb 2020 07:57:29 +0100 Subject: [PATCH 025/124] feat: decrease the default value of maxHttpBufferSize 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. --- lib/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server.js b/lib/server.js index 527c7a0f9..9b30a4abe 100644 --- a/lib/server.js +++ b/lib/server.js @@ -26,7 +26,7 @@ class Server extends EventEmitter { pingTimeout: 5000, pingInterval: 25000, upgradeTimeout: 10000, - maxHttpBufferSize: 10e7, + maxHttpBufferSize: 1e6, transports: Object.keys(transports), allowUpgrades: true, perMessageDeflate: { From b27215dceee9d1333d87c3446215c68b3fc85145 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 12 Feb 2020 07:48:19 +0100 Subject: [PATCH 026/124] chore(release): 4.0.0-alpha.0 --- CHANGELOG.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 19 ++++++++++--- package.json | 4 +-- 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..4e6f65b5f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,69 @@ +# [4.0.0-alpha.0](https://github.com/socketio/engine.io/compare/3.4.0...v4.0.0-alpha.0) (2020-02-12) + + +### Features + +* decrease the default value of maxHttpBufferSize ([734f9d1](https://github.com/socketio/engine.io/commit/734f9d1268840722c41219e69eb58318e0b2ac6b)) +* disable cookie by default and add sameSite attribute ([a374471](https://github.com/socketio/engine.io/commit/a374471d06e3681a769766a1d068898182f9305f)), closes [/github.com/jshttp/cookie#options-1](https://github.com//github.com/jshttp/cookie/issues/options-1) +* generateId method can now return a Promise ([f3c291f](https://github.com/socketio/engine.io/commit/f3c291fa613a9d50c924d74293035737fdace4f2)) +* reverse the ping-pong mechanism ([31ff875](https://github.com/socketio/engine.io/commit/31ff87593f231b86dc47ec5761936439ebd53c20)) +* use the cors module to handle cross-origin requests ([61b9492](https://github.com/socketio/engine.io/commit/61b949259ed966ef6fc8bfd61f14d1a2ef06d319)) + + +### BREAKING CHANGES + +* the handlePreflightRequest option is removed by the change. + +Before: + +``` +new Server({ + handlePreflightRequest: (req, res) => { + res.writeHead(200, { + "Access-Control-Allow-Origin": 'https://example.com', + "Access-Control-Allow-Methods": 'GET', + "Access-Control-Allow-Headers": 'Authorization', + "Access-Control-Allow-Credentials": true + }); + res.end(); + } +}) +``` + +After: + +``` +new Server({ + cors: { + origin: "https://example.com", + methods: ["GET"], + allowedHeaders: ["Authorization"], + credentials: true + } +}) +``` +* the syntax has changed from + +``` +new Server({ + cookieName: "test", + cookieHttpOnly: false, + cookiePath: "/custom" +}) +``` + +to + +``` +new Server({ + cookie: { + name: "test", + httpOnly: false, + path: "/custom" + } +}) +``` + +All other options (domain, maxAge, sameSite, ...) are now supported. + +* v3.x clients will not be able to connect anymore (they will send a ping packet and timeout while waiting for a pong packet). diff --git a/package-lock.json b/package-lock.json index 917a71cbe..a047efd0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "3.4.0", + "version": "4.0.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -537,6 +537,18 @@ "yeast": "0.1.2" }, "dependencies": { + "engine.io-parser": { + "version": "git+https://github.com/socketio/engine.io-parser.git#7f5a903f91c4851e5807595adc50964474260048", + "from": "git+https://github.com/socketio/engine.io-parser.git#v4", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, "ws": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", @@ -549,8 +561,9 @@ } }, "engine.io-parser": { - "version": "git+https://github.com/socketio/engine.io-parser.git#a947ae59a2844e4041db58ff36b270d1528b3bee", - "from": "git+https://github.com/socketio/engine.io-parser.git#v4", + "version": "4.0.0-alpha.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.0-alpha.0.tgz", + "integrity": "sha512-KhJdJGo1u5t/I2YvRRYnVZtL0hgGON2tchScqhd5NNmAx4CgFzY0COyU/bAQe82fFkn9lAjgFW9RnAtuN0+lVA==", "requires": { "after": "0.8.2", "arraybuffer.slice": "~0.0.7", diff --git a/package.json b/package.json index 83e7b766e..ac48d6f69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "3.4.0", + "version": "4.0.0-alpha.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io", "author": "Guillermo Rauch ", @@ -30,7 +30,7 @@ "cookie": "0.3.1", "cors": "~2.8.5", "debug": "~4.1.0", - "engine.io-parser": "git+https://github.com/socketio/engine.io-parser.git#v4", + "engine.io-parser": "~4.0.0-alpha.0", "ws": "^7.1.2" }, "devDependencies": { From 71ece3ebf6d2ce0e92f6824a07707b1a77beb57c Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 12 Feb 2020 08:31:57 +0100 Subject: [PATCH 027/124] chore(release): 4.0.0-alpha.1 --- CHANGELOG.md | 15 +++++++++++++++ package-lock.json | 34 ++++++++++------------------------ package.json | 4 ++-- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6f65b5f..b774e97f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# [4.0.0-alpha.1](https://github.com/socketio/engine.io/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2020-02-12) + +#### Links + +- Server release: [v4.0.0-alpha.1](https://github.com/socketio/engine.io-client/releases/tag/v4.0.0-alpha.1) +- ws version: [^7.1.2](https://github.com/websockets/ws/releases/tag/7.1.2) + + + # [4.0.0-alpha.0](https://github.com/socketio/engine.io/compare/3.4.0...v4.0.0-alpha.0) (2020-02-12) @@ -67,3 +76,9 @@ new Server({ All other options (domain, maxAge, sameSite, ...) are now supported. * v3.x clients will not be able to connect anymore (they will send a ping packet and timeout while waiting for a pong packet). + +#### Links + +- Server release: [v4.0.0-alpha.0](https://github.com/socketio/engine.io-client/releases/tag/v4.0.0-alpha.0) +- ws version: [^7.1.2](https://github.com/websockets/ws/releases/tag/7.1.2) + diff --git a/package-lock.json b/package-lock.json index a047efd0a..2e1472484 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -521,42 +521,28 @@ } }, "engine.io-client": { - "version": "git+https://github.com/socketio/engine.io-client.git#81d7171c6bb4053c802e3cc4b29a0e42dcf9c065", - "from": "git+https://github.com/socketio/engine.io-client.git#v4", + "version": "4.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.0-alpha.1.tgz", + "integrity": "sha512-mvmlG8oO65nRU4ayvD+1eKCx3jqqqoYJkFPjWAvZ2ozocXj9K/ord4nf6yVNj1PGHT1E8NNs81Tx6Nz6GFyZKA==", "dev": true, "requires": { "component-emitter": "1.2.1", "debug": "~4.1.0", - "engine.io-parser": "git+https://github.com/socketio/engine.io-parser.git#v4", + "engine.io-parser": "~4.0.0-alpha.0", "has-cors": "1.1.0", "indexof": "0.0.1", "parseqs": "0.0.5", "parseuri": "0.0.5", - "ws": "~6.1.0", + "ws": "~7.2.1", "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" }, "dependencies": { - "engine.io-parser": { - "version": "git+https://github.com/socketio/engine.io-parser.git#7f5a903f91c4851e5807595adc50964474260048", - "from": "git+https://github.com/socketio/engine.io-parser.git#v4", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, "ws": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", - "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz", + "integrity": "sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A==", + "dev": true } } }, diff --git a/package.json b/package.json index ac48d6f69..43567360d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io", "author": "Guillermo Rauch ", @@ -35,7 +35,7 @@ }, "devDependencies": { "babel-eslint": "^8.0.2", - "engine.io-client": "git+https://github.com/socketio/engine.io-client.git#v4", + "engine.io-client": "~4.0.0-alpha.1", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From dcdbccb3dd8a7b7db057d23925356034fcd35d48 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 15 Apr 2020 11:42:31 +0200 Subject: [PATCH 028/124] fix: ignore errors when forcefully closing the socket (#601) In order to catch the following errors: ``` events.js:288 throw er; // Unhandled 'error' event ^ Error: write EPIPE at afterWriteDispatched (internal/stream_base_commons.js:154:25) at writeGeneric (internal/stream_base_commons.js:145:3) at Socket._writeGeneric (net.js:780:11) at Socket._write (net.js:792:8) at doWrite (_stream_writable.js:441:12) at writeOrBuffer (_stream_writable.js:425:5) at Socket.Writable.write (_stream_writable.js:316:11) at abortConnection (/node_modules/engine.io/lib/server.js:506:12) at /node_modules/engine.io/lib/server.js:353:7 at Server.verify (/node_modules/engine.io/lib/server.js:158:14) at Server.handleUpgrade (/node_modules/engine.io/lib/server.js:351:8) ``` Closes https://github.com/socketio/engine.io/issues/596, https://github.com/socketio/engine.io/pull/598 --- lib/server.js | 3 +++ test/server.js | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/server.js b/lib/server.js index 9b30a4abe..3bf47d28f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -503,6 +503,9 @@ function sendErrorMessage(req, res, code) { */ function abortConnection(socket, code) { + socket.on("error", () => { + debug("ignoring error from closed connection"); + }); if (socket.writable) { const message = Server.errorMessages.hasOwnProperty(code) ? Server.errorMessages[code] diff --git a/test/server.js b/test/server.js index a770d7169..9207a5863 100644 --- a/test/server.js +++ b/test/server.js @@ -802,6 +802,27 @@ describe("server", function() { }); }); + it("should abort connection when upgrade fails", done => { + listen({ allowUpgrades: true }, port => { + const req = http.request( + { + port, + path: "/engine.io/", + headers: { + connection: "Upgrade", + upgrade: "websocket" + } + }, + res => { + expect(res.statusCode).to.eql(400); + res.resume(); + res.on("end", done); + } + ); + req.end(); + }); + }); + it( "should trigger if a poll request is ongoing and the underlying " + "socket closes, as in a browser tab close", From 94623c8f8b6dd48caa6316109b0200be1acc53af Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 17 Apr 2020 14:57:35 +0200 Subject: [PATCH 029/124] docs(changelog): include changelog for release 3.4.1 Merged from the 3.4.x branch. --- CHANGELOG.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b774e97f2..b789c7407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,25 @@ +## [3.4.1](https://github.com/socketio/engine.io/compare/3.4.0...3.4.1) (2020-04-17) + + +### Bug Fixes + +* ignore errors when forcefully closing the socket ([da851ec](https://github.com/socketio/engine.io/commit/da851ec4ec89d96df2ee5c711f328b5d795423e9)) +* use SameSite=Strict by default ([001ca62](https://github.com/socketio/engine.io/commit/001ca62cc4a8f511f3b2fbd9e4493ad274a6a0e5)) + +#### Links + +- Diff: [3.4.0...3.4.1](https://github.com/socketio/engine.io/compare/3.4.0...3.4.1) +- Client release: [3.4.1](https://github.com/socketio/engine.io-client/releases/tag/3.4.1) +- ws version: [^7.1.2](https://github.com/websockets/ws/releases/tag/7.1.2) + + + # [4.0.0-alpha.1](https://github.com/socketio/engine.io/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2020-02-12) #### Links -- Server release: [v4.0.0-alpha.1](https://github.com/socketio/engine.io-client/releases/tag/v4.0.0-alpha.1) +- Diff: [v4.0.0-alpha.0...v4.0.0-alpha.1](https://github.com/socketio/engine.io-client/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) +- Client release: [v4.0.0-alpha.1](https://github.com/socketio/engine.io-client/releases/tag/v4.0.0-alpha.1) - ws version: [^7.1.2](https://github.com/websockets/ws/releases/tag/7.1.2) @@ -79,6 +96,7 @@ All other options (domain, maxAge, sameSite, ...) are now supported. #### Links -- Server release: [v4.0.0-alpha.0](https://github.com/socketio/engine.io-client/releases/tag/v4.0.0-alpha.0) +- Diff: [3.4.0...v4.0.0-alpha.0](https://github.com/socketio/engine.io-client/compare/3.4.0...v4.0.0-alpha.0) +- Client release: [v4.0.0-alpha.0](https://github.com/socketio/engine.io-client/releases/tag/v4.0.0-alpha.0) - ws version: [^7.1.2](https://github.com/websockets/ws/releases/tag/7.1.2) From 82cdca23bab0ed69b61b60961900d456a3065e6a Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 4 Jun 2020 14:24:42 +0200 Subject: [PATCH 030/124] fix: remove implicit require of uws So that bundlers like webpack do not try to include it in the build. As a side-effect, any implementation which matches the API of the ws module can now be used. Before that change, you had to explicitly exclude uws: ``` // webpack.config.js module.exports = { // ... externals: { uws: 'uws' } }; ``` Related: https://github.com/socketio/engine.io/issues/575 --- lib/server.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/server.js b/lib/server.js index 3bf47d28f..003642195 100644 --- a/lib/server.js +++ b/lib/server.js @@ -69,17 +69,9 @@ class Server extends EventEmitter { if (this.ws) this.ws.close(); - let wsModule; - switch (this.opts.wsEngine) { - case "uws": - wsModule = require("uws"); - break; - case "ws": - wsModule = require("ws"); - break; - default: - throw new Error("unknown wsEngine"); - } + // add explicit require for bundlers like webpack + const wsModule = + this.opts.wsEngine === "ws" ? require("ws") : require(this.opts.wsEngine); this.ws = new wsModule.Server({ noServer: true, clientTracking: false, From 09708eb307771f65ccca8d9ddb6786ff2177663d Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 4 Jun 2020 16:23:05 +0200 Subject: [PATCH 031/124] docs(changelog): include changelog for release 3.4.2 Merged from the 3.4.x branch. --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b789c7407..9381d35a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## [3.4.2](https://github.com/socketio/engine.io/compare/3.4.1...3.4.2) (2020-06-04) + + +### Bug Fixes + +* remove explicit require of uws ([85e544a](https://github.com/socketio/engine.io/commit/85e544afd95a5890761a613263a5eba0c9a18a93)) + +#### Links + +- Diff: [3.4.1...3.4.2](https://github.com/socketio/engine.io/compare/3.4.1...3.4.2) +- Client release: - +- ws version: [^7.1.2](https://github.com/websockets/ws/releases/tag/7.1.2) + + + ## [3.4.1](https://github.com/socketio/engine.io/compare/3.4.0...3.4.1) (2020-04-17) From 03b49674e682214138fffc23645ce5ddd7a7b973 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 8 Sep 2020 02:47:18 +0200 Subject: [PATCH 032/124] chore: bump cookie version Release: https://github.com/jshttp/cookie/releases/tag/v0.4.0 Diff: https://github.com/jshttp/cookie/compare/v0.3.1...v0.4.1 --- package-lock.json | 47 +++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2e1472484..006d25c2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -128,9 +128,9 @@ } }, "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "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": { @@ -449,9 +449,9 @@ } }, "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "cookiejar": { "version": "2.1.2", @@ -1046,9 +1046,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, "loose-envify": { @@ -1111,18 +1111,18 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "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.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "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": "0.0.8" + "minimist": "^1.2.5" } }, "mocha": { @@ -1172,6 +1172,21 @@ "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=", + "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", diff --git a/package.json b/package.json index 43567360d..0cc2c1552 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "dependencies": { "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "0.3.1", + "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.1.0", "engine.io-parser": "~4.0.0-alpha.0", From ed29e5955de5ab26e50b7b60b2c821bf1d241b3f Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 8 Sep 2020 23:47:46 +0200 Subject: [PATCH 033/124] chore: bump engine.io-parser version See also: https://github.com/socketio/engine.io-protocol#difference-between-v3-and-v4 Release: https://github.com/socketio/engine.io-parser/releases/tag/4.0.0 Diff: https://github.com/socketio/engine.io-parser/compare/2.2.0...4.0.0 --- lib/transports/polling.js | 4 +- package-lock.json | 1722 ------------------------------------- package.json | 4 +- 3 files changed, 4 insertions(+), 1726 deletions(-) delete mode 100644 package-lock.json diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 680f8642c..6ed70a06e 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -185,7 +185,7 @@ class Polling extends Transport { self.onPacket(packet); }; - parser.decodePayload(data, callback); + parser.decodePayload(data).forEach(callback); } /** @@ -218,7 +218,7 @@ class Polling extends Transport { } const self = this; - parser.encodePayload(packets, this.supportsBinary, function(data) { + parser.encodePayload(packets, data => { const compress = packets.some(function(packet) { return packet.options && packet.options.compress; }); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 006d25c2e..000000000 --- a/package-lock.json +++ /dev/null @@ -1,1722 +0,0 @@ -{ - "name": "engine.io", - "version": "4.0.0-alpha.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@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, - "requires": { - "@babel/highlight": "7.0.0-beta.44" - } - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "@babel/helper-get-function-arity": "7.0.0-beta.44", - "@babel/template": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44" - } - }, - "@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, - "requires": { - "@babel/types": "7.0.0-beta.44" - } - }, - "@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, - "requires": { - "@babel/types": "7.0.0-beta.44" - } - }, - "@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, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" - } - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "@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" - }, - "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" - } - } - } - }, - "@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, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.2.0", - "to-fast-properties": "^2.0.0" - } - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "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=" - }, - "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", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "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", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "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" - } - }, - "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", - "integrity": "sha512-aCdHjhzcILdP8c9lej7hvXKvQieyRt20SF102SIGyY4cUIiw6UaAtK4j2o3dXX74jEmy0TJ0CEhv4fTIM3SzcA==", - "dev": true, - "requires": { - "@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" - } - }, - "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 - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" - }, - "base64id": { - "version": "2.0.0", - "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=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" - }, - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "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" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "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", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "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.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "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", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "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, - "requires": { - "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.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "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 - }, - "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", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, - "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", - "dev": true - }, - "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 - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "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.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "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", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "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" - } - }, - "engine.io-client": { - "version": "4.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.0-alpha.1.tgz", - "integrity": "sha512-mvmlG8oO65nRU4ayvD+1eKCx3jqqqoYJkFPjWAvZ2ozocXj9K/ord4nf6yVNj1PGHT1E8NNs81Tx6Nz6GFyZKA==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "engine.io-parser": "~4.0.0-alpha.0", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~7.2.1", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - }, - "dependencies": { - "ws": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz", - "integrity": "sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A==", - "dev": true - } - } - }, - "engine.io-parser": { - "version": "4.0.0-alpha.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.0-alpha.0.tgz", - "integrity": "sha512-KhJdJGo1u5t/I2YvRRYnVZtL0hgGON2tchScqhd5NNmAx4CgFzY0COyU/bAQe82fFkn9lAjgFW9RnAtuN0+lVA==", - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "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, - "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" - }, - "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 - }, - "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" - } - }, - "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" - } - } - } - }, - "eslint-config-prettier": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.9.0.tgz", - "integrity": "sha512-k4E14HBtcLv0uqThaI6I/n1LEqROp8XaPu6SO9Z32u5NlGRC07Enu1Bh2KEFw4FNHbekH8yzbIU9kUGxbiGmCA==", - "dev": true, - "requires": { - "get-stdin": "^6.0.0" - } - }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", - "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.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "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 - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "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", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "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", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "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" - } - }, - "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 - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "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", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "requires": { - "isarray": "2.0.1" - } - }, - "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 - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "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", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "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" - }, - "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-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" - } - } - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "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-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "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": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - }, - "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.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "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.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true - }, - "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, - "requires": { - "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", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "mime-db": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", - "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" - }, - "mime-types": { - "version": "2.1.25", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", - "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", - "requires": { - "mime-db": "1.42.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", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "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", - "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" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "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": "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=", - "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=", - "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==", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - } - } - }, - "ms": { - "version": "2.1.2", - "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", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "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.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "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 - }, - "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", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true - }, - "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 - }, - "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.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz", - "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } - } - }, - "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==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "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", - "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", - "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.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "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" - }, - "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-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" - } - } - } - }, - "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": "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" - } - }, - "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", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "dev": true, - "requires": { - "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" - }, - "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" - } - } - } - }, - "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, - "requires": { - "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", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "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 - }, - "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=", - "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 - }, - "uws": { - "version": "github:mmdevries/uws#0ca2e41cb9337c5bf6bd11e61f9a1fdf3156d56f", - "from": "github:mmdevries/uws#2.4.1", - "dev": true - }, - "vary": { - "version": "1.1.2", - "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.2.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz", - "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==", - "requires": { - "async-limiter": "^1.0.0" - } - }, - "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 - }, - "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", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - } - } -} diff --git a/package.json b/package.json index 0cc2c1552..784a11cc6 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,12 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.1.0", - "engine.io-parser": "~4.0.0-alpha.0", + "engine.io-parser": "~4.0.0", "ws": "^7.1.2" }, "devDependencies": { "babel-eslint": "^8.0.2", - "engine.io-client": "~4.0.0-alpha.1", + "engine.io-client": "github:socketio/engine.io-client#master", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From 14ca7a1cfc1a95c25fef628495982ff3d53400e7 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 9 Sep 2020 02:09:44 +0200 Subject: [PATCH 034/124] chore: restore package-lock.json file The package-lock.json file was removed in the previous commit, in order to sync the client version when upgrading the parser (and make the tests pass). --- package-lock.json | 1643 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1643 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..0cd56db42 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1643 @@ +{ + "name": "engine.io", + "version": "4.0.0-alpha.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@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, + "requires": { + "@babel/highlight": "7.0.0-beta.44" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@babel/helper-get-function-arity": "7.0.0-beta.44", + "@babel/template": "7.0.0-beta.44", + "@babel/types": "7.0.0-beta.44" + } + }, + "@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, + "requires": { + "@babel/types": "7.0.0-beta.44" + } + }, + "@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, + "requires": { + "@babel/types": "7.0.0-beta.44" + } + }, + "@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, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@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" + }, + "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" + } + } + } + }, + "@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, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.2.0", + "to-fast-properties": "^2.0.0" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "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 + } + } + }, + "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", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "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" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "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", + "integrity": "sha512-aCdHjhzcILdP8c9lej7hvXKvQieyRt20SF102SIGyY4cUIiw6UaAtK4j2o3dXX74jEmy0TJ0CEhv4fTIM3SzcA==", + "dev": true, + "requires": { + "@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" + } + }, + "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 + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "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=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "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" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "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", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "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", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "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, + "requires": { + "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", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "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 + }, + "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", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, + "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 + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "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.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "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", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "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" + } + }, + "engine.io-client": { + "version": "github:socketio/engine.io-client#f7ba966e53f4609f755880be8fa504f7252b0817", + "from": "github:socketio/engine.io-client#master", + "dev": true, + "requires": { + "base64-arraybuffer": "0.1.4", + "component-emitter": "~1.3.0", + "debug": "~4.1.0", + "engine.io-parser": "~4.0.0", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.5", + "ws": "~7.2.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "ws": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", + "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==", + "dev": true + } + } + }, + "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, + "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" + }, + "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" + } + } + } + }, + "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" + } + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "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 + }, + "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", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.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 + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "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 + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "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", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "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==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "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", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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" + } + }, + "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 + }, + "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-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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "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 + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "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", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "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.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "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, + "requires": { + "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", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "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==" + }, + "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==", + "requires": { + "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", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "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", + "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" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "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": "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=", + "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=", + "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==", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, + "ms": { + "version": "2.1.2", + "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", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "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", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", + "dev": true + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "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 + }, + "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", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, + "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 + }, + "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", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true + }, + "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, + "requires": { + "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" + } + }, + "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==", + "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", + "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", + "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", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "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", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "dev": true, + "requires": { + "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" + }, + "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" + } + } + } + }, + "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, + "requires": { + "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", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "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 + }, + "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=", + "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 + }, + "uws": { + "version": "github:mmdevries/uws#0ca2e41cb9337c5bf6bd11e61f9a1fdf3156d56f", + "from": "github:mmdevries/uws#2.4.1", + "dev": true + }, + "vary": { + "version": "1.1.2", + "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.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + }, + "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 + }, + "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", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + } + } +} From 1916d3ae62f5ed0a80a853d861e6f4c81103aa6f Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 10 Sep 2020 15:20:00 +0200 Subject: [PATCH 035/124] test: remove Node.js 8 from the test matrix Reference: https://github.com/nodejs/Release --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a93a3f47e..050848eda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ sudo: false language: node_js node_js: - - "8" - "10" - "12" - "13" From 54c67978d5b1f5e61e4183797b9880ea929bab2e Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 10 Sep 2020 15:22:13 +0200 Subject: [PATCH 036/124] docs: update the default value of maxHttpBufferSize Following https://github.com/socketio/engine.io/commit/734f9d1268840722c41219e69eb58318e0b2ac6b --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e77973510..c23dd8d4f 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ to a single process. - `upgradeTimeout` (`Number`): how many ms before an uncompleted transport upgrade is cancelled (`10000`) - `maxHttpBufferSize` (`Number`): how many bytes or characters a message can be, before closing the session (to avoid DoS). Default - value is `10E7`. + value is `1E6`. - `allowRequest` (`Function`): 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 From 078527a384b70dc46d99083fa218be5d45213e51 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 10 Sep 2020 15:43:52 +0200 Subject: [PATCH 037/124] feat: disable perMessageDeflate by default The WebSocket permessage-deflate extension, while useful is some cases, adds some extra memory overhead for each WebSocket connection, and results in huge memory usage in production deployments. It will now be disabled by default. --- README.md | 2 +- lib/server.js | 12 +++++++++--- test/server.js | 37 ++++++++++++++++++++----------------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index c23dd8d4f..246d62f9c 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ to a single process. - `allowUpgrades` (`Boolean`): whether to allow transport upgrades (`true`) - `perMessageDeflate` (`Object|Boolean`): parameters of the WebSocket permessage-deflate extension - (see [ws module](https://github.com/einaros/ws) api docs). Set to `false` to disable. (`true`) + (see [ws module](https://github.com/einaros/ws) api docs). Set to `true` to enable. (defaults to `false`) - `threshold` (`Number`): data is compressed only if the byte size is above this value (`1024`) - `httpCompression` (`Object|Boolean`): parameters of the http compression for the polling transports (see [zlib](http://nodejs.org/api/zlib.html#zlib_options) api docs). Set to `false` to disable. (`true`) diff --git a/lib/server.js b/lib/server.js index 003642195..6b8c747f6 100644 --- a/lib/server.js +++ b/lib/server.js @@ -29,9 +29,6 @@ class Server extends EventEmitter { maxHttpBufferSize: 1e6, transports: Object.keys(transports), allowUpgrades: true, - perMessageDeflate: { - threshold: 1024 - }, httpCompression: { threshold: 1024 }, @@ -56,6 +53,15 @@ class Server extends EventEmitter { this.corsMiddleware = require("cors")(this.opts.cors); } + if (opts.perMessageDeflate) { + this.opts.perMessageDeflate = Object.assign( + { + threshold: 1024 + }, + opts.perMessageDeflate + ); + } + this.init(); } diff --git a/test/server.js b/test/server.js index 9207a5863..fc543f302 100644 --- a/test/server.js +++ b/test/server.js @@ -2810,25 +2810,28 @@ describe("server", function() { }); it("should not compress when the byte size is below threshold", function(done) { - var engine = listen({ transports: ["websocket"] }, function(port) { - engine.on("connection", function(conn) { - var socket = conn.transport.socket; - var send = socket.send; - socket.send = function(data, opts, callback) { - socket.send = send; - socket.send(data, opts, callback); + var engine = listen( + { transports: ["websocket"], perMessageDeflate: true }, + function(port) { + engine.on("connection", function(conn) { + var socket = conn.transport.socket; + var send = socket.send; + socket.send = function(data, opts, callback) { + socket.send = send; + socket.send(data, opts, callback); - expect(opts.compress).to.be(false); - conn.close(); - done(); - }; + expect(opts.compress).to.be(false); + conn.close(); + done(); + }; - var buf = Buffer.allocUnsafe(100); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; - conn.send(buf, { compress: true }); - }); - eioc("http://localhost:%d".s(port), { transports: ["websocket"] }); - }); + var buf = Buffer.allocUnsafe(100); + for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + conn.send(buf, { compress: true }); + }); + eioc("http://localhost:%d".s(port), { transports: ["websocket"] }); + } + ); }); }); From 9df38d57fbc6b6a2f1f12243a7abcd7398ea8fed Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 10 Sep 2020 15:52:32 +0200 Subject: [PATCH 038/124] docs: update the list of supported engines As Node.js 8 is now EOL. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 784a11cc6..6aa5ab68a 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,6 @@ "lib/" ], "engines": { - "node": ">=8.0.0" + "node": ">=10.0.0" } } From 70b1c36be112671b690736de1a24753e8ec5ba22 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 11 Sep 2020 00:59:57 +0200 Subject: [PATCH 039/124] chore(release): 4.0.0 Diff: https://github.com/socketio/engine.io/compare/v4.0.0-alpha.1...4.0.0 Full diff: https://github.com/socketio/engine.io/compare/3.4.0...4.0.0 --- CHANGELOG.md | 22 ++++++++++++++++++++++ package-lock.json | 15 +++++++++++---- package.json | 4 ++-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9381d35a9..cbb276779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +# [4.0.0](https://github.com/socketio/engine.io/compare/v4.0.0-alpha.1...4.0.0) (2020-09-10) + +More details about this release in the blog post: https://socket.io/blog/engine-io-4-release/ + +### Bug Fixes + +* ignore errors when forcefully closing the socket ([#601](https://github.com/socketio/engine.io/issues/601)) ([dcdbccb](https://github.com/socketio/engine.io/commit/dcdbccb3dd8a7b7db057d23925356034fcd35d48)) +* remove implicit require of uws ([82cdca2](https://github.com/socketio/engine.io/commit/82cdca23bab0ed69b61b60961900d456a3065e6a)) + + +### Features + +* disable perMessageDeflate by default ([078527a](https://github.com/socketio/engine.io/commit/078527a384b70dc46d99083fa218be5d45213e51)) + +#### Links + +- Diff: [v4.0.0-alpha.1...4.0.0](https://github.com/socketio/engine.io/compare/v4.0.0-alpha.1...4.0.0) +- Full diff: [3.4.0...4.0.0](https://github.com/socketio/engine.io/compare/3.4.0...4.0.0) +- Client release: [4.0.0](https://github.com/socketio/engine.io-client/releases/tag/4.0.0) +- ws version: [^7.1.2](https://github.com/websockets/ws/releases/tag/7.1.2) + + ## [3.4.2](https://github.com/socketio/engine.io/compare/3.4.1...3.4.2) (2020-06-04) diff --git a/package-lock.json b/package-lock.json index 0cd56db42..4f0f68f35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.0-alpha.1", + "version": "4.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -511,14 +511,15 @@ } }, "engine.io-client": { - "version": "github:socketio/engine.io-client#f7ba966e53f4609f755880be8fa504f7252b0817", - "from": "github:socketio/engine.io-client#master", + "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==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", "component-emitter": "~1.3.0", "debug": "~4.1.0", - "engine.io-parser": "~4.0.0", + "engine.io-parser": "~4.0.1", "has-cors": "1.1.0", "parseqs": "0.0.6", "parseuri": "0.0.5", @@ -527,6 +528,12 @@ "yeast": "0.1.2" }, "dependencies": { + "engine.io-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.1.tgz", + "integrity": "sha512-v5aZK1hlckcJDGmHz3W8xvI3NUHYc9t8QtTbqdR5OaH3S9iJZilPubauOm+vLWOMMWzpE3hiq92l9lTAHamRCg==", + "dev": true + }, "ws": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", diff --git a/package.json b/package.json index 6aa5ab68a..9d37aafa3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.0-alpha.1", + "version": "4.0.0", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io", "author": "Guillermo Rauch ", @@ -35,7 +35,7 @@ }, "devDependencies": { "babel-eslint": "^8.0.2", - "engine.io-client": "github:socketio/engine.io-client#master", + "engine.io-client": "4.0.0", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From 609223959ce7fb177d563fb619d5b61e1cf901fc Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 11 Sep 2020 02:38:05 +0200 Subject: [PATCH 040/124] docs: update latency example The enchilada module fails to bundle the Engine.IO client, so we'll just import the published bundle. The exception seems to come from debug: > Spread must be the final element of an element list --- examples/latency/index.html | 1 + examples/latency/index.js | 26 +- examples/latency/package-lock.json | 1564 ++++++++++++++++++++++++++++ examples/latency/package.json | 6 +- examples/latency/public/index.js | 24 +- 5 files changed, 1596 insertions(+), 25 deletions(-) create mode 100644 examples/latency/package-lock.json diff --git a/examples/latency/index.html b/examples/latency/index.html index a89d95d2a..266548e06 100644 --- a/examples/latency/index.html +++ b/examples/latency/index.html @@ -9,6 +9,7 @@

EIO Latency

(connecting)

+ diff --git a/examples/latency/index.js b/examples/latency/index.js index 2622a1a13..db7ec3d42 100644 --- a/examples/latency/index.js +++ b/examples/latency/index.js @@ -3,28 +3,32 @@ * Module dependencies. */ -var express = require('express'); -var app = express(); -var server = require('http').createServer(app); -var enchilada = require('enchilada'); -var io = require('engine.io').attach(server); +const express = require('express'); +const app = express(); +const server = require('http').createServer(app); +const enchilada = require('enchilada'); +const io = require('engine.io').attach(server); app.use(enchilada({ src: __dirname + '/public', debug: true })); app.use(express.static(__dirname + '/public')); -app.get('/', function (req, res, next) { - res.sendfile('index.html'); +app.get('/', (req, res) => { + res.sendFile(__dirname + '/index.html'); }); -io.on('connection', function (socket) { - socket.on('message', function (v) { +app.get('/engine.io.min.js', (req, res) => { + res.sendFile(require.resolve('engine.io-client/dist/engine.io.min.js')); +}); + +io.on('connection', (socket) => { + socket.on('message', () => { socket.send('pong'); }); }); -var port = process.env.PORT || 3000; -server.listen(port, function () { +const port = process.env.PORT || 3000; +server.listen(port, () => { console.log('\x1B[96mlistening on localhost:' + port + ' \x1B[39m'); }); diff --git a/examples/latency/package-lock.json b/examples/latency/package-lock.json new file mode 100644 index 000000000..df4557f9a --- /dev/null +++ b/examples/latency/package-lock.json @@ -0,0 +1,1564 @@ +{ + "name": "eio-latency", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "Base64": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz", + "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg=" + }, + "JSONStream": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.7.4.tgz", + "integrity": "sha1-c0KQ5BUR7qfCz+FR+/mlY6l7l4Y=", + "requires": { + "jsonparse": "0.0.5", + "through": ">=2.2.7 <3" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "assert": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.1.2.tgz", + "integrity": "sha1-raoExGu1jG3R8pTaPrJuYijrbkQ=", + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "astw": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", + "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", + "requires": { + "acorn": "^4.0.3" + } + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" + }, + "base64-js": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.7.tgz", + "integrity": "sha1-VEANyR1pbOwyqKR5AvlxUi/uj0g=" + }, + "base64id": { + "version": "2.0.0", + "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", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "browser-pack": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-2.0.1.tgz", + "integrity": "sha1-XRxSf1bFgmd0EcTbKhKGSP9r8VA=", + "requires": { + "JSONStream": "~0.6.4", + "combine-source-map": "~0.3.0", + "through": "~2.3.4" + }, + "dependencies": { + "JSONStream": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.6.4.tgz", + "integrity": "sha1-SyyAY/j1Enh7I3X37p22kgj6Lcs=", + "requires": { + "jsonparse": "0.0.5", + "through": "~2.2.7" + }, + "dependencies": { + "through": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", + "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=" + } + } + } + } + }, + "browser-resolve": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.2.4.tgz", + "integrity": "sha1-Wa54IKgpVezTL1+3xGisIcRyOAY=", + "requires": { + "resolve": "0.6.3" + }, + "dependencies": { + "resolve": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", + "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=" + } + } + }, + "browserify": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-4.1.9.tgz", + "integrity": "sha1-xKapETse1HgSNQQxTWVunkZpJps=", + "requires": { + "JSONStream": "~0.7.1", + "assert": "~1.1.0", + "browser-pack": "~2.0.0", + "browser-resolve": "~1.2.1", + "browserify-zlib": "~0.1.2", + "buffer": "^2.3.0", + "builtins": "~0.0.3", + "commondir": "0.0.1", + "concat-stream": "~1.4.1", + "console-browserify": "~1.0.1", + "constants-browserify": "~0.0.1", + "crypto-browserify": "~1.0.9", + "deep-equal": "~0.1.0", + "defined": "~0.0.0", + "deps-sort": "~0.1.1", + "derequire": "~0.8.0", + "domain-browser": "~1.1.0", + "duplexer": "~0.1.1", + "events": "~1.0.0", + "glob": "~3.2.8", + "http-browserify": "~1.3.1", + "https-browserify": "~0.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "~6.0.0", + "module-deps": "~2.1.1", + "os-browserify": "~0.1.1", + "parents": "~0.0.1", + "path-browserify": "~0.0.0", + "process": "^0.7.0", + "punycode": "~1.2.3", + "querystring-es3": "~0.2.0", + "readable-stream": "^1.0.27-1", + "resolve": "~0.7.1", + "shallow-copy": "0.0.1", + "shell-quote": "~0.0.1", + "stream-browserify": "^1.0.0", + "stream-combiner": "~0.0.2", + "string_decoder": "~0.0.0", + "subarg": "0.0.1", + "syntax-error": "~1.1.0", + "through2": "~0.4.1", + "timers-browserify": "~1.0.1", + "tty-browserify": "~0.0.0", + "umd": "~2.1.0", + "url": "~0.10.1", + "util": "~0.10.1", + "vm-browserify": "~0.0.1", + "xtend": "^3.0.0" + } + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "requires": { + "pako": "~0.2.0" + } + }, + "buffer": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-2.8.3.tgz", + "integrity": "sha512-dyatqxbSWlkhnG5lthQ7TDh2NfShsKesnKiGyt5DmiJfvKJ1zBq1AvC3+neSY565BziAiYwbothV2tizAr2WRg==", + "requires": { + "base64-js": "0.0.7", + "ieee754": "^1.1.4", + "is-array": "^1.0.1" + } + }, + "builtins": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-0.0.7.tgz", + "integrity": "sha1-NVIZzWzxjb58Acx/0tznZc/cVJo=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "combine-source-map": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.3.0.tgz", + "integrity": "sha1-2edPWT2c1DgHMSy12EbUUe+qnrc=", + "requires": { + "convert-source-map": "~0.3.0", + "inline-source-map": "~0.3.0", + "source-map": "~0.1.31" + }, + "dependencies": { + "convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=" + } + } + }, + "commondir": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-0.0.1.tgz", + "integrity": "sha1-ifAP3NUbUZxXhzP+xWPmptp/W+I=" + }, + "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==" + }, + "concat-stream": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.11.tgz", + "integrity": "sha512-X3JMh8+4je3U1cQpG87+f9lXHDrqcb2MVLg9L7o8b1UZ0DzhRrUpdn65ttzu10PpJPPI3MQNkis+oha6TSA9Mw==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.9", + "typedarray": "~0.0.5" + } + }, + "console-browserify": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.0.3.tgz", + "integrity": "sha1-04mNLDqTEC82QZf4h0tPkrUoao4=" + }, + "constants-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-0.0.1.tgz", + "integrity": "sha1-kld9tSe6bEzwpFaNhLwDH0QeIfI=" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.1.tgz", + "integrity": "sha1-dOUYJHMFhBOwkN1zd3rLxKD/88w=" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "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=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "crypto-browserify": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", + "integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA=" + }, + "debounce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", + "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==" + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-equal": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz", + "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=" + }, + "defined": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", + "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "deps-sort": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-0.1.2.tgz", + "integrity": "sha1-2qL7YUoXyWN9gB4vVTOa43DzYRo=", + "requires": { + "JSONStream": "~0.6.4", + "minimist": "~0.0.1", + "through": "~2.3.4" + }, + "dependencies": { + "JSONStream": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.6.4.tgz", + "integrity": "sha1-SyyAY/j1Enh7I3X37p22kgj6Lcs=", + "requires": { + "jsonparse": "0.0.5", + "through": "~2.2.7" + }, + "dependencies": { + "through": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", + "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=" + } + } + } + } + }, + "derequire": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/derequire/-/derequire-0.8.0.tgz", + "integrity": "sha1-wffx2izt5Ere3gRzePA/RE6cTA0=", + "requires": { + "esprima-fb": "^3001.1.0-dev-harmony-fb", + "esrefactor": "~0.1.0", + "estraverse": "~1.5.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detective": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-3.1.0.tgz", + "integrity": "sha1-d3gkRKt1K4jKG+Lp0KA5Xx2iXu0=", + "requires": { + "escodegen": "~1.1.0", + "esprima-fb": "3001.1.0-dev-harmony-fb" + } + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "requires": { + "readable-stream": "~1.1.9" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "enchilada": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/enchilada/-/enchilada-0.13.0.tgz", + "integrity": "sha1-Sp5ZqJCwWN0qJ3ukozv763xOwQg=", + "requires": { + "browserify": "4.1.9", + "convert-source-map": "1.1.1", + "debug": "2.2.0", + "filewatcher": "3.0.0", + "mime": "1.2.11", + "ready-signal": "1.3.0", + "uglify-js": "2.5.0" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "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==", + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.1.0", + "engine.io-parser": "~4.0.0", + "ws": "^7.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "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==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "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==", + "requires": { + "base64-arraybuffer": "0.1.4", + "component-emitter": "~1.3.0", + "debug": "~4.1.0", + "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", + "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==", + "requires": { + "ms": "^2.1.1" + } + }, + "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==" + } + } + }, + "engine.io-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.1.tgz", + "integrity": "sha512-v5aZK1hlckcJDGmHz3W8xvI3NUHYc9t8QtTbqdR5OaH3S9iJZilPubauOm+vLWOMMWzpE3hiq92l9lTAHamRCg==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escodegen": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.1.0.tgz", + "integrity": "sha1-xmOSP24gqtSNDA+knzHG1PSTYM8=", + "requires": { + "esprima": "~1.0.4", + "estraverse": "~1.5.0", + "esutils": "~1.0.0", + "source-map": "~0.1.30" + }, + "dependencies": { + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" + } + } + }, + "escope": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/escope/-/escope-0.0.16.tgz", + "integrity": "sha1-QYx6CvynIdr+ZZGT/Zhig+dGU48=", + "requires": { + "estraverse": ">= 0.0.2" + } + }, + "esprima-fb": { + "version": "3001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", + "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=" + }, + "esrefactor": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/esrefactor/-/esrefactor-0.1.0.tgz", + "integrity": "sha1-0UJ5WigjOauB6Ta1t6IbEb8ZexM=", + "requires": { + "escope": "~0.0.13", + "esprima": "~1.0.2", + "estraverse": "~0.0.4" + }, + "dependencies": { + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" + }, + "estraverse": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-0.0.4.tgz", + "integrity": "sha1-AaCTLf7ldGhKWYr1pnw7+bZCjbI=" + } + } + }, + "estraverse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", + "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=" + }, + "esutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", + "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "events": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/events/-/events-1.0.2.tgz", + "integrity": "sha1-dYSdz+k9EPsFfDAFWv29UdBqjiQ=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "filewatcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.0.tgz", + "integrity": "sha1-/HYcHLkG9xWHppu8G1vTiQxQ0P8=", + "requires": { + "debounce": "^1.0.0" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "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=" + }, + "http-browserify": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.3.2.tgz", + "integrity": "sha1-tWLDRHk0mmkNemWX30la76jGBPU=", + "requires": { + "Base64": "~0.2.0", + "inherits": "~2.0.1" + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "inline-source-map": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.3.1.tgz", + "integrity": "sha1-pSi1FOaJ/OkNswiehw2S9Sestes=", + "requires": { + "source-map": "~0.3.0" + }, + "dependencies": { + "source-map": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.3.0.tgz", + "integrity": "sha1-hYb7mloAXltQHiHNGLbyG0V60fk=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "insert-module-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-6.0.0.tgz", + "integrity": "sha1-7orrne4WgZ4zqhRYilWIJK8MFdw=", + "requires": { + "JSONStream": "~0.7.1", + "concat-stream": "~1.4.1", + "lexical-scope": "~1.1.0", + "process": "~0.6.0", + "through": "~2.3.4", + "xtend": "^3.0.0" + }, + "dependencies": { + "process": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/process/-/process-0.6.0.tgz", + "integrity": "sha1-fdm+gP+q7dTLYo8YJ/HLq23AkY8=" + } + } + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-array": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz", + "integrity": "sha1-6YUMwsyGDDvAl36EzPDdRkWEJ5o=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" + }, + "lexical-scope": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.1.1.tgz", + "integrity": "sha1-3rrBBnQ18TWdkPz9npS8su5Hsr8=", + "requires": { + "astw": "^2.0.0" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" + }, + "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==" + }, + "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==", + "requires": { + "mime-db": "1.44.0" + } + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + }, + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + }, + "module-deps": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-2.1.5.tgz", + "integrity": "sha1-N1qbyATM1kzrs8Yu5kN1Xws8zyk=", + "requires": { + "JSONStream": "~0.7.1", + "browser-resolve": "~1.2.4", + "concat-stream": "~1.4.5", + "detective": "~3.1.0", + "duplexer2": "0.0.2", + "inherits": "~2.0.1", + "minimist": "~0.0.9", + "parents": "0.0.2", + "readable-stream": "^1.0.27-1", + "resolve": "~0.6.3", + "stream-combiner": "~0.1.0", + "subarg": "0.0.1", + "through2": "~0.4.1" + }, + "dependencies": { + "parents": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/parents/-/parents-0.0.2.tgz", + "integrity": "sha1-ZxR4JuSX1AdZqvW6TJllm2A00wI=" + }, + "resolve": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", + "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=" + }, + "stream-combiner": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.1.0.tgz", + "integrity": "sha1-DcOJo8ID+PTVY2j5Xd5S65Jptb4=", + "requires": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + } + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "requires": { + "wordwrap": "~0.0.2" + } + }, + "os-browserify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz", + "integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=" + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "parents": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/parents/-/parents-0.0.3.tgz", + "integrity": "sha1-+iEvAk2fpjGNu2tM5nbIvkk7nEM=", + "requires": { + "path-platform": "^0.0.1" + } + }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "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" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-platform": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.0.1.tgz", + "integrity": "sha1-tVhdfDxGPYmqAGDYZhHPGv1hfio=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "process": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/process/-/process-0.7.0.tgz", + "integrity": "sha1-xSIIFho0rfOBI0SuhdPmFQRpOJ0=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "punycode": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.2.4.tgz", + "integrity": "sha1-VACKyXKux0F13vnLpt9/qdORh0A=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + }, + "dependencies": { + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "ready-signal": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ready-signal/-/ready-signal-1.3.0.tgz", + "integrity": "sha1-VUA/kg4eHyqLBloii5pj77Cjwts=" + }, + "resolve": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.7.4.tgz", + "integrity": "sha1-OVqe+ehz+/4SvRRAi9kbuTYAPWk=" + }, + "rfile": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rfile/-/rfile-1.0.0.tgz", + "integrity": "sha1-WXCM+Qyh50xUw8/Fw2/bmBBDUmE=", + "requires": { + "callsite": "~1.0.0", + "resolve": "~0.3.0" + }, + "dependencies": { + "resolve": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.3.1.tgz", + "integrity": "sha1-NMY0R8ZkxwWY0cmxJvxDsqJDEKQ=" + } + } + }, + "ruglify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ruglify/-/ruglify-1.0.0.tgz", + "integrity": "sha1-3Ikw4qlUSidDAcyZcldMDQmGtnU=", + "requires": { + "rfile": "~1.0", + "uglify-js": "~2.2" + }, + "dependencies": { + "uglify-js": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=", + "requires": { + "optimist": "~0.3.5", + "source-map": "~0.1.7" + } + } + } + }, + "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==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" + }, + "shell-quote": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-0.0.1.tgz", + "integrity": "sha1-GkEZbzwDM8SCMjWT1ohuzxU92YY=" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "smoothie": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/smoothie/-/smoothie-1.19.0.tgz", + "integrity": "sha1-kVZqT9PFES3CrJWAAQT6Ruz7XBA=" + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "requires": { + "amdefine": ">=0.0.4" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-1.0.0.tgz", + "integrity": "sha1-v5tKv7QrJ011FHnkTg/yZWtvEZM=", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^1.0.27-1" + } + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "requires": { + "duplexer": "~0.1.1" + } + }, + "string_decoder": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.0.1.tgz", + "integrity": "sha1-9UctCo0WUOyCN1LSTm/WJ7Ob8UE=" + }, + "subarg": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-0.0.1.tgz", + "integrity": "sha1-PVawfaz7xFu7Y/dnK0O2PkY2jjo=", + "requires": { + "minimist": "~0.0.7" + } + }, + "syntax-error": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.1.6.tgz", + "integrity": "sha1-tFSXBtOGzBwdx8JCPxhXm2yt5xA=", + "requires": { + "acorn": "^2.7.0" + }, + "dependencies": { + "acorn": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=" + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~2.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, + "timers-browserify": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.0.3.tgz", + "integrity": "sha1-/7pwycEu7ZFv1nMY5imsbzIpVVE=", + "requires": { + "process": "~0.5.1" + }, + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + } + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uglify-js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.5.0.tgz", + "integrity": "sha1-SrXWWkcw7Lek+2LT9JniBU2Y+6E=", + "requires": { + "async": "~0.2.6", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.5.4" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=" + }, + "umd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/umd/-/umd-2.1.0.tgz", + "integrity": "sha1-SmMHt2LxfwLSAbX6FU5nM5bCY88=", + "requires": { + "rfile": "~1.0.0", + "ruglify": "~1.0.0", + "through": "~2.3.4", + "uglify-js": "~2.4.0" + }, + "dependencies": { + "source-map": { + "version": "0.1.34", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", + "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=", + "requires": { + "amdefine": ">=0.0.4" + } + }, + "uglify-js": { + "version": "2.4.24", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz", + "integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=", + "requires": { + "async": "~0.2.6", + "source-map": "0.1.34", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.5.4" + } + } + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "requires": { + "indexof": "0.0.1" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=" + }, + "yargs": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", + "integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=", + "requires": { + "camelcase": "^1.0.2", + "decamelize": "^1.0.0", + "window-size": "0.1.0", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + } + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + } + } +} diff --git a/examples/latency/package.json b/examples/latency/package.json index c9e60ff1f..2df714738 100644 --- a/examples/latency/package.json +++ b/examples/latency/package.json @@ -2,10 +2,10 @@ "name": "eio-latency", "version": "0.1.0", "dependencies": { - "express": "3.3.5", "enchilada": "0.13.0", - "engine.io": "1.6.0", - "engine.io-client": "1.6.0", + "engine.io": "^4.0.0", + "engine.io-client": "^4.0.0", + "express": "^4.17.1", "smoothie": "1.19.0" } } diff --git a/examples/latency/public/index.js b/examples/latency/public/index.js index d64f8efd2..ea74058e2 100644 --- a/examples/latency/public/index.js +++ b/examples/latency/public/index.js @@ -3,9 +3,8 @@ * Module dependencies. */ -var SmoothieChart = require('smoothie').SmoothieChart; -var TimeSeries = require('smoothie').TimeSeries; -var eio = require('engine.io-client'); +const SmoothieChart = require('smoothie').SmoothieChart; +const TimeSeries = require('smoothie').TimeSeries; // helper @@ -13,8 +12,8 @@ function $ (id) { return document.getElementById(id); } // chart -var smoothie; -var time; +let smoothie; +let time; function render () { if (smoothie) smoothie.stop(); @@ -30,26 +29,29 @@ function render () { } // socket -var socket = new eio.Socket(); -var last; +const socket = new eio.Socket(); +let last; function send () { last = new Date(); socket.send('ping'); $('transport').innerHTML = socket.transport.name; } -socket.on('open', function () { + +socket.on('open', () => { if ($('chart').getContext) { render(); window.onresize = render; } send(); }); -socket.on('close', function () { + +socket.on('close', () => { if (smoothie) smoothie.stop(); $('transport').innerHTML = '(disconnected)'; }); -socket.on('message', function () { - var latency = new Date() - last; + +socket.on('message', () => { + const latency = new Date() - last; $('latency').innerHTML = latency + 'ms'; if (time) time.append(+new Date(), latency); setTimeout(send, 100); From ec83022ef4670af69069ef7ba23e7c407135cde7 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 14 Sep 2020 22:00:40 +0200 Subject: [PATCH 041/124] docs: update examples with ES6 syntax --- README.md | 57 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 246d62f9c..430fedcab 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ cross-browser/cross-device bi-directional communication layer for #### (A) Listening on a port ```js -var engine = require('engine.io'); -var server = engine.listen(80); +const engine = require('engine.io'); +const server = engine.listen(80); -server.on('connection', function(socket){ +server.on('connection', socket => { socket.send('utf 8 string'); socket.send(Buffer.from([0, 1, 2, 3, 4, 5])); // binary data }); @@ -27,31 +27,32 @@ server.on('connection', function(socket){ #### (B) Intercepting requests for a http.Server ```js -var engine = require('engine.io'); -var http = require('http').createServer().listen(3000); -var server = engine.attach(http); +const engine = require('engine.io'); +const http = require('http').createServer().listen(3000); +const server = engine.attach(http); -server.on('connection', function (socket) { - socket.on('message', function(data){ }); - socket.on('close', function(){ }); +server.on('connection', socket => { + socket.on('message', data => { }); + socket.on('close', () => { }); }); ``` #### (C) Passing in requests ```js -var engine = require('engine.io'); -var server = new engine.Server(); +const engine = require('engine.io'); +const server = new engine.Server(); -server.on('connection', function(socket){ +server.on('connection', socket => { socket.send('hi'); }); // … -httpServer.on('upgrade', function(req, socket, head){ +httpServer.on('upgrade', (req, socket, head) => { server.handleUpgrade(req, socket, head); }); -httpServer.on('request', function(req, res){ + +httpServer.on('request', (req, res) => { server.handleRequest(req, res); }); ``` @@ -61,10 +62,10 @@ httpServer.on('request', function(req, res){ ```html ``` @@ -131,21 +132,21 @@ These are exposed by `require('engine.io')`: The following are identical ways to instantiate a server and then attach it. ```js -var httpServer; // previously created with `http.createServer();` from node.js api. +const httpServer; // previously created with `http.createServer();` from node.js api. // create a server first, and then attach -var eioServer = require('engine.io').Server(); +const eioServer = require('engine.io').Server(); eioServer.attach(httpServer); // or call the module as a function to get `Server` -var eioServer = require('engine.io')(); +const eioServer = require('engine.io')(); eioServer.attach(httpServer); // immediately attach -var eioServer = require('engine.io')(httpServer); +const eioServer = require('engine.io')(httpServer); // with custom options -var eioServer = require('engine.io')(httpServer, { +const eioServer = require('engine.io')(httpServer, { maxHttpBufferSize: 1e3 }); ``` @@ -163,8 +164,8 @@ var eioServer = require('engine.io')(httpServer, { - **Returns** `Server` ```js -var engine = require('engine.io'); -var server = engine.listen(3000, { +const engine = require('engine.io'); +const server = engine.listen(3000, { pingTimeout: 2000, pingInterval: 10000 }); @@ -184,9 +185,9 @@ server.on('connection', /* ... */); - **Returns** `Server` a new Server instance. ```js -var engine = require('engine.io'); -var httpServer = require('http').createServer().listen(3000); -var server = engine.attach(httpServer, { +const engine = require('engine.io'); +const httpServer = require('http').createServer().listen(3000); +const server = engine.attach(httpServer, { wsEngine: 'uws' // requires having uws as dependency }); From 428b4f52008d8f61fb7141df48e5cadf3f806f4c Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 14 Sep 2020 22:04:49 +0200 Subject: [PATCH 042/124] docs: update links to other repositories --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 430fedcab..97e843c66 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ httpServer.on('request', (req, res) => { ``` For more information on the client refer to the -[engine-client](http://github.com/learnboost/engine.io-client) repository. +[engine-client](http://github.com/socketio/engine.io-client) repository. ## What features does it have? @@ -532,9 +532,9 @@ has you covered. ### Can I implement `Engine` in other languages? -Absolutely. The [engine.io-protocol](https://github.com/LearnBoost/engine.io-protocol) -repository contains the most up to date description of the specification -at all times, and the parser implementation in JavaScript. +Absolutely. The [engine.io-protocol](https://github.com/socketio/engine.io-protocol) +repository contains the most up-to-date description of the specification +at all times. ## License From a05379b1e87d2e4cde40d3e30b134355883f4108 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 14 Sep 2020 22:07:26 +0200 Subject: [PATCH 043/124] test: use eiows The eiows package is the published version of [1], which is a fork of uws (a performant WebSocket server written in C++ with bindings for Node.js). [1] https://github.com/mmdevries/uws --- .travis.yml | 2 +- package-lock.json | 11 ++++++----- package.json | 6 +++--- test/server.js | 15 +++------------ 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 050848eda..e84f6c753 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: node_js node_js: - "10" - "12" - - "13" + - "14" git: depth: 1 notifications: diff --git a/package-lock.json b/package-lock.json index 4f0f68f35..31d334921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -510,6 +510,12 @@ "esutils": "^2.0.2" } }, + "eiows": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.3.0.tgz", + "integrity": "sha512-nuicpzx+p4+dwrkk/EQsYJU/0Fnh41P7WbG2Dgaldv8ykfvyJQhDESlBizN23q2h/vHmaJH7QWC2jwGzpnthog==", + "dev": true + }, "engine.io-client": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.0.tgz", @@ -1583,11 +1589,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uws": { - "version": "github:mmdevries/uws#0ca2e41cb9337c5bf6bd11e61f9a1fdf3156d56f", - "from": "github:mmdevries/uws#2.4.1", - "dev": true - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 9d37aafa3..dcdaf5467 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ }, "devDependencies": { "babel-eslint": "^8.0.2", + "eiows": "^3.3.0", "engine.io-client": "4.0.0", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", @@ -42,12 +43,11 @@ "mocha": "^4.0.1", "prettier": "^1.19.1", "s": "0.1.1", - "superagent": "^3.8.1", - "uws": "github:mmdevries/uws#2.4.1" + "superagent": "^3.8.1" }, "scripts": { "lint": "eslint lib/ test/ *.js", - "test": "npm run lint && npm run format:check && mocha && EIO_WS_ENGINE=uws mocha", + "test": "npm run lint && npm run format:check && mocha && EIO_WS_ENGINE=eiows mocha", "format:check": "prettier --check 'lib/**/*.js' 'test/**/*.js'", "format:fix": "prettier --write 'lib/**/*.js' 'test/**/*.js'" }, diff --git a/test/server.js b/test/server.js index fc543f302..9f734ace3 100644 --- a/test/server.js +++ b/test/server.js @@ -17,9 +17,6 @@ var expect = require("expect.js"); var request = require("superagent"); var cookieMod = require("cookie"); -// are we running uws wsEngine ? -var UWS_ENGINE = process.env.EIO_WS_ENGINE === "uws"; - /** * Tests. */ @@ -1863,9 +1860,6 @@ describe("server", function() { }); it("should send and receive data with key and cert (ws)", function(done) { - if (UWS_ENGINE) { - return this.skip(); - } var srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -1945,9 +1939,6 @@ describe("server", function() { }); it("should send and receive data with pfx (ws)", function(done) { - if (UWS_ENGINE) { - return this.skip(); - } var srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), @@ -3039,11 +3030,11 @@ describe("server", function() { }); describe("wsEngine option", function() { - it("should allow loading of other websocket server implementation like uws", function(done) { - var engine = listen({ allowUpgrades: false, wsEngine: "uws" }, function( + it("should allow loading of other websocket server implementation like eiows", function(done) { + var engine = listen({ allowUpgrades: false, wsEngine: "eiows" }, function( port ) { - expect(engine.ws instanceof require("uws").Server).to.be.ok(); + expect(engine.ws instanceof require("eiows").Server).to.be.ok(); var socket = new eioc.Socket("ws://localhost:%d".s(port)); engine.on("connection", function(conn) { conn.send("a"); From f9c0e74b14a320450fec72d6c48714145c4f5e3c Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 5 Oct 2020 01:03:28 +0200 Subject: [PATCH 044/124] chore: bump eiows to 3.3.2 The CI was failing with Node.js 14.13.0. --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 31d334921..7352518e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -511,9 +511,9 @@ } }, "eiows": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.3.0.tgz", - "integrity": "sha512-nuicpzx+p4+dwrkk/EQsYJU/0Fnh41P7WbG2Dgaldv8ykfvyJQhDESlBizN23q2h/vHmaJH7QWC2jwGzpnthog==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/eiows/-/eiows-3.3.2.tgz", + "integrity": "sha512-7cwZl/itR8NP6YXObVo583sxQcoKdcpeuRZqelnuqlQZvJH7oBSVvMRq8vZQkL4IXCXJPL8iC2M6haSawmtOsg==", "dev": true }, "engine.io-client": { From fe093bae1adce99e01dfdd3ce7542957785098b5 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 21 Oct 2020 00:46:32 +0200 Subject: [PATCH 045/124] fix: do not overwrite CORS headers upon error The Access-Control-Allow-xxx headers added by the cors middleware were overwritten when sending an error response. Those lines should have been removed in [1]. [1]: https://github.com/socketio/engine.io/commit/61b949259ed966ef6fc8bfd61f14d1a2ef06d319 Related: https://github.com/socketio/engine.io/issues/605 --- lib/server.js | 6 ----- test/server.js | 62 +++++++++++++++++++------------------------------- 2 files changed, 24 insertions(+), 44 deletions(-) diff --git a/lib/server.js b/lib/server.js index 6b8c747f6..6033b0b04 100644 --- a/lib/server.js +++ b/lib/server.js @@ -475,12 +475,6 @@ function sendErrorMessage(req, res, code) { ); return; } - if (req.headers.origin) { - headers["Access-Control-Allow-Credentials"] = "true"; - headers["Access-Control-Allow-Origin"] = req.headers.origin; - } else { - headers["Access-Control-Allow-Origin"] = "*"; - } if (res !== undefined) { res.writeHead(400, headers); res.end( diff --git a/test/server.js b/test/server.js index 9f734ace3..aeef78877 100644 --- a/test/server.js +++ b/test/server.js @@ -33,7 +33,6 @@ describe("server", function() { expect(res.status).to.be(400); expect(res.body.code).to.be(0); expect(res.body.message).to.be("Transport unknown"); - expect(res.header["access-control-allow-origin"]).to.be("*"); done(); }); }); @@ -51,12 +50,6 @@ describe("server", function() { expect(res.status).to.be(400); expect(res.body.code).to.be(0); expect(res.body.message).to.be("Transport unknown"); - expect(res.header["access-control-allow-credentials"]).to.be( - "true" - ); - expect(res.header["access-control-allow-origin"]).to.be( - "http://engine.io" - ); done(); }); }); @@ -73,12 +66,6 @@ describe("server", function() { expect(res.status).to.be(400); expect(res.body.code).to.be(1); expect(res.body.message).to.be("Session ID unknown"); - expect(res.header["access-control-allow-credentials"]).to.be( - "true" - ); - expect(res.header["access-control-allow-origin"]).to.be( - "http://engine.io" - ); done(); }); }); @@ -101,12 +88,6 @@ describe("server", function() { expect(res.status).to.be(403); expect(res.body.code).to.be(4); expect(res.body.message).to.be("Thou shall not pass"); - expect(res.header["access-control-allow-credentials"]).to.be( - undefined - ); - expect(res.header["access-control-allow-origin"]).to.be( - undefined - ); done(); }); } @@ -488,25 +469,30 @@ describe("server", function() { }); it("should disallow bad requests", function(done) { - listen(function(port) { - request - .get("http://localhost:%d/engine.io/default/".s(port)) - .set("Origin", "http://engine.io") - .query({ transport: "websocket" }) - .end(function(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"); - expect(res.header["access-control-allow-credentials"]).to.be( - "true" - ); - expect(res.header["access-control-allow-origin"]).to.be( - "http://engine.io" - ); - done(); - }); - }); + listen( + { + cors: { credentials: true, origin: "http://engine.io" } + }, + function(port) { + request + .get("http://localhost:%d/engine.io/default/".s(port)) + .set("Origin", "http://engine.io") + .query({ transport: "websocket" }) + .end(function(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"); + expect(res.header["access-control-allow-credentials"]).to.be( + "true" + ); + expect(res.header["access-control-allow-origin"]).to.be( + "http://engine.io" + ); + done(); + }); + } + ); }); it("should send a packet along with the handshake", function(done) { From c099338e04bb5a93b8997c871c27d238455d2deb Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 21 Oct 2020 23:49:16 +0200 Subject: [PATCH 046/124] refactor: remove binary handling for the polling transport Since Engine.IO v4, the binary payloads are always encoded as base64 with the polling transport. See https://github.com/socketio/engine.io-protocol#difference-between-v3-and-v4 --- lib/transports/polling.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 6ed70a06e..c49583ab3 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -109,12 +109,10 @@ class Polling extends Transport { return; } - const isBinary = "application/octet-stream" === req.headers["content-type"]; - this.dataReq = req; this.dataRes = res; - let chunks = isBinary ? Buffer.concat([]) : ""; + let chunks = ""; const self = this; function cleanup() { @@ -131,16 +129,11 @@ class Polling extends Transport { function onData(data) { let contentLength; - if (isBinary) { - chunks = Buffer.concat([chunks, data]); - contentLength = chunks.length; - } else { - chunks += data; - contentLength = Buffer.byteLength(chunks); - } + chunks += data; + contentLength = Buffer.byteLength(chunks); if (contentLength > self.maxHttpBufferSize) { - chunks = isBinary ? Buffer.concat([]) : ""; + chunks = ""; req.connection.destroy(); } } @@ -161,7 +154,7 @@ class Polling extends Transport { } req.on("close", onClose); - if (!isBinary) req.setEncoding("utf8"); + req.setEncoding("utf8"); req.on("data", onData); req.on("end", onEnd); } From 58943c3b3079c7ed3b01ee175fb303d55204ee95 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 22 Oct 2020 00:30:51 +0200 Subject: [PATCH 047/124] chore(release): 4.0.1 Diff: https://github.com/socketio/engine.io/compare/4.0.0...4.0.1 --- CHANGELOG.md | 9 +++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbb276779..38cd9a403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [4.0.1](https://github.com/socketio/engine.io/compare/4.0.0...4.0.1) (2020-10-21) + + +### Bug Fixes + +* do not overwrite CORS headers upon error ([fe093ba](https://github.com/socketio/engine.io/commit/fe093bae1adce99e01dfdd3ce7542957785098b5)) + + + # [4.0.0](https://github.com/socketio/engine.io/compare/v4.0.0-alpha.1...4.0.0) (2020-09-10) More details about this release in the blog post: https://socket.io/blog/engine-io-4-release/ diff --git a/package-lock.json b/package-lock.json index 7352518e7..9d699882d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.0", + "version": "4.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index dcdaf5467..a7715cff9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.0", + "version": "4.0.1", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", "main": "lib/engine.io", "author": "Guillermo Rauch ", From 3284208a6d7b645ca9e0468df6f757ebad94e712 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 22 Oct 2020 00:36:45 +0200 Subject: [PATCH 048/124] chore: bump engine.io-client --- package-lock.json | 34 ++++++++-------------------------- package.json | 2 +- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d699882d..b19beebb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -288,15 +288,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=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -328,12 +319,6 @@ "callsites": "^0.2.0" } }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, "callsites": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", @@ -517,9 +502,9 @@ "dev": true }, "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.0.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.1.tgz", + "integrity": "sha512-3XXfWrEutlf1vg5PlS805bD+AgZXhRIKYAG04f1iCGOs70dWEYlZGfCZUNwPwNx05lBCKs1lIeL3SkLB0P++xw==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", @@ -528,7 +513,7 @@ "engine.io-parser": "~4.0.1", "has-cors": "1.1.0", "parseqs": "0.0.6", - "parseuri": "0.0.5", + "parseuri": "0.0.6", "ws": "~7.2.1", "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" @@ -1236,13 +1221,10 @@ "dev": true }, "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", + "dev": true }, "path-is-absolute": { "version": "1.0.1", diff --git a/package.json b/package.json index a7715cff9..fee66d5a5 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.3.0", - "engine.io-client": "4.0.0", + "engine.io-client": "4.0.1", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From 17b8c2f199e7a307b6d6294b8599abacb3ec56e7 Mon Sep 17 00:00:00 2001 From: Andrew Stoker Date: Sat, 7 Nov 2020 20:00:54 -0500 Subject: [PATCH 049/124] fix: add extension in the package.json main entry (#608) The `main` entry for the `package.json` file is for defining the entry point. The entry is currently missing the required `.js` extension, this PR brings it back. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fee66d5a5..5bbc38c11 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "engine.io", "version": "4.0.1", "description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server", - "main": "lib/engine.io", + "main": "lib/engine.io.js", "author": "Guillermo Rauch ", "homepage": "https://github.com/socketio/engine.io", "contributors": [ From 16fd658274e59ddacfa9578d7d5c8f5891cd24bf Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 9 Nov 2020 10:03:04 +0100 Subject: [PATCH 050/124] chore(release): 4.0.2 Diff: https://github.com/socketio/engine.io/compare/4.0.1...4.0.2 --- CHANGELOG.md | 8 ++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38cd9a403..5aaf1826d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [4.0.2](https://github.com/socketio/engine.io/compare/4.0.1...4.0.2) (2020-11-09) + + +### Bug Fixes + +* add extension in the package.json main entry ([#608](https://github.com/socketio/engine.io/issues/608)) ([17b8c2f](https://github.com/socketio/engine.io/commit/17b8c2f199e7a307b6d6294b8599abacb3ec56e7)) + + ## [4.0.1](https://github.com/socketio/engine.io/compare/4.0.0...4.0.1) (2020-10-21) diff --git a/package-lock.json b/package-lock.json index b19beebb8..90883fe19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.1", + "version": "4.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5bbc38c11..970f2f49e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.1", + "version": "4.0.2", "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 ", From 9ddccf398eaa807414e79733f0bd3d29994f39aa Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 9 Nov 2020 10:11:20 +0100 Subject: [PATCH 051/124] chore: bump engine.io-client --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 90883fe19..154cb54c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -502,9 +502,9 @@ "dev": true }, "engine.io-client": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.1.tgz", - "integrity": "sha512-3XXfWrEutlf1vg5PlS805bD+AgZXhRIKYAG04f1iCGOs70dWEYlZGfCZUNwPwNx05lBCKs1lIeL3SkLB0P++xw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.2.tgz", + "integrity": "sha512-cfzFu0u7rr/Gmz/CefwZ6mBj9kxtsOtOavV/YLbn+2sPGE1ZTSWh3tj8427a0od+BK27zsWDpnDx98fnpnmksA==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", diff --git a/package.json b/package.json index 970f2f49e..05cbbd3f1 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "devDependencies": { "babel-eslint": "^8.0.2", "eiows": "^3.3.0", - "engine.io-client": "4.0.1", + "engine.io-client": "4.0.2", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From 67ca12c53366f9a08d4f0671fa124776a9136b5a Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 17 Nov 2020 10:06:52 +0100 Subject: [PATCH 052/124] chore(release): 4.0.3 Diff: https://github.com/socketio/engine.io/compare/4.0.2...4.0.3 --- CHANGELOG.md | 4 ++++ package-lock.json | 8 ++++---- package.json | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aaf1826d..5a7eb549c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [4.0.3](https://github.com/socketio/engine.io/compare/4.0.2...4.0.3) (2020-11-17) + +No change on the server-side, this matches the client release. + ## [4.0.2](https://github.com/socketio/engine.io/compare/4.0.1...4.0.2) (2020-11-09) diff --git a/package-lock.json b/package-lock.json index 154cb54c5..37ea8f4e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.2", + "version": "4.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -502,9 +502,9 @@ "dev": true }, "engine.io-client": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.2.tgz", - "integrity": "sha512-cfzFu0u7rr/Gmz/CefwZ6mBj9kxtsOtOavV/YLbn+2sPGE1ZTSWh3tj8427a0od+BK27zsWDpnDx98fnpnmksA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.3.tgz", + "integrity": "sha512-2U6dZkOrMGpVh11l+4pMXHMDnirnzIPIYfugEUpnAl0/3wBb+GO7euu0TT3Nn8E5hgaWSpUA+XOfrch2uZdyGA==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", diff --git a/package.json b/package.json index 05cbbd3f1..e6fb5a217 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.2", + "version": "4.0.3", "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.0.2", + "engine.io-client": "4.0.3", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From c2981c60403e21b6120cd13f506fcfaffcdf7c2d Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 17 Nov 2020 22:51:56 +0100 Subject: [PATCH 053/124] chore(release): 4.0.4 Diff: https://github.com/socketio/engine.io/compare/4.0.3...4.0.4 --- CHANGELOG.md | 4 ++++ package-lock.json | 8 ++++---- package.json | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a7eb549c..890fca972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [4.0.4](https://github.com/socketio/engine.io/compare/4.0.3...4.0.4) (2020-11-17) + +No change on the server-side, this matches the client release. + ## [4.0.3](https://github.com/socketio/engine.io/compare/4.0.2...4.0.3) (2020-11-17) No change on the server-side, this matches the client release. diff --git a/package-lock.json b/package-lock.json index 37ea8f4e5..f77d695a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.3", + "version": "4.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -502,9 +502,9 @@ "dev": true }, "engine.io-client": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.3.tgz", - "integrity": "sha512-2U6dZkOrMGpVh11l+4pMXHMDnirnzIPIYfugEUpnAl0/3wBb+GO7euu0TT3Nn8E5hgaWSpUA+XOfrch2uZdyGA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.4.tgz", + "integrity": "sha512-and4JRvjv+BQ4WBLopYUFePxju3ms3aBRk0XjaLdh/t9TKv2LCKtKKWFRoRzIfUZsu3U38FcYqNLuXhfS16vqw==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", diff --git a/package.json b/package.json index e6fb5a217..f02b4ce59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.3", + "version": "4.0.4", "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.0.3", + "engine.io-client": "4.0.4", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From 312bd356c743d1df0bd5b62faba42feb5cb8dda0 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sat, 5 Dec 2020 01:22:13 +0100 Subject: [PATCH 054/124] ci: migrate to GitHub Actions Due to the recent changes to the Travis CI platform (see [1]), we will now use GitHub Actions to run the tests. Reference: https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-nodejs [1]: https://blog.travis-ci.com/2020-11-02-travis-ci-new-billing --- .github/workflows/ci.yml | 26 ++++++++++++++++++++++++++ .travis.yml | 10 ---------- README.md | 2 +- test/server.js | 2 +- 4 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..4e1fe7b25 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: CI + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * 0' + +jobs: + test-node: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [10.x, 12.x, 14.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm test + env: + CI: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e84f6c753..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -sudo: false -language: node_js -node_js: - - "10" - - "12" - - "14" -git: - depth: 1 -notifications: - irc: "irc.freenode.org#socket.io" diff --git a/README.md b/README.md index 97e843c66..62c800e1b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Engine.IO: the realtime engine -[![Build Status](https://travis-ci.org/socketio/engine.io.svg?branch=master)](http://travis-ci.org/socketio/engine.io) +[![Build Status](https://github.com/socketio/engine.io/workflows/CI/badge.svg)](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 diff --git a/test/server.js b/test/server.js index aeef78877..63b645576 100644 --- a/test/server.js +++ b/test/server.js @@ -1059,7 +1059,7 @@ describe("server", function() { it("should trigger transport close before open for ws", function(done) { var opts = { transports: ["websocket"] }; listen(opts, function(port) { - var url = "ws://%s:%d".s("0.0.0.50", port); + var url = "ws://%s:%d".s("0.0.0.0", port); var socket = new eioc.Socket(url); socket.on("open", function() { done(new Error("Test invalidation")); From f5efa1e02a94c72f44f3273e1ca9964426a1ed0c Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 7 Dec 2020 10:57:28 +0100 Subject: [PATCH 055/124] refactor: use ES6 syntax for the tests --- test/common.js | 10 +- test/engine.io.js | 160 +- test/fixtures/server-close-upgraded.js | 10 +- test/fixtures/server-close-upgrading.js | 10 +- test/fixtures/server-close.js | 10 +- test/jsonp.js | 189 ++- test/server.js | 1792 +++++++++++------------ 7 files changed, 1081 insertions(+), 1100 deletions(-) diff --git a/test/common.js b/test/common.js index 14845c7ff..e55222934 100644 --- a/test/common.js +++ b/test/common.js @@ -1,20 +1,16 @@ -/** - * Module dependencies. - */ - -var eio = require(".."); +const eio = require(".."); /** * Listen shortcut that fires a callback on an ephemeral port. */ -exports.listen = function(opts, fn) { +exports.listen = (opts, fn) => { if ("function" === typeof opts) { fn = opts; opts = {}; } - var e = eio.listen(0, opts, function() { + const e = eio.listen(0, opts, () => { fn(e.httpServer.address().port); }); diff --git a/test/engine.io.js b/test/engine.io.js index 95f0449a1..121754ccf 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -1,40 +1,36 @@ -/** - * Test dependencies. - */ - -var net = require("net"); -var eio = require(".."); -var listen = require("./common").listen; -var expect = require("expect.js"); -var request = require("superagent"); -var http = require("http"); +const net = require("net"); +const eio = require(".."); +const listen = require("./common").listen; +const expect = require("expect.js"); +const request = require("superagent"); +const http = require("http"); /** * Tests. */ -describe("engine", function() { - it("should expose protocol number", function() { +describe("engine", () => { + it("should expose protocol number", () => { expect(eio.protocol).to.be.a("number"); }); - it("should be the same version as client", function() { - var version = require("../package").version; + it("should be the same version as client", () => { + const version = require("../package").version; expect(version).to.be(require("engine.io-client/package").version); }); - describe("engine()", function() { - it("should create a Server when require called with no arguments", function() { - var engine = eio(); + describe("engine()", () => { + it("should create a Server when require called with no arguments", () => { + const engine = eio(); expect(engine).to.be.an(eio.Server); expect(engine.ws).to.be.ok(); }); }); - describe("listen", function() { - it("should open a http server that returns 501", function(done) { - listen(function(port) { - request.get("http://localhost:%d/".s(port), function(err, res) { + describe("listen", () => { + it("should open a http server that returns 501", done => { + listen(port => { + request.get("http://localhost:%d/".s(port), (err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(501); done(); @@ -43,30 +39,30 @@ describe("engine", function() { }); }); - describe("attach()", function() { - it("should work from require()", function() { - var server = http.createServer(); - var engine = eio(server); + describe("attach()", () => { + it("should work from require()", () => { + const server = http.createServer(); + const engine = eio(server); expect(engine).to.be.an(eio.Server); }); - it("should return an engine.Server", function() { - var server = http.createServer(); - var engine = eio.attach(server); + it("should return an engine.Server", () => { + const server = http.createServer(); + const engine = eio.attach(server); expect(engine).to.be.an(eio.Server); }); - it("should attach engine to an http server", function(done) { - var server = http.createServer(); + it("should attach engine to an http server", done => { + const server = http.createServer(); eio.attach(server); - server.listen(function() { - var uri = "http://localhost:%d/engine.io/default/".s( + server.listen(() => { + const uri = "http://localhost:%d/engine.io/default/".s( server.address().port ); - request.get(uri, function(err, res) { + request.get(uri, (err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(0); @@ -77,12 +73,12 @@ describe("engine", function() { }); }); - it("should destroy upgrades not handled by engine", function(done) { - var server = http.createServer(); + it("should destroy upgrades not handled by engine", done => { + const server = http.createServer(); eio.attach(server, { destroyUpgradeTimeout: 50 }); - server.listen(function() { - var client = net.createConnection(server.address().port); + server.listen(() => { + const client = net.createConnection(server.address().port); client.setEncoding("ascii"); client.write( [ @@ -94,24 +90,24 @@ describe("engine", function() { ].join("\r\n") ); - var check = setTimeout(function() { + const check = setTimeout(() => { done(new Error("Client should have ended")); }, 100); - client.on("end", function() { + client.on("end", () => { clearTimeout(check); done(); }); }); }); - it("should not destroy unhandled upgrades with destroyUpgrade:false", function(done) { - var server = http.createServer(); + it("should not destroy unhandled upgrades with destroyUpgrade:false", done => { + const server = http.createServer(); eio.attach(server, { destroyUpgrade: false, destroyUpgradeTimeout: 50 }); - server.listen(function() { - var client = net.createConnection(server.address().port); - client.on("connect", function() { + server.listen(() => { + const client = net.createConnection(server.address().port); + client.on("connect", () => { client.setEncoding("ascii"); client.write( [ @@ -123,7 +119,7 @@ describe("engine", function() { ].join("\r\n") ); - setTimeout(function() { + setTimeout(() => { client.removeListener("end", onEnd); done(); }, 100); @@ -137,13 +133,13 @@ describe("engine", function() { }); }); - it("should destroy unhandled upgrades with after a timeout", function(done) { - var server = http.createServer(); + it("should destroy unhandled upgrades with after a timeout", done => { + const server = http.createServer(); eio.attach(server, { destroyUpgradeTimeout: 200 }); - server.listen(function() { - var client = net.createConnection(server.address().port); - client.on("connect", function() { + server.listen(() => { + const client = net.createConnection(server.address().port); + client.on("connect", () => { client.setEncoding("ascii"); client.write( [ @@ -158,7 +154,7 @@ describe("engine", function() { // send from client to server // tests that socket is still alive // this will not keep the socket open as the server does not handle it - setTimeout(function() { + setTimeout(() => { client.write("foo"); }, 100); @@ -171,23 +167,23 @@ describe("engine", function() { }); }); - it("should not destroy handled upgrades with after a timeout", function(done) { - var server = http.createServer(); + it("should not destroy handled upgrades with after a timeout", done => { + const server = http.createServer(); eio.attach(server, { destroyUpgradeTimeout: 100 }); // write to the socket to keep engine.io from closing it by writing before the timeout - server.on("upgrade", function(req, socket) { + server.on("upgrade", (req, socket) => { socket.write("foo"); - socket.on("data", function(chunk) { + socket.on("data", chunk => { expect(chunk.toString()).to.be("foo"); socket.end(); }); }); - server.listen(function() { - var client = net.createConnection(server.address().port); + server.listen(() => { + const client = net.createConnection(server.address().port); - client.on("connect", function() { + client.on("connect", () => { client.setEncoding("ascii"); client.write( [ @@ -200,25 +196,25 @@ describe("engine", function() { ); // test that socket is still open by writing after the timeout period - setTimeout(function() { + setTimeout(() => { client.write("foo"); }, 200); - client.on("data", function(data) {}); + client.on("data", data => {}); client.on("end", done); }); }); }); - it("should preserve original request listeners", function(done) { - var listeners = 0; - var server = http.createServer(function(req, res) { + it("should preserve original request listeners", done => { + let listeners = 0; + const server = http.createServer((req, res) => { expect(req && res).to.be.ok(); listeners++; }); - server.on("request", function(req, res) { + server.on("request", (req, res) => { expect(req && res).to.be.ok(); res.writeHead(200); res.end(""); @@ -227,24 +223,24 @@ describe("engine", function() { eio.attach(server); - server.listen(function() { - var port = server.address().port; - request.get("http://localhost:%d/engine.io/default/".s(port), function( - 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), function(err, res) { - expect(err).to.be(null); - expect(res.status).to.be(200); - expect(listeners).to.eql(2); - server.once("close", done); - server.close(); - }); - }); + server.listen(() => { + const port = server.address().port; + request.get( + "http://localhost:%d/engine.io/default/".s(port), + (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) => { + expect(err).to.be(null); + expect(res.status).to.be(200); + expect(listeners).to.eql(2); + server.once("close", done); + server.close(); + }); + } + ); }); }); }); diff --git a/test/fixtures/server-close-upgraded.js b/test/fixtures/server-close-upgraded.js index df773cd28..fafb32b4e 100644 --- a/test/fixtures/server-close-upgraded.js +++ b/test/fixtures/server-close-upgraded.js @@ -1,9 +1,9 @@ -var eioc = require("engine.io-client"); -var listen = require("../common").listen; +const eioc = require("engine.io-client"); +const listen = require("../common").listen; -var engine = listen(function(port) { - var socket = new eioc.Socket("ws://localhost:" + port); - socket.on("upgrade", function() { +const engine = listen(port => { + const socket = new eioc.Socket("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 f47cfba6a..4fe2917fd 100644 --- a/test/fixtures/server-close-upgrading.js +++ b/test/fixtures/server-close-upgrading.js @@ -1,9 +1,9 @@ -var eioc = require("engine.io-client"); -var listen = require("../common").listen; +const eioc = require("engine.io-client"); +const listen = require("../common").listen; -var engine = listen(function(port) { - var socket = new eioc.Socket("ws://localhost:" + port); - socket.on("upgrading", function() { +const engine = listen(port => { + const socket = new eioc.Socket("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 c1fd4537f..6d8b4c0c1 100644 --- a/test/fixtures/server-close.js +++ b/test/fixtures/server-close.js @@ -1,9 +1,9 @@ -var eioc = require("engine.io-client"); -var listen = require("../common").listen; +const eioc = require("engine.io-client"); +const listen = require("../common").listen; -var engine = listen(function(port) { - var socket = new eioc.Socket("ws://localhost:" + port); - socket.on("open", function() { +const engine = listen(port => { + const socket = new eioc.Socket("ws://localhost:" + port); + socket.on("open", () => { engine.httpServer.close(); engine.close(); }); diff --git a/test/jsonp.js b/test/jsonp.js index 39407cd32..ecbb8630e 100644 --- a/test/jsonp.js +++ b/test/jsonp.js @@ -1,68 +1,64 @@ -/** - * Module dependencies. - */ +const eioc = require("engine.io-client"); +const listen = require("./common").listen; +const expect = require("expect.js"); +const request = require("superagent"); -var eioc = require("engine.io-client"); -var listen = require("./common").listen; -var expect = require("expect.js"); -var request = require("superagent"); - -describe("JSONP", function() { - before(function() { +describe("JSONP", () => { + before(() => { // we have to override the browser's functionality for JSONP document = { // eslint-disable-line no-global-assign body: { - appendChild: function() {}, - removeChild: function() {} + appendChild: () => {}, + removeChild: () => {} } }; document.createElement = function(name) { - var self = this; + const self = this; if ("script" === name) { - var script = {}; + const script = {}; - script.__defineGetter__("parentNode", function() { - return document.body; - }); + script.__defineGetter__("parentNode", () => document.body); - script.__defineSetter__("src", function(uri) { - request.get(uri).end(function(err, res) { + 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) { - var form = { + const form = { style: {}, action: "", - parentNode: { removeChild: function() {} }, - removeChild: function() {}, - setAttribute: function() {}, - appendChild: function() {}, + parentNode: { + removeChild: () => {} + }, + removeChild: () => {}, + setAttribute: () => {}, + appendChild: () => {}, submit: function() { request .post(this.action) .type("form") .send({ d: self.areaValue }) - .end(function() {}); + .end(() => {}); } }; return form; } else if ("textarea" === name) { - var textarea = {}; + const textarea = {}; // a hack to be able to access the area data when form is sent - textarea.__defineSetter__("value", function(data) { + textarea.__defineSetter__("value", data => { self.areaValue = data; }); return textarea; } else if (~name.indexOf("iframe")) { - var iframe = {}; - setTimeout(function() { + const iframe = {}; + setTimeout(() => { if (iframe.onload) iframe.onload(); }, 0); @@ -72,34 +68,32 @@ describe("JSONP", function() { } }; - document.getElementsByTagName = function(name) { - return [ - { - parentNode: { - insertBefore: function() {} - } + document.getElementsByTagName = name => [ + { + parentNode: { + insertBefore: () => {} } - ]; - }; + } + ]; }); - after(function() { + after(() => { delete document.getElementsByTagName; delete document.createElement; delete global.document; }); - describe("handshake", function() { - it("should open with polling JSONP when requested", function(done) { - var engine = listen( + describe("handshake", () => { + it("should open with polling JSONP when requested", done => { + const engine = listen( { allowUpgrades: false, transports: ["polling"] }, - function(port) { + port => { eioc("ws://localhost:" + port, { transports: ["polling"], forceJSONP: true, upgrade: false }); - engine.on("connection", function(socket) { + engine.on("connection", socket => { expect(socket.transport.name).to.be("polling"); expect(socket.transport.head).to.be("___eio[0]("); done(); @@ -109,36 +103,33 @@ describe("JSONP", function() { }); }); - describe("messages", function() { - var engine, port, socket; + describe("messages", () => { + let engine, port, socket; - beforeEach(function(done) { - engine = listen( - { allowUpgrades: false, transports: ["polling"] }, - function(p) { - port = p; + beforeEach(done => { + engine = listen({ allowUpgrades: false, transports: ["polling"] }, p => { + port = p; - socket = new eioc.Socket("ws://localhost:" + port, { - transports: ["polling"], - forceJSONP: true, - upgrade: false - }); + socket = new eioc.Socket("ws://localhost:" + port, { + transports: ["polling"], + forceJSONP: true, + upgrade: false + }); - done(); - } - ); + done(); + }); }); - it("should arrive from client to server and back (pollingJSONP)", function(done) { - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + 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", function() { + socket.on("open", () => { socket.send("a"); - socket.on("message", function(msg) { + socket.on("message", msg => { expect(socket.transport.query.j).to.not.be(undefined); expect(msg).to.be("a"); done(); @@ -146,73 +137,73 @@ describe("JSONP", function() { }); }); - it("should not fail JSON.parse for stringified messages", function(done) { - engine.on("connection", function(conn) { - conn.on("message", function(message) { + 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", function() { + socket.on("open", () => { socket.send(JSON.stringify({ test: "a\r\nb\n\n\n\nc" })); }); }); - it("should parse newlines in message correctly", function(done) { - engine.on("connection", function(conn) { - conn.on("message", function(message) { + 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", function() { + 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)", function(done) { - var binaryData = Buffer.allocUnsafe(5); + 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", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { conn.send(msg); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send(binaryData); - socket.on("message", function(msg) { - for (var i = 0; i < msg.length; i++) expect(msg[i]).to.be(i); + socket.on("message", msg => { + for (let i = 0; i < msg.length; i++) expect(msg[i]).to.be(i); done(); }); }); }); }); - describe("close", function() { - it("should trigger when server closes a client", function(done) { - var engine = listen( + describe("close", () => { + it("should trigger when server closes a client", done => { + const engine = listen( { allowUpgrades: false, transports: ["polling"] }, - function(port) { - var socket = new eioc.Socket("ws://localhost:" + port, { + port => { + const socket = new eioc.Socket("ws://localhost:" + port, { transports: ["polling"], forceJSONP: true, upgrade: false }); - var total = 2; + let total = 2; - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + engine.on("connection", conn => { + conn.on("close", reason => { expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function() { + setTimeout(() => { conn.close(); }, 10); }); - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { expect(reason).to.be("transport close"); --total || done(); }); @@ -221,32 +212,32 @@ describe("JSONP", function() { ); }); - it("should trigger when client closes", function(done) { - var engine = listen( + it("should trigger when client closes", done => { + const engine = listen( { allowUpgrades: false, transports: ["polling"] }, - function(port) { - var socket = new eioc.Socket("ws://localhost:" + port, { + port => { + const socket = new eioc.Socket("ws://localhost:" + port, { transports: ["polling"], forceJSONP: true, upgrade: false }); - var total = 2; + let total = 2; - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + engine.on("connection", conn => { + conn.on("close", reason => { expect(reason).to.be("transport close"); --total || done(); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("a"); - socket.on("close", function(reason) { + socket.on("close", reason => { expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function() { + setTimeout(() => { socket.close(); }, 10); }); diff --git a/test/server.js b/test/server.js index 63b645576..a01c0564f 100644 --- a/test/server.js +++ b/test/server.js @@ -1,34 +1,30 @@ /* eslint-disable standard/no-callback-literal */ -/** - * Tests dependencies. - */ - -var http = require("http"); -var https = require("https"); -var fs = require("fs"); -var path = require("path"); -var exec = require("child_process").exec; -var zlib = require("zlib"); -var eio = require(".."); -var eioc = require("engine.io-client"); -var listen = require("./common").listen; -var expect = require("expect.js"); -var request = require("superagent"); -var cookieMod = require("cookie"); +const http = require("http"); +const https = require("https"); +const fs = require("fs"); +const path = require("path"); +const exec = require("child_process").exec; +const zlib = require("zlib"); +const eio = require(".."); +const eioc = require("engine.io-client"); +const listen = require("./common").listen; +const expect = require("expect.js"); +const request = require("superagent"); +const cookieMod = require("cookie"); /** * Tests. */ -describe("server", function() { - describe("verification", function() { - it("should disallow non-existent transports", function(done) { - listen(function(port) { +describe("server", () => { + describe("verification", () => { + it("should disallow non-existent transports", done => { + listen(port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "tobi" }) // no tobi transport - outrageous - .end(function(err, res) { + .end((err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(0); @@ -38,14 +34,14 @@ describe("server", function() { }); }); - it("should disallow `constructor` as transports", function(done) { + it("should disallow `constructor` as transports", done => { // make sure we check for actual properties - not those present on every {} - listen(function(port) { + listen(port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "constructor" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(0); @@ -55,13 +51,13 @@ describe("server", function() { }); }); - it("should disallow non-existent sids", function(done) { - listen(function(port) { + it("should disallow non-existent sids", done => { + listen(port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "polling", sid: "test" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(1); @@ -71,19 +67,19 @@ describe("server", function() { }); }); - it("should disallow requests that are rejected by `allowRequest`", function(done) { + it("should disallow requests that are rejected by `allowRequest`", done => { listen( { - allowRequest: function(req, fn) { + allowRequest: (req, fn) => { fn("Thou shall not pass", false); } }, - function(port) { + port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "polling" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(403); expect(res.body.code).to.be(4); @@ -94,18 +90,18 @@ describe("server", function() { ); }); - it("should disallow connection that are rejected by `allowRequest`", function(done) { + it("should disallow connection that are rejected by `allowRequest`", done => { listen( { - allowRequest: function(req, fn) { + allowRequest: (req, fn) => { fn(null, false); } }, - function(port) { - var client = eioc("ws://localhost:%d".s(port), { + port => { + const client = eioc("ws://localhost:%d".s(port), { transports: ["websocket"] }); - client.on("error", function() { + client.on("error", () => { done(); }); } @@ -113,13 +109,13 @@ describe("server", function() { }); }); - describe("handshake", function() { + describe("handshake", () => { 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 }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); // hack-obtain sid const sid = res.text.match(/"sid":"([^"]+)"/)[1]; @@ -136,7 +132,7 @@ describe("server", function() { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( @@ -152,7 +148,7 @@ describe("server", function() { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( @@ -168,7 +164,7 @@ describe("server", function() { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( @@ -184,7 +180,7 @@ describe("server", function() { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( @@ -200,7 +196,7 @@ describe("server", function() { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( @@ -216,7 +212,7 @@ describe("server", function() { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( @@ -232,7 +228,7 @@ describe("server", function() { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling", b64: 1 }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); const sid = res.text.match(/"sid":"([^"]+)"/)[1]; expect(res.headers["set-cookie"][0]).to.be( @@ -248,7 +244,7 @@ describe("server", function() { request .get("http://localhost:%d/engine.io/default/".s(port)) .query({ transport: "polling" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); expect(res.headers["set-cookie"]).to.be(undefined); done(); @@ -256,13 +252,13 @@ describe("server", function() { }); }); - it("should register a new client", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { + it("should register a new client", done => { + const engine = listen({ allowUpgrades: false }, port => { expect(Object.keys(engine.clients)).to.have.length(0); expect(engine.clientsCount).to.be(0); - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", () => { expect(Object.keys(engine.clients)).to.have.length(1); expect(engine.clientsCount).to.be(1); done(); @@ -270,19 +266,17 @@ describe("server", function() { }); }); - it("should register a new client with custom id", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { + it("should register a new client with custom id", done => { + const engine = listen({ allowUpgrades: false }, port => { expect(Object.keys(engine.clients)).to.have.length(0); expect(engine.clientsCount).to.be(0); - var customId = "CustomId" + Date.now(); + const customId = "CustomId" + Date.now(); - engine.generateId = function(req) { - return customId; - }; + engine.generateId = req => customId; - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.once("open", function() { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.once("open", () => { expect(Object.keys(engine.clients)).to.have.length(1); expect(engine.clientsCount).to.be(1); expect(socket.id).to.be(customId); @@ -292,13 +286,11 @@ describe("server", function() { }); }); - it("should register a new client with custom id (with a Promise)", function(done) { + it("should register a new client with custom id (with a Promise)", done => { const engine = listen({ allowUpgrades: false }, port => { const customId = "CustomId" + Date.now(); - engine.generateId = function() { - return Promise.resolve(customId); - }; + engine.generateId = () => Promise.resolve(customId); const socket = new eioc.Socket("ws://localhost:%d".s(port)); socket.once("open", () => { @@ -309,7 +301,7 @@ describe("server", function() { }); }); - it("should disallow connection that are rejected by `generateId`", function(done) { + it("should disallow connection that are rejected by `generateId`", done => { const engine = listen({ allowUpgrades: false }, port => { engine.generateId = () => { return Promise.reject(new Error("nope")); @@ -322,10 +314,10 @@ describe("server", function() { }); }); - it("should exchange handshake data", function(done) { - listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("handshake", function(obj) { + it("should exchange handshake data", done => { + listen({ allowUpgrades: false }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("handshake", obj => { expect(obj.sid).to.be.a("string"); expect(obj.pingTimeout).to.be.a("number"); expect(obj.upgrades).to.be.an("array"); @@ -334,82 +326,82 @@ describe("server", function() { }); }); - it("should allow custom ping timeouts", function(done) { - listen({ allowUpgrades: false, pingTimeout: 123 }, function(port) { - var socket = new eioc.Socket("http://localhost:%d".s(port)); - socket.on("handshake", function(obj) { + it("should allow custom ping timeouts", done => { + listen({ allowUpgrades: false, pingTimeout: 123 }, port => { + const socket = new eioc.Socket("http://localhost:%d".s(port)); + socket.on("handshake", obj => { expect(obj.pingTimeout).to.be(123); done(); }); }); }); - it("should trigger a connection event with a Socket", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { + it("should trigger a connection event with a Socket", done => { + const engine = listen({ allowUpgrades: false }, port => { eioc("ws://localhost:%d".s(port)); - engine.on("connection", function(socket) { + engine.on("connection", socket => { expect(socket).to.be.an(eio.Socket); done(); }); }); }); - it("should open with polling by default", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { + it("should open with polling by default", done => { + const engine = listen({ allowUpgrades: false }, port => { eioc("ws://localhost:%d".s(port)); - engine.on("connection", function(socket) { + engine.on("connection", socket => { expect(socket.transport.name).to.be("polling"); done(); }); }); }); - it("should be able to open with ws directly", function(done) { - var engine = listen({ transports: ["websocket"] }, function(port) { + it("should be able to open with ws directly", done => { + const engine = listen({ transports: ["websocket"] }, port => { eioc("ws://localhost:%d".s(port), { transports: ["websocket"] }); - engine.on("connection", function(socket) { + engine.on("connection", socket => { expect(socket.transport.name).to.be("websocket"); done(); }); }); }); - it("should not suggest any upgrades for websocket", function(done) { - listen({ transports: ["websocket"] }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + it("should not suggest any upgrades for websocket", done => { + listen({ transports: ["websocket"] }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - socket.on("handshake", function(obj) { + socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(0); done(); }); }); }); - it("should not suggest upgrades when none are availble", function(done) { - listen({ transports: ["polling"] }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), {}); - socket.on("handshake", function(obj) { + it("should not suggest upgrades when none are availble", done => { + listen({ transports: ["polling"] }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(0); done(); }); }); }); - it("should only suggest available upgrades", function(done) { - listen({ transports: ["polling"] }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), {}); - socket.on("handshake", function(obj) { + it("should only suggest available upgrades", done => { + listen({ transports: ["polling"] }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(0); done(); }); }); }); - it("should suggest all upgrades when no transports are disabled", function(done) { - listen({}, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), {}); - socket.on("handshake", function(obj) { + it("should suggest all upgrades when no transports are disabled", done => { + listen({}, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), {}); + socket.on("handshake", obj => { expect(obj.upgrades).to.have.length(1); expect(obj.upgrades).to.have.contain("websocket"); done(); @@ -417,26 +409,26 @@ describe("server", function() { }); }); - it("default to polling when proxy doesn't support websocket", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - engine.on("connection", function(socket) { - socket.on("message", function(msg) { + it("default to polling when proxy doesn't support websocket", done => { + const engine = listen({ allowUpgrades: false }, port => { + engine.on("connection", socket => { + socket.on("message", msg => { if ("echo" === msg) socket.send(msg); }); }); var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { + socket.on("open", () => { request .get("http://localhost:%d/engine.io/".s(port)) .set({ connection: "close" }) .query({ transport: "websocket", sid: socket.id }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(3); socket.send("echo"); - socket.on("message", function(msg) { + socket.on("message", msg => { expect(msg).to.be("echo"); done(); }); @@ -445,10 +437,10 @@ describe("server", function() { }); }); - it("should allow arbitrary data through query string", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { + it("should allow arbitrary data through query string", done => { + const engine = listen({ allowUpgrades: false }, port => { eioc("ws://localhost:%d".s(port), { query: { a: "b" } }); - engine.on("connection", function(conn) { + engine.on("connection", conn => { expect(conn.request._query).to.have.keys("transport", "a"); expect(conn.request._query.a).to.be("b"); done(); @@ -456,10 +448,10 @@ describe("server", function() { }); }); - it("should allow data through query string in uri", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { + 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)); - engine.on("connection", function(conn) { + engine.on("connection", conn => { expect(conn.request._query.EIO).to.be.a("string"); expect(conn.request._query.a).to.be("b"); expect(conn.request._query.c).to.be("d"); @@ -468,17 +460,17 @@ describe("server", function() { }); }); - it("should disallow bad requests", function(done) { + it("should disallow bad requests", done => { listen( { cors: { credentials: true, origin: "http://engine.io" } }, - function(port) { + port => { request .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "websocket" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be.an(Error); expect(res.status).to.be(400); expect(res.body.code).to.be(3); @@ -495,11 +487,11 @@ describe("server", function() { ); }); - it("should send a packet along with the handshake", function(done) { - listen({ initialPacket: "faster!" }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { - socket.on("message", function(msg) { + it("should send a packet along with the handshake", done => { + listen({ initialPacket: "faster!" }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.be("faster!"); done(); }); @@ -508,15 +500,15 @@ describe("server", function() { }); }); - describe("close", function() { - it("should be able to access non-empty writeBuffer at closing (server)", function(done) { - var opts = { allowUpgrades: false }; - var engine = listen(opts, function(port) { + describe("close", () => { + 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)); - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + engine.on("connection", conn => { + conn.on("close", reason => { expect(conn.writeBuffer.length).to.be(1); - setTimeout(function() { + setTimeout(() => { expect(conn.writeBuffer.length).to.be(0); // writeBuffer has been cleared }, 10); done(); @@ -527,14 +519,14 @@ describe("server", function() { }); }); - it("should be able to access non-empty writeBuffer at closing (client)", function(done) { - var opts = { allowUpgrades: false }; - listen(opts, function(port) { - var socket = new eioc.Socket("http://localhost:%d".s(port)); - socket.on("open", function() { - socket.on("close", function(reason) { + 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)); + socket.on("open", () => { + socket.on("close", reason => { expect(socket.writeBuffer.length).to.be(1); - setTimeout(function() { + setTimeout(() => { expect(socket.writeBuffer.length).to.be(0); }, 10); done(); @@ -545,13 +537,13 @@ describe("server", function() { }); }); - it("should trigger on server if the client does not pong", function(done) { - var opts = { allowUpgrades: false, pingInterval: 5, pingTimeout: 5 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("http://localhost:%d".s(port)); - socket.sendPacket = function() {}; - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + 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)); + socket.sendPacket = () => {}; + engine.on("connection", conn => { + conn.on("close", reason => { expect(reason).to.be("ping timeout"); done(); }); @@ -559,17 +551,21 @@ describe("server", function() { }); }); - it("should trigger on server even when there is no outstanding polling request (GH-198)", function(done) { - var opts = { allowUpgrades: false, pingInterval: 500, pingTimeout: 500 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("http://localhost:%d".s(port)); - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + it("should trigger on server even when there is no outstanding polling request (GH-198)", done => { + const opts = { + allowUpgrades: false, + pingInterval: 500, + pingTimeout: 500 + }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("http://localhost:%d".s(port)); + engine.on("connection", conn => { + conn.on("close", reason => { expect(reason).to.be("ping timeout"); done(); }); // client abruptly disconnects, no polling request on this tick since we've just connected - socket.sendPacket = socket.onPacket = function() {}; + socket.sendPacket = socket.onPacket = () => {}; socket.close(); // then server app tries to close the socket, since client disappeared conn.close(); @@ -577,15 +573,15 @@ describe("server", function() { }); }); - it("should trigger on client if server does not meet ping timeout", function(done) { - var opts = { allowUpgrades: false, pingInterval: 50, pingTimeout: 30 }; - listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { + 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)); + socket.on("open", () => { // override onPacket and Transport#onClose to simulate an inactive server after handshake - socket.onPacket = function() {}; - socket.transport.onClose = function() {}; - socket.on("close", function(reason, err) { + socket.onPacket = () => {}; + socket.transport.onClose = () => {}; + socket.on("close", (reason, err) => { expect(reason).to.be("ping timeout"); done(); }); @@ -593,47 +589,47 @@ describe("server", function() { }); }); - it("should trigger on both ends upon ping timeout", function(done) { - var opts = { allowUpgrades: false, pingTimeout: 50, pingInterval: 50 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var total = 2; + 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)); + let total = 2; function onClose(reason, err) { expect(reason).to.be("ping timeout"); --total || done(); } - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.on("close", onClose); }); - socket.on("open", function() { + socket.on("open", () => { // override onPacket and Transport#onClose to simulate an inactive server after handshake - socket.onPacket = socket.sendPacket = function() {}; - socket.transport.onClose = function() {}; + socket.onPacket = socket.sendPacket = () => {}; + socket.transport.onClose = () => {}; socket.on("close", onClose); }); }); }); - it("should trigger when server closes a client", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var total = 2; + 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)); + let total = 2; - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + engine.on("connection", conn => { + conn.on("close", reason => { expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function() { + setTimeout(() => { conn.close(); }, 10); }); - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { expect(reason).to.be("transport close"); --total || done(); }); @@ -641,26 +637,26 @@ describe("server", function() { }); }); - it("should trigger when server closes a client (ws)", function(done) { - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["websocket"] }); - var total = 2; + let total = 2; - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + engine.on("connection", conn => { + conn.on("close", reason => { expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function() { + setTimeout(() => { conn.close(); }, 10); }); - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { expect(reason).to.be("transport close"); --total || done(); }); @@ -668,117 +664,117 @@ describe("server", function() { }); }); - it("should allow client reconnect after restarting (ws)", function(done) { - var opts = { transports: ["websocket"] }; - var engine = listen(opts, function(port) { + it("should allow client reconnect after restarting (ws)", done => { + const opts = { transports: ["websocket"] }; + const engine = listen(opts, port => { engine.httpServer.close(); engine.httpServer.listen(port); - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - engine.once("connection", function(conn) { - setTimeout(function() { + engine.once("connection", conn => { + setTimeout(() => { conn.close(); }, 10); }); - socket.once("close", function(reason) { + socket.once("close", reason => { expect(reason).to.be("transport close"); done(); }); }); }); - it("should trigger when client closes", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var total = 2; + it("should trigger when client closes", done => { + const engine = listen({ allowUpgrades: false }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let total = 2; - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + engine.on("connection", conn => { + conn.on("close", reason => { expect(reason).to.be("transport close"); --total || done(); }); }); - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function() { + setTimeout(() => { socket.close(); }, 10); }); }); }); - it("should trigger when client closes (ws)", function(done) { - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["websocket"] }); - var total = 2; + let total = 2; - engine.on("connection", function(conn) { - conn.on("close", function(reason) { + engine.on("connection", conn => { + conn.on("close", reason => { expect(reason).to.be("transport close"); --total || done(); }); }); - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { expect(reason).to.be("forced close"); --total || done(); }); - setTimeout(function() { + setTimeout(() => { socket.close(); }, 10); }); }); }); - it("should trigger when calling socket.close() in payload", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); + 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)); - engine.on("connection", function(conn) { - conn.send(null, function() { + engine.on("connection", conn => { + conn.send(null, () => { socket.close(); }); conn.send("this should not be handled"); - conn.on("close", function(reason) { + conn.on("close", reason => { expect(reason).to.be("transport close"); done(); }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.not.be("this should not be handled"); }); - socket.on("close", function(reason) { + socket.on("close", reason => { expect(reason).to.be("forced close"); }); }); }); }); - it("should abort upgrade if socket is closed (GH-35)", function(done) { - listen({ allowUpgrades: true }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { + 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)); + socket.on("open", () => { socket.close(); // we wait until complete to see if we get an uncaught EPIPE - setTimeout(function() { + setTimeout(() => { done(); }, 100); }); @@ -809,15 +805,15 @@ describe("server", function() { it( "should trigger if a poll request is ongoing and the underlying " + "socket closes, as in a browser tab close", - function($done) { - var engine = listen({ allowUpgrades: false }, function(port) { + $done => { + const engine = listen({ allowUpgrades: false }, port => { // hack to access the sockets created by node-xmlhttprequest // see: https://github.com/driverdan/node-XMLHttpRequest/issues/44 - var request = require("http").request; - var sockets = []; + const request = require("http").request; + const sockets = []; http.request = function(opts) { - var req = request.apply(null, arguments); - req.on("socket", function(socket) { + const req = request.apply(null, arguments); + req.on("socket", socket => { sockets.push(socket); }); return req; @@ -829,15 +825,15 @@ describe("server", function() { } var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var serverSocket; + let serverSocket; - engine.on("connection", function(s) { + engine.on("connection", s => { serverSocket = s; }); - socket.transport.on("poll", function() { + socket.transport.on("poll", () => { // we set a timer to wait for the request to actually reach - setTimeout(function() { + setTimeout(() => { // at this time server's `connection` should have been fired expect(serverSocket).to.be.an("object"); @@ -853,7 +849,7 @@ describe("server", function() { // kill the underlying connection sockets[1].end(); - serverSocket.on("close", function(reason, err) { + serverSocket.on("close", (reason, err) => { expect(reason).to.be("transport error"); expect(err.message).to.be("poll connection closed prematurely"); done(); @@ -864,12 +860,12 @@ describe("server", function() { } ); - it("should not trigger with connection: close header", function($done) { - var engine = listen({ allowUpgrades: false }, function(port) { + it("should not trigger with connection: close header", $done => { + const engine = listen({ allowUpgrades: false }, port => { // intercept requests to add connection: close - var request = http.request; + const request = http.request; http.request = function() { - var opts = arguments[0]; + const opts = arguments[0]; opts.headers = opts.headers || {}; opts.headers.Connection = "close"; return request.apply(this, arguments); @@ -880,18 +876,18 @@ describe("server", function() { $done(); } - engine.on("connection", function(socket) { - socket.on("message", function(msg) { + engine.on("connection", socket => { + socket.on("message", msg => { expect(msg).to.equal("test"); socket.send("woot"); }); }); var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { + socket.on("open", () => { socket.send("test"); }); - socket.on("message", function(msg) { + socket.on("message", msg => { expect(msg).to.be("woot"); done(); }); @@ -901,28 +897,28 @@ describe("server", function() { it( "should not trigger early with connection `ping timeout`" + "after post handshake timeout", - function(done) { + done => { // first timeout should trigger after `pingInterval + pingTimeout`, // not just `pingTimeout`. - var opts = { + const opts = { allowUpgrades: false, pingInterval: 300, pingTimeout: 100 }; - listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var clientCloseReason = null; + listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let clientCloseReason = null; - socket.on("handshake", function() { - socket.onPacket = function() {}; + socket.on("handshake", () => { + socket.onPacket = () => {}; }); - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { clientCloseReason = reason; }); }); - setTimeout(function() { + setTimeout(() => { expect(clientCloseReason).to.be(null); done(); }, 200); @@ -933,27 +929,31 @@ describe("server", function() { it( "should not trigger early with connection `ping timeout` " + "after post ping timeout", - function(done) { + done => { // ping timeout should trigger after `pingInterval + pingTimeout`, // not just `pingTimeout`. - var opts = { allowUpgrades: false, pingInterval: 80, pingTimeout: 50 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var clientCloseReason = null; + const opts = { + allowUpgrades: false, + pingInterval: 80, + pingTimeout: 50 + }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let clientCloseReason = null; - engine.on("connection", function(conn) { - conn.on("heartbeat", function() { - conn.onPacket = function() {}; + engine.on("connection", conn => { + conn.on("heartbeat", () => { + conn.onPacket = () => {}; }); }); - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { clientCloseReason = reason; }); }); - setTimeout(function() { + setTimeout(() => { expect(clientCloseReason).to.be(null); done(); }, 100); @@ -964,26 +964,30 @@ describe("server", function() { it( "should trigger early with connection `transport close` " + "after missing pong", - function(done) { + done => { // ping timeout should trigger after `pingInterval + pingTimeout`, // not just `pingTimeout`. - var opts = { allowUpgrades: false, pingInterval: 80, pingTimeout: 50 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var clientCloseReason = null; + const opts = { + allowUpgrades: false, + pingInterval: 80, + pingTimeout: 50 + }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let clientCloseReason = null; - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { clientCloseReason = reason; }); }); - engine.on("connection", function(conn) { - conn.on("heartbeat", function() { - setTimeout(function() { + engine.on("connection", conn => { + conn.on("heartbeat", () => { + setTimeout(() => { conn.close(); }, 20); - setTimeout(function() { + setTimeout(() => { expect(clientCloseReason).to.be("transport close"); done(); }, 100); @@ -996,32 +1000,32 @@ describe("server", function() { it( "should trigger with connection `ping timeout` " + "after `pingInterval + pingTimeout`", - function(done) { - var opts = { + done => { + const opts = { allowUpgrades: false, pingInterval: 300, pingTimeout: 100 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var clientCloseReason = null; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let clientCloseReason = null; - socket.on("open", function() { - socket.on("close", function(reason) { + socket.on("open", () => { + socket.on("close", reason => { clientCloseReason = reason; }); }); - engine.on("connection", function(conn) { - conn.once("heartbeat", function() { - socket.onPacket = function() {}; - setTimeout(function() { + engine.on("connection", conn => { + conn.once("heartbeat", () => { + socket.onPacket = () => {}; + setTimeout(() => { expect(clientCloseReason).to.be(null); }, 150); - setTimeout(function() { + setTimeout(() => { expect(clientCloseReason).to.be(null); }, 350); - setTimeout(function() { + setTimeout(() => { expect(clientCloseReason).to.be("ping timeout"); done(); }, 500); @@ -1033,13 +1037,13 @@ describe("server", function() { it( "should abort the polling data request if it is " + "in progress", - function(done) { - var engine = listen({ transports: ["polling"] }, function(port) { - var socket = new eioc.Socket("http://localhost:%d".s(port)); + done => { + const engine = listen({ transports: ["polling"] }, port => { + const socket = new eioc.Socket("http://localhost:%d".s(port)); - engine.on("connection", function(conn) { - var onDataRequest = conn.transport.onDataRequest; - conn.transport.onDataRequest = function(req, res) { + engine.on("connection", conn => { + const onDataRequest = conn.transport.onDataRequest; + conn.transport.onDataRequest = (req, res) => { engine.httpServer.close(done); onDataRequest.call(conn.transport, req, res); req.removeAllListeners(); @@ -1047,7 +1051,7 @@ describe("server", function() { }; }); - socket.on("open", function() { + socket.on("open", () => { socket.send("test"); }); }); @@ -1056,15 +1060,15 @@ describe("server", function() { // tests https://github.com/LearnBoost/engine.io-client/issues/207 // websocket test, transport error - it("should trigger transport close before open for ws", function(done) { - var opts = { transports: ["websocket"] }; - listen(opts, function(port) { - var url = "ws://%s:%d".s("0.0.0.0", port); - var socket = new eioc.Socket(url); - socket.on("open", function() { + 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 socket = new eioc.Socket(url); + socket.on("open", () => { done(new Error("Test invalidation")); }); - socket.on("close", function(reason) { + socket.on("close", reason => { expect(reason).to.be("transport error"); done(); }); @@ -1073,14 +1077,14 @@ describe("server", function() { // tests https://github.com/LearnBoost/engine.io-client/issues/207 // polling test, transport error - it("should trigger transport close before open for xhr", function(done) { - var opts = { transports: ["polling"] }; - listen(opts, function(port) { - var socket = new eioc.Socket("http://invalidserver:%d".s(port)); - socket.on("open", function() { + 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)); + socket.on("open", () => { done(new Error("Test invalidation")); }); - socket.on("close", function(reason) { + socket.on("close", reason => { expect(reason).to.be("transport error"); done(); }); @@ -1089,14 +1093,14 @@ describe("server", function() { // tests https://github.com/LearnBoost/engine.io-client/issues/207 // websocket test, force close - it("should trigger force close before open for ws", function(done) { - var opts = { transports: ["websocket"] }; - listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { + 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)); + socket.on("open", () => { done(new Error("Test invalidation")); }); - socket.on("close", function(reason) { + socket.on("close", reason => { expect(reason).to.be("forced close"); done(); }); @@ -1106,14 +1110,14 @@ describe("server", function() { // tests https://github.com/LearnBoost/engine.io-client/issues/207 // polling test, force close - it("should trigger force close before open for xhr", function(done) { - var opts = { transports: ["polling"] }; - listen(opts, function(port) { - var socket = new eioc.Socket("http://localhost:%d".s(port)); - socket.on("open", function() { + 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)); + socket.on("open", () => { done(new Error("Test invalidation")); }); - socket.on("close", function(reason) { + socket.on("close", reason => { expect(reason).to.be("forced close"); done(); }); @@ -1121,79 +1125,79 @@ describe("server", function() { }); }); - it("should close transport upon ping timeout (ws)", function(done) { - var opts = { + it("should close transport upon ping timeout (ws)", done => { + const opts = { allowUpgrades: false, transports: ["websocket"], pingInterval: 50, pingTimeout: 30 }; - var engine = listen(opts, function(port) { - engine.on("connection", function(conn) { + const engine = listen(opts, port => { + engine.on("connection", conn => { conn.transport.on("close", done); }); - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); // override to simulate an inactive client - socket.sendPacket = socket.onHeartbeat = function() {}; + socket.sendPacket = socket.onHeartbeat = () => {}; }); }); - it("should close transport upon ping timeout (polling)", function(done) { - var opts = { + it("should close transport upon ping timeout (polling)", done => { + const opts = { allowUpgrades: false, transports: ["polling"], pingInterval: 50, pingTimeout: 30 }; - var engine = listen(opts, function(port) { - engine.on("connection", function(conn) { + const engine = listen(opts, port => { + engine.on("connection", conn => { conn.transport.on("close", done); }); - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["polling"] }); // override to simulate an inactive client - socket.sendPacket = socket.onHeartbeat = function() {}; + socket.sendPacket = socket.onHeartbeat = () => {}; }); }); - it("should close transport upon parse error (ws)", function(done) { - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - engine.on("connection", function(conn) { + it("should close transport upon parse error (ws)", done => { + const opts = { allowUpgrades: false, transports: ["websocket"] }; + const engine = listen(opts, port => { + engine.on("connection", conn => { conn.transport.on("close", done); }); - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - socket.on("open", function() { + socket.on("open", () => { socket.transport.ws.send("invalid"); }); }); }); - it("should close transport upon parse error (polling)", function(done) { - var opts = { allowUpgrades: false, transports: ["polling"] }; - var engine = listen(opts, function(port) { - engine.on("connection", function(conn) { + it("should close transport upon parse error (polling)", done => { + const opts = { allowUpgrades: false, transports: ["polling"] }; + const engine = listen(opts, port => { + engine.on("connection", conn => { conn.transport.closeTimeout = 100; conn.transport.on("close", done); }); - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["polling"] }); - socket.on("open", function() { - socket.transport.doWrite("invalid", function() {}); + socket.on("open", () => { + socket.transport.doWrite("invalid", () => {}); }); }); }); - it("should close upgrading transport upon socket close", function(done) { - var engine = listen(function(port) { - engine.on("connection", function(conn) { - conn.on("upgrading", function(transport) { + it("should close upgrading transport upon socket close", done => { + const engine = listen(port => { + engine.on("connection", conn => { + conn.on("upgrading", transport => { transport.on("close", done); conn.close(); }); @@ -1202,51 +1206,51 @@ describe("server", function() { }); }); - it("should close upgrading transport upon upgrade timeout", function(done) { - var opts = { upgradeTimeout: 100 }; - var engine = listen(opts, function(port) { - engine.on("connection", function(conn) { - conn.on("upgrading", function(transport) { + it("should close upgrading transport upon upgrade timeout", done => { + const opts = { upgradeTimeout: 100 }; + const engine = listen(opts, port => { + engine.on("connection", conn => { + conn.on("upgrading", transport => { transport.on("close", done); }); }); - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("upgrading", function(transport) { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("upgrading", transport => { // override not to complete upgrading - transport.send = function() {}; + transport.send = () => {}; }); }); }); - it("should not crash when messing with Object prototype", function(done) { + it("should not crash when messing with Object prototype", done => { Object.prototype.foo = "bar"; // eslint-disable-line no-extend-native - var engine = listen({ allowUpgrades: true }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { + const engine = listen({ allowUpgrades: true }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + socket.on("open", () => { engine.close(); - setTimeout(function() { + setTimeout(() => { done(); }, 100); }); }); }); - describe("graceful close", function() { + describe("graceful close", () => { function fixture(filename) { return ( process.execPath + " " + path.join(__dirname, "fixtures", filename) ); } - it("should stop socket and timers", function(done) { + it("should stop socket and timers", done => { exec(fixture("server-close.js"), done); }); - it("should stop upgraded socket and timers", function(done) { + it("should stop upgraded socket and timers", done => { exec(fixture("server-close-upgraded.js"), done); }); - it("should stop upgrading socket and timers", function(done) { + it("should stop upgrading socket and timers", done => { exec(fixture("server-close-upgrading.js"), done); }); }); @@ -1255,14 +1259,14 @@ describe("server", function() { describe("messages", function() { this.timeout(5000); - it("should arrive from server to client", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - engine.on("connection", function(conn) { + it("should arrive from server to client", done => { + const engine = listen({ allowUpgrades: false }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", conn => { conn.send("a"); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.be("a"); done(); }); @@ -1270,20 +1274,20 @@ describe("server", function() { }); }); - it("should arrive from server to client (multiple)", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var expected = ["a", "b", "c"]; - var i = 0; + 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 expected = ["a", "b", "c"]; + let i = 0; - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send("a"); // we use set timeouts to ensure the messages are delivered as part // of different. - setTimeout(function() { + setTimeout(() => { conn.send("b"); - setTimeout(function() { + setTimeout(() => { // here we make sure we buffer both the close packet and // a regular packet conn.send("c"); @@ -1291,99 +1295,99 @@ describe("server", function() { }, 50); }, 50); - conn.on("close", function() { + conn.on("close", () => { // since close fires right after the buffer is drained - setTimeout(function() { + setTimeout(() => { expect(i).to.be(3); done(); }, 50); }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.be(expected[i++]); }); }); }); }); - it("should not be receiving data when getting a message longer than maxHttpBufferSize when polling", function(done) { - var opts = { + it("should not be receiving data when getting a message longer than maxHttpBufferSize when polling", done => { + const opts = { allowUpgrades: false, transports: ["polling"], maxHttpBufferSize: 5 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", conn => { + conn.on("message", msg => { done( new Error("Test invalidation (message is longer than allowed)") ); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("aasdasdakjhasdkjhasdkjhasdkjhasdkjhasdkjhasdkjha"); }); - socket.on("close", function() { + socket.on("close", () => { done(); }); }); }); - it("should not be receiving data when getting a message longer than maxHttpBufferSize (websocket)", function(done) { - var opts = { maxHttpBufferSize: 5 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["websocket"] }); - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { done( new Error("Test invalidation (message is longer than allowed)") ); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("aasdasdakjhasdkjhasdkjhasdkjhasdkjhasdkjhasdkjha"); }); - socket.on("close", function() { + socket.on("close", () => { done(); }); }); }); - it("should receive data when getting a message shorter than maxHttpBufferSize when polling", function(done) { - var opts = { + it("should receive data when getting a message shorter than maxHttpBufferSize when polling", done => { + const opts = { allowUpgrades: false, transports: ["polling"], maxHttpBufferSize: 5 }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", conn => { + conn.on("message", msg => { expect(msg).to.be("a"); done(); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("a"); }); }); }); - it("should arrive from server to client (ws)", function(done) { - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["websocket"] }); - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send("a"); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.be("a"); done(); }); @@ -1391,52 +1395,52 @@ describe("server", function() { }); }); - it("should arrive from server to client (multiple, ws)", function(done) { - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["websocket"] }); - var expected = ["a", "b", "c"]; - var i = 0; + const expected = ["a", "b", "c"]; + let i = 0; - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send("a"); - setTimeout(function() { + setTimeout(() => { conn.send("b"); - setTimeout(function() { + setTimeout(() => { conn.send("c"); conn.close(); }, 50); }, 50); - conn.on("close", function() { - setTimeout(function() { + conn.on("close", () => { + setTimeout(() => { expect(i).to.be(3); done(); }, 50); }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.be(expected[i++]); }); }); }); }); - it("should arrive from server to client (multiple, no delay, ws)", function(done) { - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["websocket"] }); - var expected = ["a", "b", "c"]; - var i = 0; + const expected = ["a", "b", "c"]; + let i = 0; - engine.on("connection", function(conn) { - conn.on("close", function() { - setTimeout(function() { + engine.on("connection", conn => { + conn.on("close", () => { + setTimeout(() => { expect(i).to.be(3); done(); }, 50); @@ -1447,34 +1451,34 @@ describe("server", function() { conn.close(); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.be(expected[i++]); }); }); }); }); - it("should arrive when binary data is sent as Int8Array (ws)", function(done) { - var binaryData = new Int8Array(5); - for (var i = 0; i < binaryData.length; i++) { + it("should arrive when binary data is sent as Int8Array (ws)", done => { + const binaryData = new Int8Array(5); + for (let i = 0; i < binaryData.length; i++) { binaryData[i] = i; } - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const opts = { allowUpgrades: false, transports: ["websocket"] }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send(binaryData); }); - socket.on("open", function() { - socket.on("message", function(msg) { - for (var i = 0; i < binaryData.length; i++) { - var num = msg.readInt8(i); + socket.on("open", () => { + socket.on("message", msg => { + for (let i = 0; i < binaryData.length; i++) { + const num = msg.readInt8(i); expect(num).to.be(i); } done(); @@ -1483,26 +1487,28 @@ describe("server", function() { }); }); - it("should arrive when binary data is sent as Int32Array (ws)", function(done) { - var binaryData = new Int32Array(5); - for (var i = 0; i < binaryData.length; i++) { + it("should arrive when binary data is sent as Int32Array (ws)", done => { + const binaryData = new Int32Array(5); + for (let i = 0; i < binaryData.length; i++) { binaryData[i] = (i + 100) * 9823; } - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const opts = { allowUpgrades: false, transports: ["websocket"] }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send(binaryData); }); - socket.on("open", function() { - socket.on("message", function(msg) { - for (var i = 0, ii = 0; ii < binaryData.length; i += 4, ii++) { - var num = msg.readInt32LE(i); + socket.on("open", () => { + socket.on("message", msg => { + let i = 0, + ii = 0; + for (; ii < binaryData.length; i += 4, ii++) { + const num = msg.readInt32LE(i); expect(num).to.be((ii + 100) * 9823); } done(); @@ -1511,26 +1517,28 @@ describe("server", function() { }); }); - it("should arrive when binary data is sent as Int32Array, given as ArrayBuffer(ws)", function(done) { - var binaryData = new Int32Array(5); - for (var i = 0; i < binaryData.length; i++) { + it("should arrive when binary data is sent as Int32Array, given as ArrayBuffer(ws)", done => { + const binaryData = new Int32Array(5); + for (let i = 0; i < binaryData.length; i++) { binaryData[i] = (i + 100) * 9823; } - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const opts = { allowUpgrades: false, transports: ["websocket"] }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send(binaryData.buffer); }); - socket.on("open", function() { - socket.on("message", function(msg) { - for (var i = 0, ii = 0; ii < binaryData.length; i += 4, ii++) { - var num = msg.readInt32LE(i); + socket.on("open", () => { + socket.on("message", msg => { + let i = 0, + ii = 0; + for (; ii < binaryData.length; i += 4, ii++) { + const num = msg.readInt32LE(i); expect(num).to.be((ii + 100) * 9823); } done(); @@ -1539,26 +1547,26 @@ describe("server", function() { }); }); - it("should arrive when binary data is sent as Buffer (ws)", function(done) { - var binaryData = Buffer.allocUnsafe(5); - for (var i = 0; i < binaryData.length; i++) { + it("should arrive when binary data is sent as Buffer (ws)", done => { + const binaryData = Buffer.allocUnsafe(5); + for (let i = 0; i < binaryData.length; i++) { binaryData.writeInt8(i, i); } - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const opts = { allowUpgrades: false, transports: ["websocket"] }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send(binaryData); }); - socket.on("open", function() { - socket.on("message", function(msg) { - for (var i = 0; i < binaryData.length; i++) { - var num = msg.readInt8(i); + socket.on("open", () => { + socket.on("message", msg => { + for (let i = 0; i < binaryData.length; i++) { + const num = msg.readInt8(i); expect(num).to.be(i); } done(); @@ -1567,26 +1575,26 @@ describe("server", function() { }); }); - it("should arrive when binary data sent as Buffer (polling)", function(done) { - var binaryData = Buffer.allocUnsafe(5); - for (var i = 0; i < binaryData.length; i++) { + it("should arrive when binary data sent as Buffer (polling)", done => { + const binaryData = Buffer.allocUnsafe(5); + for (let i = 0; i < binaryData.length; i++) { binaryData.writeInt8(i, i); } - var opts = { allowUpgrades: false, transports: ["polling"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const opts = { allowUpgrades: false, transports: ["polling"] }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["polling"] }); - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send(binaryData); }); - socket.on("open", function() { - socket.on("message", function(msg) { - for (var i = 0; i < binaryData.length; i++) { - var num = msg.readInt8(i); + socket.on("open", () => { + socket.on("message", msg => { + for (let i = 0; i < binaryData.length; i++) { + const num = msg.readInt8(i); expect(num).to.be(i); } @@ -1596,28 +1604,28 @@ describe("server", function() { }); }); - it("should arrive as ArrayBuffer if requested when binary data sent as Buffer (ws)", function(done) { - var binaryData = Buffer.allocUnsafe(5); - for (var i = 0; i < binaryData.length; i++) { + it("should arrive as ArrayBuffer if requested when binary data sent as Buffer (ws)", done => { + const binaryData = Buffer.allocUnsafe(5); + for (let i = 0; i < binaryData.length; i++) { binaryData.writeInt8(i, i); } - var opts = { allowUpgrades: false, transports: ["websocket"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const opts = { allowUpgrades: false, transports: ["websocket"] }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); socket.binaryType = "arraybuffer"; - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send(binaryData); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg instanceof ArrayBuffer).to.be(true); - var intArray = new Int8Array(msg); - for (var i = 0; i < binaryData.length; i++) { + const intArray = new Int8Array(msg); + for (let i = 0; i < binaryData.length; i++) { expect(intArray[i]).to.be(i); } @@ -1627,28 +1635,28 @@ describe("server", function() { }); }); - it("should arrive as ArrayBuffer if requested when binary data sent as Buffer (polling)", function(done) { - var binaryData = Buffer.allocUnsafe(5); - for (var i = 0; i < binaryData.length; i++) { + 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++) { binaryData.writeInt8(i, i); } - var opts = { allowUpgrades: false, transports: ["polling"] }; - var engine = listen(opts, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const opts = { allowUpgrades: false, transports: ["polling"] }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["polling"] }); socket.binaryType = "arraybuffer"; - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send(binaryData); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg instanceof ArrayBuffer).to.be(true); - var intArray = new Int8Array(msg); - for (var i = 0; i < binaryData.length; i++) { + const intArray = new Int8Array(msg); + for (let i = 0; i < binaryData.length; i++) { expect(intArray[i]).to.be(i); } @@ -1658,27 +1666,27 @@ describe("server", function() { }); }); - it("should trigger a flush/drain event", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - engine.on("connection", function(socket) { - var totalEvents = 4; + it("should trigger a flush/drain event", done => { + const engine = listen({ allowUpgrades: false }, port => { + engine.on("connection", socket => { + let totalEvents = 4; - engine.on("flush", function(sock, buf) { + engine.on("flush", (sock, buf) => { expect(sock).to.be(socket); expect(buf).to.be.an("array"); --totalEvents || done(); }); - socket.on("flush", function(buf) { + socket.on("flush", buf => { expect(buf).to.be.an("array"); --totalEvents || done(); }); - engine.on("drain", function(sock) { + engine.on("drain", sock => { expect(sock).to.be(socket); expect(socket.writeBuffer.length).to.be(0); --totalEvents || done(); }); - socket.on("drain", function() { + socket.on("drain", () => { expect(socket.writeBuffer.length).to.be(0); --totalEvents || done(); }); @@ -1697,29 +1705,29 @@ describe("server", function() { this.slow(4000); this.timeout(8000); - var opts = { + const opts = { transports: ["websocket"], pingInterval: 200, pingTimeout: 100 }; - var engine = listen(opts, function(port) { - var messageCount = 100; - var messagePayload = new Array(256 * 256).join("a"); - var connection = null; - engine.on("connection", function(conn) { + const engine = listen(opts, port => { + const messageCount = 100; + const messagePayload = new Array(256 * 256).join("a"); + let connection = null; + engine.on("connection", conn => { connection = conn; }); - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - socket.on("open", function() { - for (var i = 0; i < messageCount; i++) { + socket.on("open", () => { + for (let i = 0; i < messageCount; i++) { // connection.send('message: ' + i); // works connection.send(messagePayload + "|message: " + i); // does not work } - var receivedCount = 0; - socket.on("message", function(msg) { + let receivedCount = 0; + socket.on("message", msg => { receivedCount += 1; if (receivedCount === messageCount) { done(); @@ -1730,32 +1738,32 @@ describe("server", function() { } ); - it("should support chinese", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var shi = "石室詩士施氏,嗜獅,誓食十獅。"; - var shi2 = "氏時時適市視獅。"; - engine.on("connection", function(conn) { + it("should support chinese", done => { + const engine = listen({ allowUpgrades: false }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + const shi = "石室詩士施氏,嗜獅,誓食十獅。"; + const shi2 = "氏時時適市視獅。"; + engine.on("connection", conn => { conn.send("."); conn.send(shi); conn.send(shi2); - conn.once("message", function(msg0) { + conn.once("message", msg0 => { expect(msg0).to.be("."); - conn.once("message", function(msg) { + conn.once("message", msg => { expect(msg).to.be(shi); - conn.once("message", function(msg2) { + conn.once("message", msg2 => { expect(msg2).to.be(shi2); done(); }); }); }); }); - socket.on("open", function() { - socket.once("message", function(msg0) { + socket.on("open", () => { + socket.once("message", msg0 => { expect(msg0).to.be("."); - socket.once("message", function(msg) { + socket.once("message", msg => { expect(msg).to.be(shi); - socket.once("message", function(msg2) { + socket.once("message", msg2 => { expect(msg2).to.be(shi2); socket.send("."); socket.send(shi); @@ -1767,8 +1775,8 @@ describe("server", function() { }); }); - it("should send and receive data with key and cert (polling)", function(done) { - var srvOpts = { + it("should send and receive data with key and cert (polling)", done => { + const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), ca: fs.readFileSync("test/fixtures/ca.crt"), @@ -1776,39 +1784,39 @@ describe("server", function() { rejectUnauthorized: true }; - var opts = { + const opts = { key: fs.readFileSync("test/fixtures/client.key"), cert: fs.readFileSync("test/fixtures/client.crt"), ca: fs.readFileSync("test/fixtures/ca.crt"), transports: ["polling"] }; - var srv = https.createServer(srvOpts, function(req, res) { + const srv = https.createServer(srvOpts, (req, res) => { res.writeHead(200); res.end("hello world\n"); }); - var engine = eio({ transports: ["polling"], allowUpgrades: false }); + const engine = eio({ transports: ["polling"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function() { - var port = srv.address().port; - var socket = new eioc.Socket("https://localhost:%d".s(port), opts); + srv.listen(() => { + const port = srv.address().port; + const socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { expect(msg).to.be("hello"); done(); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("hello"); }); }); }); - it("should send and receive data with ca when not requiring auth (polling)", function(done) { - var srvOpts = { + it("should send and receive data with ca when not requiring auth (polling)", done => { + const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), ca: fs.readFileSync("test/fixtures/ca.crt"), @@ -1816,37 +1824,37 @@ describe("server", function() { rejectUnauthorized: false }; - var opts = { + const opts = { ca: fs.readFileSync("test/fixtures/ca.crt"), transports: ["polling"] }; - var srv = https.createServer(srvOpts, function(req, res) { + const srv = https.createServer(srvOpts, (req, res) => { res.writeHead(200); res.end("hello world\n"); }); - var engine = eio({ transports: ["polling"], allowUpgrades: false }); + const engine = eio({ transports: ["polling"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function() { - var port = srv.address().port; - var socket = new eioc.Socket("https://localhost:%d".s(port), opts); + srv.listen(() => { + const port = srv.address().port; + const socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { expect(msg).to.be("hello"); done(); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("hello"); }); }); }); - it("should send and receive data with key and cert (ws)", function(done) { - var srvOpts = { + it("should send and receive data with key and cert (ws)", done => { + const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), ca: fs.readFileSync("test/fixtures/ca.crt"), @@ -1854,39 +1862,39 @@ describe("server", function() { rejectUnauthorized: true }; - var opts = { + const opts = { key: fs.readFileSync("test/fixtures/client.key"), cert: fs.readFileSync("test/fixtures/client.crt"), ca: fs.readFileSync("test/fixtures/ca.crt"), transports: ["websocket"] }; - var srv = https.createServer(srvOpts, function(req, res) { + const srv = https.createServer(srvOpts, (req, res) => { res.writeHead(200); res.end("hello world\n"); }); - var engine = eio({ transports: ["websocket"], allowUpgrades: false }); + const engine = eio({ transports: ["websocket"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function() { - var port = srv.address().port; - var socket = new eioc.Socket("https://localhost:%d".s(port), opts); + srv.listen(() => { + const port = srv.address().port; + const socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { expect(msg).to.be("hello"); done(); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("hello"); }); }); }); - it("should send and receive data with pfx (polling)", function(done) { - var srvOpts = { + it("should send and receive data with pfx (polling)", done => { + const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), ca: fs.readFileSync("test/fixtures/ca.crt"), @@ -1894,38 +1902,38 @@ describe("server", function() { rejectUnauthorized: true }; - var opts = { + const opts = { pfx: fs.readFileSync("test/fixtures/client.pfx"), ca: fs.readFileSync("test/fixtures/ca.crt"), transports: ["polling"] }; - var srv = https.createServer(srvOpts, function(req, res) { + const srv = https.createServer(srvOpts, (req, res) => { res.writeHead(200); res.end("hello world\n"); }); - var engine = eio({ transports: ["polling"], allowUpgrades: false }); + const engine = eio({ transports: ["polling"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function() { - var port = srv.address().port; - var socket = new eioc.Socket("https://localhost:%d".s(port), opts); + srv.listen(() => { + const port = srv.address().port; + const socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { expect(msg).to.be("hello"); done(); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("hello"); }); }); }); - it("should send and receive data with pfx (ws)", function(done) { - var srvOpts = { + it("should send and receive data with pfx (ws)", done => { + const srvOpts = { key: fs.readFileSync("test/fixtures/server.key"), cert: fs.readFileSync("test/fixtures/server.crt"), ca: fs.readFileSync("test/fixtures/ca.crt"), @@ -1933,71 +1941,71 @@ describe("server", function() { rejectUnauthorized: true }; - var opts = { + const opts = { pfx: fs.readFileSync("test/fixtures/client.pfx"), ca: fs.readFileSync("test/fixtures/ca.crt"), transports: ["websocket"] }; - var srv = https.createServer(srvOpts, function(req, res) { + const srv = https.createServer(srvOpts, (req, res) => { res.writeHead(200); res.end("hello world\n"); }); - var engine = eio({ transports: ["websocket"], allowUpgrades: false }); + const engine = eio({ transports: ["websocket"], allowUpgrades: false }); engine.attach(srv); - srv.listen(function() { - var port = srv.address().port; - var socket = new eioc.Socket("https://localhost:%d".s(port), opts); + srv.listen(() => { + const port = srv.address().port; + const socket = new eioc.Socket("https://localhost:%d".s(port), opts); - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { expect(msg).to.be("hello"); done(); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("hello"); }); }); }); }); - describe("send", function() { - describe("writeBuffer", function() { - it("should not empty until `drain` event (polling)", function(done) { - listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + describe("send", () => { + 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), { transports: ["polling"] }); - var totalEvents = 2; - socket.on("open", function() { + let totalEvents = 2; + socket.on("open", () => { socket.send("a"); socket.send("b"); // writeBuffer should be nonempty, with 'a' still in it expect(socket.writeBuffer.length).to.eql(2); }); - socket.transport.on("drain", function() { + socket.transport.on("drain", () => { expect(socket.writeBuffer.length).to.eql(--totalEvents); totalEvents || done(); }); }); }); - it("should not empty until `drain` event (websocket)", function(done) { - listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + it("should not empty until `drain` event (websocket)", done => { + listen({ allowUpgrades: false }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - var totalEvents = 2; - socket.on("open", function() { + let totalEvents = 2; + socket.on("open", () => { socket.send("a"); socket.send("b"); // writeBuffer should be nonempty, with 'a' still in it expect(socket.writeBuffer.length).to.eql(2); }); - socket.transport.on("drain", function() { + socket.transport.on("drain", () => { expect(socket.writeBuffer.length).to.eql(--totalEvents); totalEvents || done(); }); @@ -2005,23 +2013,23 @@ describe("server", function() { }); }); - describe("callback", function() { - it("should execute in order when message sent (client) (polling)", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["polling"] }); - var i = 0; - var j = 0; + let i = 0; + let j = 0; - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { conn.send(msg); }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { // send another packet until we've sent 3 total if (++i < 3) { expect(i).to.eql(j); @@ -2034,7 +2042,7 @@ describe("server", function() { function sendFn() { socket.send( j, - (function(value) { + (value => { j++; })(j) ); @@ -2045,22 +2053,22 @@ describe("server", function() { }); }); - it("should execute in order when message sent (client) (websocket)", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["websocket"] }); - var i = 0; - var j = 0; + let i = 0; + let j = 0; - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { conn.send(msg); }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { // send another packet until we've sent 3 total if (++i < 3) { expect(i).to.eql(j); @@ -2073,7 +2081,7 @@ describe("server", function() { function sendFn() { socket.send( j, - (function(value) { + (value => { j++; })(j) ); @@ -2084,22 +2092,22 @@ describe("server", function() { }); }); - it("should execute in order with payloads (client) (polling)", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["polling"] }); - var i = 0; - var lastCbFired = 0; + let i = 0; + let lastCbFired = 0; - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { conn.send(msg); }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.eql(i + 1); i++; }); @@ -2113,38 +2121,38 @@ describe("server", function() { } // 2 and 3 will be in the same payload - socket.once("flush", function() { - socket.send(2, function() { + socket.once("flush", () => { + socket.send(2, () => { cb(2); }); - socket.send(3, function() { + socket.send(3, () => { cb(3); }); }); - socket.send(1, function() { + socket.send(1, () => { cb(1); }); }); }); }); - it("should execute in order with payloads (client) (websocket)", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["websocket"] }); - var i = 0; - var lastCbFired = 0; + let i = 0; + let lastCbFired = 0; - engine.on("connection", function(conn) { - conn.on("message", function(msg) { + engine.on("connection", conn => { + conn.on("message", msg => { conn.send(msg); }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { expect(msg).to.eql(i + 1); i++; }); @@ -2158,91 +2166,91 @@ describe("server", function() { } // 2 and 3 will be in the same payload - socket.once("flush", function() { - socket.send(2, function() { + socket.once("flush", () => { + socket.send(2, () => { cb(2); }); - socket.send(3, function() { + socket.send(3, () => { cb(3); }); }); - socket.send(1, function() { + socket.send(1, () => { cb(1); }); }); }); }); - it("should execute when message sent (polling)", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + it("should execute when message sent (polling)", done => { + const engine = listen({ allowUpgrades: false }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["polling"] }); - var i = 0; - var j = 0; + let i = 0; + let j = 0; - engine.on("connection", function(conn) { - conn.send("a", function(transport) { + engine.on("connection", conn => { + conn.send("a", transport => { i++; }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { j++; }); }); - setTimeout(function() { + setTimeout(() => { expect(i).to.be(j); done(); }, 100); }); }); - it("should execute when message sent (websocket)", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + it("should execute when message sent (websocket)", done => { + const engine = listen({ allowUpgrades: false }, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["websocket"] }); - var i = 0; - var j = 0; + let i = 0; + let j = 0; - engine.on("connection", function(conn) { - conn.send("a", function(transport) { + engine.on("connection", conn => { + conn.send("a", transport => { i++; }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { j++; }); }); - setTimeout(function() { + setTimeout(() => { expect(i).to.be(j); done(); }, 100); }); }); - it("should execute once for each send", function(done) { - var engine = listen(function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var a = 0; - var b = 0; - var c = 0; - var all = 0; + it("should execute once for each send", done => { + const engine = listen(port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let a = 0; + let b = 0; + let c = 0; + let all = 0; - engine.on("connection", function(conn) { + engine.on("connection", conn => { conn.send("a"); conn.send("b"); conn.send("c"); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { if (msg === "a") a++; if (msg === "b") b++; if (msg === "c") c++; @@ -2258,81 +2266,81 @@ describe("server", function() { }); }); - it("should execute in multipart packet", function(done) { - var engine = listen(function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - var i = 0; - var j = 0; + it("should execute in multipart packet", done => { + const engine = listen(port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let i = 0; + let j = 0; - engine.on("connection", function(conn) { - conn.send("b", function(transport) { + engine.on("connection", conn => { + conn.send("b", transport => { i++; }); - conn.send("a", function(transport) { + conn.send("a", transport => { i++; }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { j++; }); }); - setTimeout(function() { + setTimeout(() => { expect(i).to.be(j); done(); }, 200); }); }); - it("should execute in multipart packet (polling)", function(done) { - var engine = listen(function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + it("should execute in multipart packet (polling)", done => { + const engine = listen(port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { transports: ["polling"] }); - var i = 0; - var j = 0; + let i = 0; + let j = 0; - engine.on("connection", function(conn) { - conn.send("d", function(transport) { + engine.on("connection", conn => { + conn.send("d", transport => { i++; }); - conn.send("c", function(transport) { + conn.send("c", transport => { i++; }); - conn.send("b", function(transport) { + conn.send("b", transport => { i++; }); - conn.send("a", function(transport) { + conn.send("a", transport => { i++; }); }); - socket.on("open", function() { - socket.on("message", function(msg) { + socket.on("open", () => { + socket.on("message", msg => { j++; }); }); - setTimeout(function() { + setTimeout(() => { expect(i).to.be(j); done(); }, 200); }); }); - it("should clean callback references when socket gets closed with pending callbacks", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["polling"] }); - engine.on("connection", function(conn) { - socket.transport.on("pollComplete", function() { - conn.send("a", function(transport) { + engine.on("connection", conn => { + socket.transport.on("pollComplete", () => { + conn.send("a", transport => { done(new Error("Test invalidation")); }); @@ -2344,7 +2352,7 @@ describe("server", function() { socket.close(); }); - conn.on("close", function(reason) { + conn.on("close", reason => { expect(conn.packetsFn).to.be.empty(); expect(conn.sentCallbackFn).to.be.empty(); done(); @@ -2353,23 +2361,23 @@ describe("server", function() { }); }); - it("should not execute when it is not actually sent (polling)", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + 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), { transports: ["polling"] }); - socket.transport.on("pollComplete", function(msg) { + socket.transport.on("pollComplete", msg => { socket.close(); }); - engine.on("connection", function(conn) { - var err; + engine.on("connection", conn => { + let err; conn.send("a"); - conn.send("b", function(transport) { + conn.send("b", transport => { err = new Error("Test invalidation"); }); - conn.on("close", function(reason) { + conn.on("close", reason => { done(err); }); }); @@ -2378,30 +2386,28 @@ describe("server", function() { }); }); - describe("packet", function() { - it("should emit when socket receives packet", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - engine.on("connection", function(conn) { - conn.on("packet", function(packet) { + 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)); + engine.on("connection", conn => { + conn.on("packet", packet => { expect(packet.type).to.be("message"); expect(packet.data).to.be("a"); done(); }); }); - socket.on("open", function() { + socket.on("open", () => { socket.send("a"); }); }); }); - it("should emit when receives pong", function(done) { - var engine = listen({ allowUpgrades: false, pingInterval: 4 }, function( - port - ) { + it("should emit when receives pong", done => { + const engine = listen({ allowUpgrades: false, pingInterval: 4 }, port => { eioc("ws://localhost:%d".s(port)); - engine.on("connection", function(conn) { - conn.on("packet", function(packet) { + engine.on("connection", conn => { + conn.on("packet", packet => { conn.close(); expect(packet.type).to.be("pong"); done(); @@ -2411,12 +2417,12 @@ describe("server", function() { }); }); - describe("packetCreate", function() { - it("should emit before socket send message", function(done) { - var engine = listen({ allowUpgrades: false }, function(port) { + describe("packetCreate", () => { + it("should emit before socket send message", done => { + const engine = listen({ allowUpgrades: false }, port => { eioc("ws://localhost:%d".s(port)); - engine.on("connection", function(conn) { - conn.on("packetCreate", function(packet) { + engine.on("connection", conn => { + conn.on("packetCreate", packet => { expect(packet.type).to.be("message"); expect(packet.data).to.be("a"); done(); @@ -2426,13 +2432,11 @@ describe("server", function() { }); }); - it("should emit before send pong", function(done) { - var engine = listen({ allowUpgrades: false, pingInterval: 4 }, function( - port - ) { + it("should emit before send pong", done => { + const engine = listen({ allowUpgrades: false, pingInterval: 4 }, port => { eioc("ws://localhost:%d".s(port)); - engine.on("connection", function(conn) { - conn.on("packetCreate", function(packet) { + engine.on("connection", conn => { + conn.on("packetCreate", packet => { conn.close(); expect(packet.type).to.be("ping"); done(); @@ -2442,24 +2446,25 @@ describe("server", function() { }); }); - describe("upgrade", function() { - it("should upgrade", function(done) { - var engine = listen(function(port) { + describe("upgrade", () => { + it("should upgrade", done => { + const engine = listen(port => { // it takes both to send 50 to verify - var ready = 2; - var closed = 2; + let ready = 2; + let closed = 2; + function finish() { - setTimeout(function() { + setTimeout(() => { socket.close(); }, 10); } // server - engine.on("connection", function(conn) { - var lastSent = 0; - var lastReceived = 0; - var upgraded = false; - var interval = setInterval(function() { + engine.on("connection", conn => { + let lastSent = 0; + let lastReceived = 0; + let upgraded = false; + const interval = setInterval(() => { lastSent++; conn.send(lastSent); if (50 === lastSent) { @@ -2470,20 +2475,20 @@ describe("server", function() { expect(conn.request._query.transport).to.be("polling"); - conn.on("message", function(msg) { + conn.on("message", msg => { expect(conn.request._query).to.be.an("object"); lastReceived++; expect(msg).to.eql(lastReceived); }); - conn.on("upgrade", function(to) { + conn.on("upgrade", to => { expect(conn.request._query.transport).to.be("polling"); upgraded = true; expect(to.name).to.be("websocket"); expect(conn.transport.name).to.be("websocket"); }); - conn.on("close", function(reason) { + conn.on("close", reason => { expect(reason).to.be("transport close"); expect(lastSent).to.be(50); expect(lastReceived).to.be(50); @@ -2494,11 +2499,11 @@ describe("server", function() { // client var socket = new eioc.Socket("ws://localhost:%d".s(port)); - socket.on("open", function() { - var lastSent = 0; - var lastReceived = 0; - var upgrades = 0; - var interval = setInterval(function() { + socket.on("open", () => { + let lastSent = 0; + let lastReceived = 0; + let upgrades = 0; + const interval = setInterval(() => { lastSent++; socket.send(lastSent); if (50 === lastSent) { @@ -2506,7 +2511,7 @@ describe("server", function() { --ready || finish(); } }, 2); - socket.on("upgrading", function(to) { + socket.on("upgrading", to => { // we want to make sure for the sake of this test that we have a buffer expect(to.name).to.equal("websocket"); upgrades++; @@ -2519,15 +2524,15 @@ describe("server", function() { expect(socket.writeBuffer).to.not.be.empty(); }); - socket.on("upgrade", function(to) { + socket.on("upgrade", to => { expect(to.name).to.equal("websocket"); upgrades++; }); - socket.on("message", function(msg) { + socket.on("message", msg => { lastReceived++; expect(lastReceived).to.eql(msg); }); - socket.on("close", function(reason) { + socket.on("close", reason => { expect(reason).to.be("forced close"); expect(lastSent).to.be(50); expect(upgrades).to.be(2); @@ -2541,19 +2546,17 @@ describe("server", function() { }); }); - describe("http compression", function() { + describe("http compression", () => { function getSidFromResponse(res) { - var c = cookieMod.parse(res.headers["set-cookie"][0]); + const c = cookieMod.parse(res.headers["set-cookie"][0]); return c[Object.keys(c)[0]]; } - it("should compress by default", function(done) { - var engine = listen({ cookie: true, transports: ["polling"] }, function( - port - ) { - engine.on("connection", function(conn) { - var buf = Buffer.allocUnsafe(1024); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + it("should compress by default", done => { + const engine = listen({ cookie: true, transports: ["polling"] }, port => { + engine.on("connection", conn => { + const buf = Buffer.allocUnsafe(1024); + for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf); }); @@ -2562,15 +2565,15 @@ describe("server", function() { port: port, path: "/engine.io/default/?transport=polling" }, - function(res) { - var sid = getSidFromResponse(res); + res => { + const sid = getSidFromResponse(res); http.get( { port: port, path: "/engine.io/default/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, - function(res) { + res => { expect(res.headers["content-encoding"]).to.equal("gzip"); res .pipe(zlib.createGunzip()) @@ -2584,13 +2587,11 @@ describe("server", function() { }); }); - it("should compress using deflate", function(done) { - var engine = listen({ cookie: true, transports: ["polling"] }, function( - port - ) { - engine.on("connection", function(conn) { - var buf = Buffer.allocUnsafe(1024); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + it("should compress using deflate", done => { + const engine = listen({ cookie: true, transports: ["polling"] }, port => { + engine.on("connection", conn => { + const buf = Buffer.allocUnsafe(1024); + for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf); }); @@ -2599,15 +2600,15 @@ describe("server", function() { port: port, path: "/engine.io/default/?transport=polling" }, - function(res) { - var sid = getSidFromResponse(res); + res => { + const sid = getSidFromResponse(res); http.get( { port: port, path: "/engine.io/default/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "deflate" } }, - function(res) { + res => { expect(res.headers["content-encoding"]).to.equal("deflate"); res .pipe(zlib.createDeflate()) @@ -2621,17 +2622,17 @@ describe("server", function() { }); }); - it("should set threshold", function(done) { - var engine = listen( + it("should set threshold", done => { + const engine = listen( { cookie: true, transports: ["polling"], httpCompression: { threshold: 0 } }, - function(port) { - engine.on("connection", function(conn) { - var buf = Buffer.allocUnsafe(10); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + port => { + engine.on("connection", conn => { + const buf = Buffer.allocUnsafe(10); + for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf); }); @@ -2640,15 +2641,15 @@ describe("server", function() { port: port, path: "/engine.io/default/?transport=polling" }, - function(res) { - var sid = getSidFromResponse(res); + res => { + const sid = getSidFromResponse(res); http.get( { port: port, path: "/engine.io/default/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, - function(res) { + res => { expect(res.headers["content-encoding"]).to.equal("gzip"); done(); } @@ -2659,13 +2660,13 @@ describe("server", function() { ); }); - it("should disable compression", function(done) { - var engine = listen( + it("should disable compression", done => { + const engine = listen( { cookie: true, transports: ["polling"], httpCompression: false }, - function(port) { - engine.on("connection", function(conn) { - var buf = Buffer.allocUnsafe(1024); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + port => { + engine.on("connection", conn => { + const buf = Buffer.allocUnsafe(1024); + for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf); }); @@ -2674,15 +2675,15 @@ describe("server", function() { port: port, path: "/engine.io/default/?transport=polling" }, - function(res) { - var sid = getSidFromResponse(res); + res => { + const sid = getSidFromResponse(res); http.get( { port: port, path: "/engine.io/default/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, - function(res) { + res => { expect(res.headers["content-encoding"]).to.be(undefined); done(); } @@ -2693,13 +2694,11 @@ describe("server", function() { ); }); - it("should disable compression per message", function(done) { - var engine = listen({ cookie: true, transports: ["polling"] }, function( - port - ) { - engine.on("connection", function(conn) { - var buf = Buffer.allocUnsafe(1024); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + it("should disable compression per message", done => { + const engine = listen({ cookie: true, transports: ["polling"] }, port => { + engine.on("connection", conn => { + const buf = Buffer.allocUnsafe(1024); + for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf, { compress: false }); }); @@ -2708,15 +2707,15 @@ describe("server", function() { port: port, path: "/engine.io/default/?transport=polling" }, - function(res) { - var sid = getSidFromResponse(res); + res => { + const sid = getSidFromResponse(res); http.get( { port: port, path: "/engine.io/default/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, - function(res) { + res => { expect(res.headers["content-encoding"]).to.be(undefined); done(); } @@ -2726,13 +2725,11 @@ describe("server", function() { }); }); - it("should not compress when the byte size is below threshold", function(done) { - var engine = listen({ cookie: true, transports: ["polling"] }, function( - port - ) { - engine.on("connection", function(conn) { - var buf = Buffer.allocUnsafe(100); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + it("should not compress when the byte size is below threshold", done => { + const engine = listen({ cookie: true, transports: ["polling"] }, port => { + engine.on("connection", conn => { + const buf = Buffer.allocUnsafe(100); + for (let i = 0; i < buf.length; i++) buf[i] = i % 0xff; conn.send(buf); }); @@ -2741,15 +2738,15 @@ describe("server", function() { port: port, path: "/engine.io/default/?transport=polling" }, - function(res) { - var sid = getSidFromResponse(res); + res => { + const sid = getSidFromResponse(res); http.get( { port: port, path: "/engine.io/default/?transport=polling&sid=" + sid, headers: { "Accept-Encoding": "gzip, deflate" } }, - function(res) { + res => { expect(res.headers["content-encoding"]).to.be(undefined); done(); } @@ -2760,15 +2757,15 @@ describe("server", function() { }); }); - describe("permessage-deflate", function() { - it("should set threshold", function(done) { - var engine = listen( + describe("permessage-deflate", () => { + it("should set threshold", done => { + const engine = listen( { transports: ["websocket"], perMessageDeflate: { threshold: 0 } }, - function(port) { - engine.on("connection", function(conn) { - var socket = conn.transport.socket; - var send = socket.send; - socket.send = function(data, opts, callback) { + port => { + engine.on("connection", conn => { + const socket = conn.transport.socket; + const send = socket.send; + socket.send = (data, opts, callback) => { socket.send = send; socket.send(data, opts, callback); @@ -2777,8 +2774,8 @@ describe("server", function() { done(); }; - var buf = Buffer.allocUnsafe(100); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + const buf = Buffer.allocUnsafe(100); + 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"] }); @@ -2786,14 +2783,14 @@ describe("server", function() { ); }); - it("should not compress when the byte size is below threshold", function(done) { - var engine = listen( + it("should not compress when the byte size is below threshold", done => { + const engine = listen( { transports: ["websocket"], perMessageDeflate: true }, - function(port) { - engine.on("connection", function(conn) { - var socket = conn.transport.socket; - var send = socket.send; - socket.send = function(data, opts, callback) { + port => { + engine.on("connection", conn => { + const socket = conn.transport.socket; + const send = socket.send; + socket.send = (data, opts, callback) => { socket.send = send; socket.send(data, opts, callback); @@ -2802,8 +2799,8 @@ describe("server", function() { done(); }; - var buf = Buffer.allocUnsafe(100); - for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff; + const buf = Buffer.allocUnsafe(100); + 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"] }); @@ -2815,42 +2812,42 @@ describe("server", function() { describe("extraHeaders", function() { this.timeout(5000); - var headers = { + const headers = { "x-custom-header-for-my-project": "my-secret-access-token", cookie: "user_session=NI2JlCKF90aE0sJZD9ZzujtdsUqNYSBYxzlTsvdSUe35ZzdtVRGqYFr0kdGxbfc5gUOkR9RGp20GVKza; path=/; expires=Tue, 07-Apr-2015 18:18:08 GMT; secure; HttpOnly" }; function testForTransport(transport, done) { - var engine = listen(function(port) { - var socket = new eioc.Socket("ws://localhost:%d".s(port), { + const engine = listen(port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port), { extraHeaders: headers, transports: [transport] }); - engine.on("connection", function(conn) { - for (var h in headers) { + engine.on("connection", conn => { + for (let h in headers) { expect(conn.request.headers[h]).to.equal(headers[h]); } done(); }); - socket.on("open", function() {}); + socket.on("open", () => {}); }); } - it("should arrive from client to server via WebSockets", function(done) { + it("should arrive from client to server via WebSockets", done => { testForTransport("websocket", done); }); - it("should arrive from client to server via XMLHttpRequest", function(done) { + it("should arrive from client to server via XMLHttpRequest", done => { testForTransport("polling", done); }); }); - describe("response headers", function() { + describe("response headers", () => { function testForHeaders(headers, done) { - var engine = listen(function(port) { - engine.on("connection", function(conn) { - conn.transport.once("headers", function(headers) { + const engine = listen(port => { + engine.on("connection", conn => { + conn.transport.once("headers", headers => { expect(headers["X-XSS-Protection"]).to.be("0"); conn.close(); done(); @@ -2864,16 +2861,16 @@ describe("server", function() { }); } - it("should contain X-XSS-Protection: 0 for IE8", function(done) { - var headers = { + it("should contain X-XSS-Protection: 0 for IE8", done => { + const headers = { "user-agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)" }; testForHeaders(headers, done); }); - it("should contain X-XSS-Protection: 0 for IE11", function(done) { - var headers = { + it("should contain X-XSS-Protection: 0 for IE11", done => { + const headers = { "user-agent": "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko" }; @@ -2881,7 +2878,7 @@ describe("server", function() { }); }); - describe("cors", function() { + describe("cors", () => { it("should allow CORS from the current origin (preflight request)", done => { listen( { cors: { origin: true, headers: ["my-header"], credentials: true } }, @@ -2890,7 +2887,7 @@ describe("server", function() { .options("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "polling" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); expect(res.status).to.be(204); expect(res.body).to.be.empty(); @@ -2920,7 +2917,7 @@ describe("server", function() { .get("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://engine.io") .query({ transport: "polling" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); expect(res.status).to.be(200); expect(res.body).to.be.empty(); @@ -2954,7 +2951,7 @@ describe("server", function() { .options("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://bad-domain.com") .query({ transport: "polling" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); expect(res.status).to.be(204); expect(res.body).to.be.empty(); @@ -2988,7 +2985,7 @@ describe("server", function() { .options("http://localhost:%d/engine.io/default/".s(port)) .set("Origin", "http://good-domain.com") .query({ transport: "polling" }) - .end(function(err, res) { + .end((err, res) => { expect(err).to.be(null); expect(res.status).to.be(200); expect(res.body).to.be.empty(); @@ -3015,29 +3012,30 @@ describe("server", function() { }); }); - describe("wsEngine option", function() { - it("should allow loading of other websocket server implementation like eiows", function(done) { - var engine = listen({ allowUpgrades: false, wsEngine: "eiows" }, function( - port - ) { - expect(engine.ws instanceof require("eiows").Server).to.be.ok(); - var socket = new eioc.Socket("ws://localhost:%d".s(port)); - engine.on("connection", function(conn) { - conn.send("a"); - }); - socket.on("open", function() { - socket.on("message", function(msg) { - expect(msg).to.be("a"); - done(); + describe("wsEngine option", () => { + it("should allow loading of other websocket server implementation like eiows", done => { + const engine = listen( + { allowUpgrades: false, wsEngine: "eiows" }, + port => { + expect(engine.ws instanceof require("eiows").Server).to.be.ok(); + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + engine.on("connection", conn => { + conn.send("a"); }); - }); - }); + socket.on("open", () => { + socket.on("message", msg => { + expect(msg).to.be("a"); + done(); + }); + }); + } + ); }); }); - describe("remoteAddress", function() { - it("should be defined (polling)", function(done) { - var engine = listen({ transports: ["polling"] }, port => { + describe("remoteAddress", () => { + it("should be defined (polling)", done => { + const engine = listen({ transports: ["polling"] }, port => { eioc("ws://localhost:%d".s(port), { transports: ["polling"] }); engine.on("connection", socket => { expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); @@ -3046,8 +3044,8 @@ describe("server", function() { }); }); - it("should be defined (ws)", function(done) { - var engine = listen({ transports: ["websocket"] }, port => { + it("should be defined (ws)", done => { + const engine = listen({ transports: ["websocket"] }, port => { eioc("ws://localhost:%d".s(port), { transports: ["websocket"] }); engine.on("connection", socket => { expect(socket.remoteAddress).to.be("::ffff:127.0.0.1"); From e7115b88bb75beca57d13380113531408cda4770 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 7 Dec 2020 11:26:35 +0100 Subject: [PATCH 056/124] chore(release): 4.0.5 Diff: https://github.com/socketio/engine.io/compare/4.0.4...4.0.5 --- CHANGELOG.md | 4 ++++ package-lock.json | 19 +++++++++++-------- package.json | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 890fca972..e0d6478df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [4.0.5](https://github.com/socketio/engine.io/compare/4.0.4...4.0.5) (2020-12-07) + +No change on the server-side, this matches the client release. + ## [4.0.4](https://github.com/socketio/engine.io/compare/4.0.3...4.0.4) (2020-11-17) No change on the server-side, this matches the client release. diff --git a/package-lock.json b/package-lock.json index f77d695a7..daa7ffe3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.4", + "version": "4.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -502,9 +502,9 @@ "dev": true }, "engine.io-client": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.4.tgz", - "integrity": "sha512-and4JRvjv+BQ4WBLopYUFePxju3ms3aBRk0XjaLdh/t9TKv2LCKtKKWFRoRzIfUZsu3U38FcYqNLuXhfS16vqw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.5.tgz", + "integrity": "sha512-1lkn0QdekHQPMTcxUh8LqIuxQHNtKV5GvqkQzmZ1rYKAvB6puMm13U7K1ps3OQZ4joE46asQiAKrcdL9weNEVw==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", @@ -520,10 +520,13 @@ }, "dependencies": { "engine.io-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.1.tgz", - "integrity": "sha512-v5aZK1hlckcJDGmHz3W8xvI3NUHYc9t8QtTbqdR5OaH3S9iJZilPubauOm+vLWOMMWzpE3hiq92l9lTAHamRCg==", - "dev": true + "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==", + "dev": true, + "requires": { + "base64-arraybuffer": "0.1.4" + } }, "ws": { "version": "7.2.5", diff --git a/package.json b/package.json index f02b4ce59..e9e5a04c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.4", + "version": "4.0.5", "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.0.4", + "engine.io-client": "4.0.5", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From 04ea3585e8e6a82992b6cfb363efc91a534a898f Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 30 Dec 2020 10:01:18 +0100 Subject: [PATCH 057/124] docs(changelog): include changelog for release 3.5.0 Merged from the 3.5.x branch. --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0d6478df..46f8be48e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# [3.5.0](https://github.com/socketio/engine.io/compare/3.4.2...3.5.0) (2020-12-30) + + +### Features + +* add support for all cookie options ([19cc582](https://github.com/socketio/engine.io/commit/19cc58264a06dca47ed401fbaca32dcdb80a903b)), closes [/github.com/jshttp/cookie#options-1](https://github.com//github.com/jshttp/cookie/issues/options-1) +* disable perMessageDeflate by default ([5ad2736](https://github.com/socketio/engine.io/commit/5ad273601eb66c7b318542f87026837bf9dddd21)) + + + ## [4.0.5](https://github.com/socketio/engine.io/compare/4.0.4...4.0.5) (2020-12-07) No change on the server-side, this matches the client release. From cec27502f5b55c8a2ff289db34019629bf6a97ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDarkovi=C4=87?= Date: Wed, 30 Dec 2020 10:04:47 +0100 Subject: [PATCH 058/124] fix: correctly pass the options when using the Server constructor (#610) Fixes https://github.com/socketio/engine.io/issues/580 --- lib/engine.io.js | 2 +- test/engine.io.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/engine.io.js b/lib/engine.io.js index 2c1ce9d10..43f7c371b 100644 --- a/lib/engine.io.js +++ b/lib/engine.io.js @@ -26,7 +26,7 @@ exports = module.exports = function() { } // if first argument is not an http server, then just make a regular eio server - return new Server(arguments); + return new Server(...arguments); }; /** diff --git a/test/engine.io.js b/test/engine.io.js index 121754ccf..23f7034e6 100644 --- a/test/engine.io.js +++ b/test/engine.io.js @@ -25,6 +25,11 @@ describe("engine", () => { expect(engine).to.be.an(eio.Server); expect(engine.ws).to.be.ok(); }); + + it("should pass options correctly to the Server", () => { + const engine = eio({ cors: true }); + expect(engine.opts).to.have.property("cors", true); + }); }); describe("listen", () => { From fffa0a3fb70dd05a426e47fa300531f020078a4a Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 30 Dec 2020 10:24:16 +0100 Subject: [PATCH 059/124] chore: update GitHub issue templates --- .github/ISSUE_TEMPLATE.md | 25 --------- .github/ISSUE_TEMPLATE/bug_report.md | 66 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++ 4 files changed, 91 insertions(+), 25 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 196c48004..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,25 +0,0 @@ - -*Note*: for support questions, please use one of these channels: [stackoverflow](http://stackoverflow.com/questions/tagged/engine.io) or [slack](https://socketio.slack.com) - -### You want to: - -* [x] report a *bug* -* [ ] request a *feature* - -### Current behaviour - - -### Steps to reproduce (if the current behaviour is a bug) - - -### Expected behaviour - - -### Setup -- OS: -- browser: -- engine.io version: - -### Other information (e.g. stacktraces, related issues, suggestions how to fix) - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..7deb7968f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,66 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** + +Please fill the following code example: + +Engine.IO server version: `x.y.z` + +*Server* + +```js +const engine = require("engine.io"); +const server = engine.listen(3000, {}); + +server.on("connection", (socket) => { + console.log("connection"); + + socket.on("message", (data) => { + console.log("data", data); + }); + + socket.on("close", () => { + console.log("close"); + }); +}); +``` + +Engine.IO client version: `x.y.z` + +*Client* + +```js +const socket = require("engine.io-client")("ws://localhost:3000"); + +socket.on("open", () => { + console.log("open"); + + socket.on("message", (data) => { + console.log("data", data); + }); + + socket.on("close", () => { + console.log("close"); + }); +}); +``` + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Platform:** + - Device: [e.g. Samsung S8] + - OS: [e.g. Android 9.2] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..fe32cb299 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a Question + url: https://github.com/socketio/socket.io/discussions/new?category=q-a + about: Ask the community for help diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..36014cde5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: 'enhancement' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 5a91253f2132e67780ca3d788168c74c36640904 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 4 Jan 2021 23:03:49 +0100 Subject: [PATCH 060/124] chore: bump ws and debug versions --- package-lock.json | 23 ++++++++++++++++------- package.json | 4 ++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index daa7ffe3a..3811b2d07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -461,11 +461,11 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "deep-is": { @@ -519,6 +519,15 @@ "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==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, "engine.io-parser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", @@ -1610,9 +1619,9 @@ } }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, "xmlhttprequest-ssl": { "version": "1.5.5", diff --git a/package.json b/package.json index e9e5a04c9..8ce4eba97 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,9 @@ "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" }, "devDependencies": { "babel-eslint": "^8.0.2", From 9b621523efa7598ee2043d608db9ce07f2b67cb0 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 4 Jan 2021 23:27:25 +0100 Subject: [PATCH 061/124] chore(release): 4.0.6 Diff: https://github.com/socketio/engine.io/compare/4.0.5...4.0.6 --- CHANGELOG.md | 9 +++++++++ package-lock.json | 27 ++++++--------------------- package.json | 4 ++-- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46f8be48e..0c4aa00d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [4.0.6](https://github.com/socketio/engine.io/compare/4.0.5...4.0.6) (2021-01-04) + + +### Bug Fixes + +* correctly pass the options when using the Server constructor ([#610](https://github.com/socketio/engine.io/issues/610)) ([cec2750](https://github.com/socketio/engine.io/commit/cec27502f5b55c8a2ff289db34019629bf6a97ca)) + + + # [3.5.0](https://github.com/socketio/engine.io/compare/3.4.2...3.5.0) (2020-12-30) diff --git a/package-lock.json b/package-lock.json index 3811b2d07..fcb60db66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.5", + "version": "4.0.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -502,32 +502,23 @@ "dev": true }, "engine.io-client": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.5.tgz", - "integrity": "sha512-1lkn0QdekHQPMTcxUh8LqIuxQHNtKV5GvqkQzmZ1rYKAvB6puMm13U7K1ps3OQZ4joE46asQiAKrcdL9weNEVw==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.6.tgz", + "integrity": "sha512-5lPh8rrhxIruo5ZlgFt31KM626o5OCXrCHBweieWWuVicDtnYdz/iR93k6N9k0Xs61WrYxZKIWXzeSaJF6fpNA==", "dev": true, "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.6", - "ws": "~7.2.1", + "ws": "~7.4.2", "xmlhttprequest-ssl": "~1.5.4", "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==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, "engine.io-parser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", @@ -536,12 +527,6 @@ "requires": { "base64-arraybuffer": "0.1.4" } - }, - "ws": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", - "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==", - "dev": true } } }, diff --git a/package.json b/package.json index 8ce4eba97..4113505b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.5", + "version": "4.0.6", "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.0.5", + "engine.io-client": "4.0.6", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", "expect.js": "^0.3.1", From 663d326d18de598318bd2120b2b70cd51adf8955 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sat, 9 Jan 2021 16:00:27 +0100 Subject: [PATCH 062/124] feat: add support for v3.x clients In order to ease the migration to Socket.IO v3, the Engine.IO server can now communicate with v3.x clients. ```js const eioServer = require("engine.io")(httpServer, { allowEIO3: true // false by default }); ``` If `allowEIO3` is false, the v3.x clients will now receive an HTTP 400 response ("Unsupported protocol version"). Note: the code of the v3 parser has been imported from [1] and browser-related dependencies were removed. [1]: https://github.com/socketio/engine.io-parser/tree/2.2.1 Related: - https://github.com/socketio/engine.io-protocol/issues/35 - https://github.com/socketio/socket.io-protocol/issues/21 --- .prettierignore | 1 + README.md | 1 + lib/parser-v3/index.js | 483 ++++++++++++++++++++++++ lib/parser-v3/utf8.js | 210 +++++++++++ lib/server.js | 22 +- lib/socket.js | 28 +- lib/transport.js | 7 +- lib/transports/polling.js | 43 ++- lib/transports/websocket.js | 3 +- package-lock.json | 96 +++++ package.json | 3 +- test/common.js | 8 + test/fixtures/server-close-upgraded.js | 2 +- test/fixtures/server-close-upgrading.js | 2 +- test/fixtures/server-close.js | 2 +- test/jsonp.js | 2 +- test/server.js | 166 ++++++-- 17 files changed, 1013 insertions(+), 66 deletions(-) create mode 100644 .prettierignore create mode 100644 lib/parser-v3/index.js create mode 100644 lib/parser-v3/utf8.js diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..935d77928 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +lib/parser-v3/ diff --git a/README.md b/README.md index 62c800e1b..90cc933e5 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,7 @@ to a single process. - `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. - `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`) - `close` - Closes all clients - **Returns** `Server` for chaining diff --git a/lib/parser-v3/index.js b/lib/parser-v3/index.js new file mode 100644 index 000000000..bd5d56baa --- /dev/null +++ b/lib/parser-v3/index.js @@ -0,0 +1,483 @@ +// imported from https://github.com/socketio/engine.io-parser/tree/2.2.x + +/** + * Module dependencies. + */ + +var utf8 = require('./utf8'); + +/** + * Current protocol version. + */ +exports.protocol = 3; + +const hasBinary = (packets) => { + for (const packet of packets) { + if (packet.data instanceof ArrayBuffer || ArrayBuffer.isView(packet.data)) { + return true; + } + } + return false; +} + +/** + * Packet types. + */ + +var packets = exports.packets = { + open: 0 // non-ws + , close: 1 // non-ws + , ping: 2 + , pong: 3 + , message: 4 + , upgrade: 5 + , noop: 6 +}; + +var packetslist = Object.keys(packets); + +/** + * Premade error packet. + */ + +var err = { type: 'error', data: 'parser error' }; + +const EMPTY_BUFFER = Buffer.concat([]); + +/** + * Encodes a packet. + * + * [ ] + * + * Example: + * + * 5hello world + * 3 + * 4 + * + * Binary is encoded in an identical principle + * + * @api private + */ + +exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) { + if (typeof supportsBinary === 'function') { + callback = supportsBinary; + supportsBinary = null; + } + + if (typeof utf8encode === 'function') { + callback = utf8encode; + utf8encode = null; + } + + if (Buffer.isBuffer(packet.data)) { + return encodeBuffer(packet, supportsBinary, callback); + } else if (packet.data && (packet.data.buffer || packet.data) instanceof ArrayBuffer) { + return encodeBuffer({ type: packet.type, data: arrayBufferToBuffer(packet.data) }, supportsBinary, callback); + } + + // Sending data as a utf-8 string + var encoded = packets[packet.type]; + + // data fragment is optional + if (undefined !== packet.data) { + encoded += utf8encode ? utf8.encode(String(packet.data), { strict: false }) : String(packet.data); + } + + return callback('' + encoded); +}; + +/** + * Encode Buffer data + */ + +function encodeBuffer(packet, supportsBinary, callback) { + if (!supportsBinary) { + return exports.encodeBase64Packet(packet, callback); + } + + var data = packet.data; + var typeBuffer = Buffer.allocUnsafe(1); + typeBuffer[0] = packets[packet.type]; + return callback(Buffer.concat([typeBuffer, data])); +} + +/** + * Encodes a packet with binary data in a base64 string + * + * @param {Object} packet, has `type` and `data` + * @return {String} base64 encoded message + */ + +exports.encodeBase64Packet = function(packet, callback){ + var data = Buffer.isBuffer(packet.data) ? packet.data : arrayBufferToBuffer(packet.data); + var message = 'b' + packets[packet.type]; + message += data.toString('base64'); + return callback(message); +}; + +/** + * Decodes a packet. Data also available as an ArrayBuffer if requested. + * + * @return {Object} with `type` and `data` (if any) + * @api private + */ + +exports.decodePacket = function (data, binaryType, utf8decode) { + if (data === undefined) { + return err; + } + + var type; + + // String data + if (typeof data === 'string') { + + type = data.charAt(0); + + if (type === 'b') { + return exports.decodeBase64Packet(data.substr(1), binaryType); + } + + if (utf8decode) { + data = tryDecode(data); + if (data === false) { + return err; + } + } + + if (Number(type) != type || !packetslist[type]) { + return err; + } + + if (data.length > 1) { + return { type: packetslist[type], data: data.substring(1) }; + } else { + return { type: packetslist[type] }; + } + } + + // Binary data + if (binaryType === 'arraybuffer') { + // wrap Buffer/ArrayBuffer data into an Uint8Array + var intArray = new Uint8Array(data); + type = intArray[0]; + return { type: packetslist[type], data: intArray.buffer.slice(1) }; + } + + if (data instanceof ArrayBuffer) { + data = arrayBufferToBuffer(data); + } + type = data[0]; + return { type: packetslist[type], data: data.slice(1) }; +}; + +function tryDecode(data) { + try { + data = utf8.decode(data, { strict: false }); + } catch (e) { + return false; + } + return data; +} + +/** + * Decodes a packet encoded in a base64 string. + * + * @param {String} base64 encoded message + * @return {Object} with `type` and `data` (if any) + */ + +exports.decodeBase64Packet = function(msg, binaryType) { + var type = packetslist[msg.charAt(0)]; + var data = Buffer.from(msg.substr(1), 'base64'); + if (binaryType === 'arraybuffer') { + var abv = new Uint8Array(data.length); + for (var i = 0; i < abv.length; i++){ + abv[i] = data[i]; + } + data = abv.buffer; + } + return { type: type, data: data }; +}; + +/** + * Encodes multiple messages (payload). + * + * :data + * + * Example: + * + * 11:hello world2:hi + * + * If any contents are binary, they will be encoded as base64 strings. Base64 + * encoded strings are marked with a b before the length specifier + * + * @param {Array} packets + * @api private + */ + +exports.encodePayload = function (packets, supportsBinary, callback) { + if (typeof supportsBinary === 'function') { + callback = supportsBinary; + supportsBinary = null; + } + + if (supportsBinary && hasBinary(packets)) { + return exports.encodePayloadAsBinary(packets, callback); + } + + if (!packets.length) { + return callback('0:'); + } + + function encodeOne(packet, doneCallback) { + exports.encodePacket(packet, supportsBinary, false, function(message) { + doneCallback(null, setLengthHeader(message)); + }); + } + + map(packets, encodeOne, function(err, results) { + return callback(results.join('')); + }); +}; + +function setLengthHeader(message) { + return message.length + ':' + message; +} + +/** + * Async array map using after + */ + +function map(ary, each, done) { + const results = new Array(ary.length); + let count = 0; + + for (let i = 0; i < ary.length; i++) { + each(ary[i], (error, msg) => { + results[i] = msg; + if (++count === ary.length) { + done(null, results); + } + }); + } +} + +/* + * Decodes data when a payload is maybe expected. Possible binary contents are + * decoded from their base64 representation + * + * @param {String} data, callback method + * @api public + */ + +exports.decodePayload = function (data, binaryType, callback) { + if (typeof data !== 'string') { + return exports.decodePayloadAsBinary(data, binaryType, callback); + } + + if (typeof binaryType === 'function') { + callback = binaryType; + binaryType = null; + } + + if (data === '') { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + var length = '', n, msg, packet; + + for (var i = 0, l = data.length; i < l; i++) { + var chr = data.charAt(i); + + if (chr !== ':') { + length += chr; + continue; + } + + if (length === '' || (length != (n = Number(length)))) { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + msg = data.substr(i + 1, n); + + if (length != msg.length) { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + if (msg.length) { + packet = exports.decodePacket(msg, binaryType, false); + + if (err.type === packet.type && err.data === packet.data) { + // parser error in individual packet - ignoring payload + return callback(err, 0, 1); + } + + var more = callback(packet, i + n, l); + if (false === more) return; + } + + // advance cursor + i += n; + length = ''; + } + + if (length !== '') { + // parser error - ignoring payload + return callback(err, 0, 1); + } + +}; + +/** + * + * Converts a buffer to a utf8.js encoded string + * + * @api private + */ + +function bufferToString(buffer) { + var str = ''; + for (var i = 0, l = buffer.length; i < l; i++) { + str += String.fromCharCode(buffer[i]); + } + return str; +} + +/** + * + * Converts a utf8.js encoded string to a buffer + * + * @api private + */ + +function stringToBuffer(string) { + var buf = Buffer.allocUnsafe(string.length); + for (var i = 0, l = string.length; i < l; i++) { + buf.writeUInt8(string.charCodeAt(i), i); + } + return buf; +} + +/** + * + * Converts an ArrayBuffer to a Buffer + * + * @api private + */ + +function arrayBufferToBuffer(data) { + // data is either an ArrayBuffer or ArrayBufferView. + var length = data.byteLength || data.length; + var offset = data.byteOffset || 0; + + return Buffer.from(data.buffer || data, offset, length); +} + +/** + * Encodes multiple messages (payload) as binary. + * + * <1 = binary, 0 = string>[...] + * + * Example: + * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers + * + * @param {Array} packets + * @return {Buffer} encoded payload + * @api private + */ + +exports.encodePayloadAsBinary = function (packets, callback) { + if (!packets.length) { + return callback(EMPTY_BUFFER); + } + + map(packets, encodeOneBinaryPacket, function(err, results) { + return callback(Buffer.concat(results)); + }); +}; + +function encodeOneBinaryPacket(p, doneCallback) { + + function onBinaryPacketEncode(packet) { + + var encodingLength = '' + packet.length; + var sizeBuffer; + + if (typeof packet === 'string') { + sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2); + sizeBuffer[0] = 0; // is a string (not true binary = 0) + for (var i = 0; i < encodingLength.length; i++) { + sizeBuffer[i + 1] = parseInt(encodingLength[i], 10); + } + sizeBuffer[sizeBuffer.length - 1] = 255; + return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)])); + } + + sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2); + sizeBuffer[0] = 1; // is binary (true binary = 1) + for (var i = 0; i < encodingLength.length; i++) { + sizeBuffer[i + 1] = parseInt(encodingLength[i], 10); + } + sizeBuffer[sizeBuffer.length - 1] = 255; + + doneCallback(null, Buffer.concat([sizeBuffer, packet])); + } + + exports.encodePacket(p, true, true, onBinaryPacketEncode); + +} + + +/* + * Decodes data when a payload is maybe expected. Strings are decoded by + * interpreting each byte as a key code for entries marked to start with 0. See + * description of encodePayloadAsBinary + + * @param {Buffer} data, callback method + * @api public + */ + +exports.decodePayloadAsBinary = function (data, binaryType, callback) { + if (typeof binaryType === 'function') { + callback = binaryType; + binaryType = null; + } + + var bufferTail = data; + var buffers = []; + var i; + + while (bufferTail.length > 0) { + var strLen = ''; + var isString = bufferTail[0] === 0; + for (i = 1; ; i++) { + if (bufferTail[i] === 255) break; + // 310 = char length of Number.MAX_VALUE + if (strLen.length > 310) { + return callback(err, 0, 1); + } + strLen += '' + bufferTail[i]; + } + bufferTail = bufferTail.slice(strLen.length + 1); + + var msgLength = parseInt(strLen, 10); + + var msg = bufferTail.slice(1, msgLength + 1); + if (isString) msg = bufferToString(msg); + buffers.push(msg); + bufferTail = bufferTail.slice(msgLength + 1); + } + + var total = buffers.length; + for (i = 0; i < total; i++) { + var buffer = buffers[i]; + callback(exports.decodePacket(buffer, binaryType, true), i, total); + } +}; diff --git a/lib/parser-v3/utf8.js b/lib/parser-v3/utf8.js new file mode 100644 index 000000000..b878740ff --- /dev/null +++ b/lib/parser-v3/utf8.js @@ -0,0 +1,210 @@ +/*! https://mths.be/utf8js v2.1.2 by @mathias */ + +var stringFromCharCode = String.fromCharCode; + +// Taken from https://mths.be/punycode +function ucs2decode(string) { + var output = []; + var counter = 0; + var length = string.length; + var value; + var extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; +} + +// Taken from https://mths.be/punycode +function ucs2encode(array) { + var length = array.length; + var index = -1; + var value; + var output = ''; + while (++index < length) { + value = array[index]; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + } + return output; +} + +function checkScalarValue(codePoint, strict) { + if (codePoint >= 0xD800 && codePoint <= 0xDFFF) { + if (strict) { + throw Error( + 'Lone surrogate U+' + codePoint.toString(16).toUpperCase() + + ' is not a scalar value' + ); + } + return false; + } + return true; +} +/*--------------------------------------------------------------------------*/ + +function createByte(codePoint, shift) { + return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); +} + +function encodeCodePoint(codePoint, strict) { + if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence + return stringFromCharCode(codePoint); + } + var symbol = ''; + if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence + symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); + } + else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence + if (!checkScalarValue(codePoint, strict)) { + codePoint = 0xFFFD; + } + symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); + symbol += createByte(codePoint, 6); + } + else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence + symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); + symbol += createByte(codePoint, 12); + symbol += createByte(codePoint, 6); + } + symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); + return symbol; +} + +function utf8encode(string, opts) { + opts = opts || {}; + var strict = false !== opts.strict; + + var codePoints = ucs2decode(string); + var length = codePoints.length; + var index = -1; + var codePoint; + var byteString = ''; + while (++index < length) { + codePoint = codePoints[index]; + byteString += encodeCodePoint(codePoint, strict); + } + return byteString; +} + +/*--------------------------------------------------------------------------*/ + +function readContinuationByte() { + if (byteIndex >= byteCount) { + throw Error('Invalid byte index'); + } + + var continuationByte = byteArray[byteIndex] & 0xFF; + byteIndex++; + + if ((continuationByte & 0xC0) == 0x80) { + return continuationByte & 0x3F; + } + + // If we end up here, it’s not a continuation byte + throw Error('Invalid continuation byte'); +} + +function decodeSymbol(strict) { + var byte1; + var byte2; + var byte3; + var byte4; + var codePoint; + + if (byteIndex > byteCount) { + throw Error('Invalid byte index'); + } + + if (byteIndex == byteCount) { + return false; + } + + // Read first byte + byte1 = byteArray[byteIndex] & 0xFF; + byteIndex++; + + // 1-byte sequence (no continuation bytes) + if ((byte1 & 0x80) == 0) { + return byte1; + } + + // 2-byte sequence + if ((byte1 & 0xE0) == 0xC0) { + byte2 = readContinuationByte(); + codePoint = ((byte1 & 0x1F) << 6) | byte2; + if (codePoint >= 0x80) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 3-byte sequence (may include unpaired surrogates) + if ((byte1 & 0xF0) == 0xE0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; + if (codePoint >= 0x0800) { + return checkScalarValue(codePoint, strict) ? codePoint : 0xFFFD; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 4-byte sequence + if ((byte1 & 0xF8) == 0xF0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + byte4 = readContinuationByte(); + codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) | + (byte3 << 0x06) | byte4; + if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { + return codePoint; + } + } + + throw Error('Invalid UTF-8 detected'); +} + +var byteArray; +var byteCount; +var byteIndex; +function utf8decode(byteString, opts) { + opts = opts || {}; + var strict = false !== opts.strict; + + byteArray = ucs2decode(byteString); + byteCount = byteArray.length; + byteIndex = 0; + var codePoints = []; + var tmp; + while ((tmp = decodeSymbol(strict)) !== false) { + codePoints.push(tmp); + } + return ucs2encode(codePoints); +} + +module.exports = { + version: '2.1.2', + encode: utf8encode, + decode: utf8decode +}; diff --git a/lib/server.js b/lib/server.js index 6033b0b04..a7007357c 100644 --- a/lib/server.js +++ b/lib/server.js @@ -32,7 +32,8 @@ class Server extends EventEmitter { httpCompression: { threshold: 1024 }, - cors: false + cors: false, + allowEIO3: false }, opts ); @@ -228,6 +229,17 @@ class Server extends EventEmitter { * @api private */ async handshake(transportName, req) { + const protocol = req._query.EIO === "3" ? 3 : 4; // 4th revision by default + if (protocol === 3 && !this.opts.allowEIO3) { + debug("unsupported protocol version"); + sendErrorMessage( + req, + req.res, + Server.errors.UNSUPPORTED_PROTOCOL_VERSION + ); + return; + } + let id; try { id = await this.generateId(req); @@ -258,7 +270,7 @@ class Server extends EventEmitter { sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST); return; } - const socket = new Socket(id, this, transport, req); + const socket = new Socket(id, this, transport, req, protocol); const self = this; if (this.opts.cookie) { @@ -442,7 +454,8 @@ Server.errors = { UNKNOWN_SID: 1, BAD_HANDSHAKE_METHOD: 2, BAD_REQUEST: 3, - FORBIDDEN: 4 + FORBIDDEN: 4, + UNSUPPORTED_PROTOCOL_VERSION: 5 }; Server.errorMessages = { @@ -450,7 +463,8 @@ Server.errorMessages = { 1: "Session ID unknown", 2: "Bad handshake method", 3: "Bad request", - 4: "Forbidden" + 4: "Forbidden", + 5: "Unsupported protocol version" }; /** diff --git a/lib/socket.js b/lib/socket.js index f9236cdde..debf1a6a4 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -7,7 +7,7 @@ class Socket extends EventEmitter { * * @api private */ - constructor(id, server, transport, req) { + constructor(id, server, transport, req, protocol) { super(); this.id = id; this.server = server; @@ -19,6 +19,7 @@ class Socket extends EventEmitter { this.sentCallbackFn = []; this.cleanupFn = []; this.request = req; + this.protocol = protocol; // Cache IP since it might not be in the req later if (req.websocket && req.websocket._socket) { @@ -61,7 +62,16 @@ class Socket extends EventEmitter { } this.emit("open"); - this.schedulePing(); + + if (this.protocol === 3) { + // in protocol v3, the client sends a ping, and the server answers with a pong + this.resetPingTimeout( + this.server.opts.pingInterval + this.server.opts.pingTimeout + ); + } else { + // in protocol v4, the server sends a ping, and the client answers with a pong + this.schedulePing(); + } } /** @@ -83,7 +93,21 @@ class Socket extends EventEmitter { ); 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"); diff --git a/lib/transport.js b/lib/transport.js index f45c5911b..7fb2603a5 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -1,5 +1,6 @@ const EventEmitter = require("events"); -const parser = require("engine.io-parser"); +const parser_v4 = require("engine.io-parser"); +const parser_v3 = require("./parser-v3/index"); const debug = require("debug")("engine:transport"); /** @@ -21,6 +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; } /** @@ -90,7 +93,7 @@ class Transport extends EventEmitter { * @api private */ onData(data) { - this.onPacket(parser.decodePacket(data)); + this.onPacket(this.parser.decodePacket(data)); } /** diff --git a/lib/transports/polling.js b/lib/transports/polling.js index c49583ab3..e84f2773a 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -1,5 +1,4 @@ const Transport = require("../transport"); -const parser = require("engine.io-parser"); const zlib = require("zlib"); const accepts = require("accepts"); const debug = require("debug")("engine:polling"); @@ -109,10 +108,16 @@ class Polling extends Transport { 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 chunks = isBinary ? Buffer.concat([]) : ""; const self = this; function cleanup() { @@ -129,11 +134,16 @@ class Polling extends Transport { function onData(data) { let contentLength; - chunks += data; - contentLength = Buffer.byteLength(chunks); + if (isBinary) { + chunks = Buffer.concat([chunks, data]); + contentLength = chunks.length; + } else { + chunks += data; + contentLength = Buffer.byteLength(chunks); + } if (contentLength > self.maxHttpBufferSize) { - chunks = ""; + chunks = isBinary ? Buffer.concat([]) : ""; req.connection.destroy(); } } @@ -154,7 +164,7 @@ class Polling extends Transport { } req.on("close", onClose); - req.setEncoding("utf8"); + if (!isBinary) req.setEncoding("utf8"); req.on("data", onData); req.on("end", onEnd); } @@ -178,7 +188,11 @@ class Polling extends Transport { self.onPacket(packet); }; - parser.decodePayload(data).forEach(callback); + if (this.protocol === 3) { + this.parser.decodePayload(data, callback); + } else { + this.parser.decodePayload(data).forEach(callback); + } } /** @@ -210,13 +224,18 @@ class Polling extends Transport { this.shouldClose = null; } - const self = this; - parser.encodePayload(packets, data => { - const compress = packets.some(function(packet) { + const doWrite = data => { + const compress = packets.some(packet => { return packet.options && packet.options.compress; }); - self.write(data, { compress: compress }); - }); + this.write(data, { compress }); + }; + + if (this.protocol === 3) { + this.parser.encodePayload(packets, this.supportsBinary, doWrite); + } else { + this.parser.encodePayload(packets, doWrite); + } } /** diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 7f0f0cb0d..6cd86c546 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -1,5 +1,4 @@ const Transport = require("../transport"); -const parser = require("engine.io-parser"); const debug = require("debug")("engine:ws"); class WebSocket extends Transport { @@ -71,7 +70,7 @@ class WebSocket extends Transport { for (var i = 0; i < packets.length; i++) { var packet = packets[i]; - parser.encodePacket(packet, self.supportsBinary, send); + this.parser.encodePacket(packet, self.supportsBinary, send); } function send(data) { diff --git a/package-lock.json b/package-lock.json index fcb60db66..611b9f1b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -150,6 +150,12 @@ } } }, + "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", @@ -198,6 +204,12 @@ "sprintf-js": "~1.0.2" } }, + "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 + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -288,6 +300,12 @@ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, + "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 + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -405,6 +423,12 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, + "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 + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -530,6 +554,55 @@ } } }, + "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==", + "dev": true, + "requires": { + "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.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "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, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "engine.io-parser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.0.tgz", @@ -833,6 +906,23 @@ "ansi-regex": "^2.0.0" } }, + "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, + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, "has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", @@ -872,6 +962,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", diff --git a/package.json b/package.json index 4113505b7..c5a662256 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "babel-eslint": "^8.0.2", "eiows": "^3.3.0", "engine.io-client": "4.0.6", + "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", @@ -47,7 +48,7 @@ }, "scripts": { "lint": "eslint lib/ test/ *.js", - "test": "npm run lint && npm run format:check && mocha && EIO_WS_ENGINE=eiows mocha", + "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'" }, diff --git a/test/common.js b/test/common.js index e55222934..b8d0e91c3 100644 --- a/test/common.js +++ b/test/common.js @@ -1,4 +1,8 @@ const eio = require(".."); +const eioc = + process.env.EIO_CLIENT === "3" + ? require("engine.io-client-v3") + : require("engine.io-client"); /** * Listen shortcut that fires a callback on an ephemeral port. @@ -10,6 +14,8 @@ exports.listen = (opts, fn) => { opts = {}; } + opts.allowEIO3 = true; + const e = eio.listen(0, opts, () => { fn(e.httpServer.address().port); }); @@ -17,6 +23,8 @@ exports.listen = (opts, fn) => { return e; }; +exports.eioc = eioc; + /** * Sprintf util. */ diff --git a/test/fixtures/server-close-upgraded.js b/test/fixtures/server-close-upgraded.js index fafb32b4e..46d8983e1 100644 --- a/test/fixtures/server-close-upgraded.js +++ b/test/fixtures/server-close-upgraded.js @@ -1,4 +1,4 @@ -const eioc = require("engine.io-client"); +const eioc = require("../common").eioc; const listen = require("../common").listen; const engine = listen(port => { diff --git a/test/fixtures/server-close-upgrading.js b/test/fixtures/server-close-upgrading.js index 4fe2917fd..7938658e1 100644 --- a/test/fixtures/server-close-upgrading.js +++ b/test/fixtures/server-close-upgrading.js @@ -1,4 +1,4 @@ -const eioc = require("engine.io-client"); +const eioc = require("../common").eioc; const listen = require("../common").listen; const engine = listen(port => { diff --git a/test/fixtures/server-close.js b/test/fixtures/server-close.js index 6d8b4c0c1..532ee92c3 100644 --- a/test/fixtures/server-close.js +++ b/test/fixtures/server-close.js @@ -1,4 +1,4 @@ -const eioc = require("engine.io-client"); +const eioc = require("../common").eioc; const listen = require("../common").listen; const engine = listen(port => { diff --git a/test/jsonp.js b/test/jsonp.js index ecbb8630e..d1edf4284 100644 --- a/test/jsonp.js +++ b/test/jsonp.js @@ -1,4 +1,4 @@ -const eioc = require("engine.io-client"); +const eioc = require("./common").eioc; const listen = require("./common").listen; const expect = require("expect.js"); const request = require("superagent"); diff --git a/test/server.js b/test/server.js index a01c0564f..e58d09e83 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 eio = require(".."); -const eioc = require("engine.io-client"); +const eioc = require("./common").eioc; const listen = require("./common").listen; const expect = require("expect.js"); const request = require("superagent"); @@ -487,6 +487,26 @@ describe("server", () => { ); }); + it("should disallow unsupported protocol versions", done => { + const httpServer = http.createServer(); + const engine = eio({ allowEIO3: false }); + engine.attach(httpServer); + httpServer.listen(() => { + const port = httpServer.address().port; + request + .get("http://localhost:%d/engine.io/".s(port)) + .query({ transport: "polling", EIO: 3 }) + .end((err, res) => { + expect(err).to.be.an(Error); + 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(); + }); + }); + }); + it("should send a packet along with the handshake", done => { listen({ initialPacket: "faster!" }, port => { const socket = new eioc.Socket("ws://localhost:%d".s(port)); @@ -997,43 +1017,83 @@ describe("server", () => { } ); - it( - "should trigger with connection `ping timeout` " + - "after `pingInterval + pingTimeout`", - done => { - const opts = { - allowUpgrades: false, - pingInterval: 300, - pingTimeout: 100 - }; - const engine = listen(opts, port => { - const socket = new eioc.Socket("ws://localhost:%d".s(port)); - let clientCloseReason = null; + if (process.env.EIO_CLIENT === "3") { + it( + "should trigger with connection `ping timeout` " + + "after `pingInterval + pingTimeout`", + done => { + const opts = { + allowUpgrades: false, + pingInterval: 300, + pingTimeout: 100 + }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let clientCloseReason = null; - socket.on("open", () => { - socket.on("close", reason => { - clientCloseReason = reason; + socket.on("open", () => { + socket.on("close", reason => { + clientCloseReason = reason; + }); + }); + + engine.on("connection", conn => { + conn.once("heartbeat", () => { + setTimeout(() => { + socket.onPacket = () => {}; + expect(clientCloseReason).to.be(null); + }, 150); + setTimeout(() => { + expect(clientCloseReason).to.be(null); + }, 350); + setTimeout(() => { + expect(clientCloseReason).to.be("ping timeout"); + done(); + }, 500); + }); }); }); + } + ); + } else { + it( + "should trigger with connection `ping timeout` " + + "after `pingInterval + pingTimeout`", + done => { + const opts = { + allowUpgrades: false, + pingInterval: 300, + pingTimeout: 100 + }; + const engine = listen(opts, port => { + const socket = new eioc.Socket("ws://localhost:%d".s(port)); + let clientCloseReason = null; - engine.on("connection", conn => { - conn.once("heartbeat", () => { - socket.onPacket = () => {}; - setTimeout(() => { - expect(clientCloseReason).to.be(null); - }, 150); - setTimeout(() => { - expect(clientCloseReason).to.be(null); - }, 350); - setTimeout(() => { - expect(clientCloseReason).to.be("ping timeout"); - done(); - }, 500); + socket.on("open", () => { + socket.on("close", reason => { + clientCloseReason = reason; + }); + }); + + engine.on("connection", conn => { + conn.once("heartbeat", () => { + socket.onPacket = () => {}; + setTimeout(() => { + expect(clientCloseReason).to.be(null); + }, 150); + setTimeout(() => { + expect(clientCloseReason).to.be(null); + }, 350); + setTimeout(() => { + expect(clientCloseReason).to.be("ping timeout"); + done(); + }, 500); + }); }); }); - }); - } - ); + } + ); + } it( "should abort the polling data request if it is " + "in progress", @@ -1796,7 +1856,11 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ transports: ["polling"], allowUpgrades: false }); + const engine = eio({ + transports: ["polling"], + allowUpgrades: false, + allowEIO3: true + }); engine.attach(srv); srv.listen(() => { const port = srv.address().port; @@ -1834,7 +1898,11 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ transports: ["polling"], allowUpgrades: false }); + const engine = eio({ + transports: ["polling"], + allowUpgrades: false, + allowEIO3: true + }); engine.attach(srv); srv.listen(() => { const port = srv.address().port; @@ -1874,7 +1942,11 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ transports: ["websocket"], allowUpgrades: false }); + const engine = eio({ + transports: ["websocket"], + allowUpgrades: false, + allowEIO3: true + }); engine.attach(srv); srv.listen(() => { const port = srv.address().port; @@ -1913,7 +1985,11 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ transports: ["polling"], allowUpgrades: false }); + const engine = eio({ + transports: ["polling"], + allowUpgrades: false, + allowEIO3: true + }); engine.attach(srv); srv.listen(() => { const port = srv.address().port; @@ -1952,7 +2028,11 @@ describe("server", () => { res.end("hello world\n"); }); - const engine = eio({ transports: ["websocket"], allowUpgrades: false }); + const engine = eio({ + transports: ["websocket"], + allowUpgrades: false, + allowEIO3: true + }); engine.attach(srv); srv.listen(() => { const port = srv.address().port; @@ -2409,7 +2489,11 @@ describe("server", () => { engine.on("connection", conn => { conn.on("packet", packet => { conn.close(); - expect(packet.type).to.be("pong"); + if (process.env.EIO_CLIENT === "3") { + expect(packet.type).to.be("ping"); + } else { + expect(packet.type).to.be("pong"); + } done(); }); }); @@ -2438,7 +2522,11 @@ describe("server", () => { engine.on("connection", conn => { conn.on("packetCreate", packet => { conn.close(); - expect(packet.type).to.be("ping"); + if (process.env.EIO_CLIENT === "3") { + expect(packet.type).to.be("pong"); + } else { + expect(packet.type).to.be("ping"); + } done(); }); }); From e5b307c16d8e7594fcec4eb23508f23f78546dc6 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 14 Jan 2021 01:47:14 +0100 Subject: [PATCH 063/124] chore(release): 4.1.0 Diff: https://github.com/socketio/engine.io/compare/4.0.6...4.1.0 --- 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 0c4aa00d5..9f90a0993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# [4.1.0](https://github.com/socketio/engine.io/compare/4.0.6...4.1.0) (2021-01-14) + + +### Features + +* add support for v3.x clients ([663d326](https://github.com/socketio/engine.io/commit/663d326d18de598318bd2120b2b70cd51adf8955)) + + ## [4.0.6](https://github.com/socketio/engine.io/compare/4.0.5...4.0.6) (2021-01-04) diff --git a/package-lock.json b/package-lock.json index 611b9f1b0..a1c9e6ff7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.6", + "version": "4.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -526,9 +526,9 @@ "dev": true }, "engine.io-client": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.6.tgz", - "integrity": "sha512-5lPh8rrhxIruo5ZlgFt31KM626o5OCXrCHBweieWWuVicDtnYdz/iR93k6N9k0Xs61WrYxZKIWXzeSaJF6fpNA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.1.0.tgz", + "integrity": "sha512-OUmn4m71/lW3ixICv4h3DuBRuh3ri0w3cDuepjsrINSbbqbni4Xw1shTFiKhl0v58lEtNpwJTpSKJJ3fondu5Q==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", diff --git a/package.json b/package.json index c5a662256..39ce5b244 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.0.6", + "version": "4.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": "4.0.6", + "engine.io-client": "4.1.0", "engine.io-client-v3": "npm:engine.io-client@3.5.0", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", From ff2b8aba48ebcb0de5626d3b76fddc94c398395f Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 2 Feb 2021 10:27:30 +0100 Subject: [PATCH 064/124] fix: do not reset the ping timer after upgrade There was two issues with this behavior: - v3 clients (with allowEIO3: true) were also receiving a "ping" after a successful upgrade, which is incorrect (in v3, it's the client that sends the "ping", and the server answers with a "pong") - the ping timer is not reset after upgrade on the client-side, so an upgrade which took longer than the `pingTimeout` duration could lead to a "ping timeout" error on the client-side I think the latter issue is present since the initial implementation. Related: https://github.com/socketio/socket.io-client-swift/pull/1309#issuecomment-768475704 --- lib/socket.js | 1 - test/server.js | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/socket.js b/lib/socket.js index debf1a6a4..4920a26ef 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -237,7 +237,6 @@ class Socket extends EventEmitter { self.clearTransport(); self.setTransport(transport); self.emit("upgrade", transport); - self.schedulePing(); self.flush(); if (self.readyState === "closing") { transport.close(function() { diff --git a/test/server.js b/test/server.js index e58d09e83..9d846123f 100644 --- a/test/server.js +++ b/test/server.js @@ -1282,6 +1282,24 @@ 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)); + socket.on("open", () => { + setTimeout(() => { + socket.removeListener("close"); + engine.close(); + socket.close(); + done(); + }, 500); + }); + socket.on("close", () => { + done(new Error("should not happen")); + }); + }); + }); + 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 => { From ae840fa8ed4601ef1593f8adbb3a5e9e15b4e546 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 2 Feb 2021 10:49:24 +0100 Subject: [PATCH 065/124] chore: point towards the master branch for the CI badge Reference: https://docs.github.com/en/actions/managing-workflow-runs/adding-a-workflow-status-badge#using-the-branch-parameter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90cc933e5..8f68afe6a 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)](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 9534355b89737808e16065f6076c7aef2c5b5de2 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 2 Feb 2021 10:51:04 +0100 Subject: [PATCH 066/124] chore(release): 4.1.1 Diff: https://github.com/socketio/engine.io/compare/4.1.0...4.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 9f90a0993..a1c249e65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [4.1.1](https://github.com/socketio/engine.io/compare/4.1.0...4.1.1) (2021-02-02) + + +### Bug Fixes + +* do not reset the ping timer after upgrade ([ff2b8ab](https://github.com/socketio/engine.io/commit/ff2b8aba48ebcb0de5626d3b76fddc94c398395f)), 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) + + # [4.1.0](https://github.com/socketio/engine.io/compare/4.0.6...4.1.0) (2021-01-14) diff --git a/package-lock.json b/package-lock.json index a1c9e6ff7..c335cecbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.1.0", + "version": "4.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -526,9 +526,9 @@ "dev": true }, "engine.io-client": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.1.0.tgz", - "integrity": "sha512-OUmn4m71/lW3ixICv4h3DuBRuh3ri0w3cDuepjsrINSbbqbni4Xw1shTFiKhl0v58lEtNpwJTpSKJJ3fondu5Q==", + "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==", "dev": true, "requires": { "base64-arraybuffer": "0.1.4", diff --git a/package.json b/package.json index 39ce5b244..9ab7e89e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine.io", - "version": "4.1.0", + "version": "4.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": "4.1.0", + "engine.io-client": "4.1.1", "engine.io-client-v3": "npm:engine.io-client@3.5.0", "eslint": "^4.19.1", "eslint-config-prettier": "^6.9.0", From 5a7fa132c442bc1e7eefa1cf38168ee951575ded Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Tue, 2 Mar 2021 08:49:58 +0100 Subject: [PATCH 067/124] 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 068/124] 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 069/124] 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 070/124] 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 071/124] 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 072/124] 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 073/124] 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 074/124] 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 075/124] 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 076/124] 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 077/124] 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 078/124] 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 079/124] 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 080/124] 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 081/124] 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 082/124] 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 083/124] 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 084/124] 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 085/124] 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 086/124] 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 087/124] 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 088/124] 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 089/124] 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 090/124] 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 091/124] 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 092/124] 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 093/124] 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 094/124] 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 095/124] 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 096/124] 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 097/124] 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 098/124] 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 099/124] 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 100/124] 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 101/124] 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 102/124] 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 103/124] 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 104/124] 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 105/124] 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 106/124] 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 107/124] 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 108/124] 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 109/124] 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 110/124] 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 111/124] 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 112/124] 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 113/124] 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 114/124] 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 115/124] 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 116/124] 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 117/124] 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 118/124] 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 119/124] 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 120/124] 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 121/124] 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 122/124] 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 123/124] 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 124/124] 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",