From 908a2fb9d93c83a0bf4e9d4c434a1d21c7ed8446 Mon Sep 17 00:00:00 2001 From: Emilien Kenler Date: Sat, 11 Mar 2017 16:12:52 +0900 Subject: [PATCH 1/2] [feat] Add an option to toggle handling of OPTIONS requests (#484) By setting `handlePreflightRequest` to false, OPTIONS request are no longer processed by engine.io. A function can also be provided. --- README.md | 1 + lib/server.js | 9 +++++++- test/server.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bdb44770..f07e6d42a 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,7 @@ 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 99dfb4150..91d2b116e 100644 --- a/lib/server.js +++ b/lib/server.js @@ -446,6 +446,9 @@ Server.prototype.attach = function (server, options) { path += '/'; function check (req) { + if ('OPTIONS' === req.method && false === options.handlePreflightRequest) { + return false; + } return path === req.url.substr(0, path.length); } @@ -459,7 +462,11 @@ Server.prototype.attach = function (server, options) { server.on('request', function (req, res) { if (check(req)) { debug('intercepting request for path "%s"', path); - self.handleRequest(req, res); + 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); diff --git a/test/server.js b/test/server.js index 8e2d8943c..b57c01132 100644 --- a/test/server.js +++ b/test/server.js @@ -2542,6 +2542,67 @@ 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 (res) { + 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 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 (res) { + expect(res.status).to.be(501); + expect(res.body.code).to.be(undefined); + 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'] = '*'; + } + 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}, function (port) { + request.options('http://localhost:%d/engine.io/default/'.s(port)) + .set('Origin', 'http://engine.io') + .query({ transport: 'polling' }) + .end(function (res) { + 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(); + }); + }); + }); + }); + if (!UWS_ENGINE && parseInt(process.versions.node, 10) >= 4) { describe('wsEngine option', function () { it('should allow loading of other websocket server implementation like uws', function (done) { From 10e9b5d0a6956bfdf82e2204bb1b5dd120962425 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Sat, 11 Mar 2017 08:48:55 +0100 Subject: [PATCH 2/2] minor fix --- test/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/server.js b/test/server.js index b57c01132..745cecebd 100644 --- a/test/server.js +++ b/test/server.js @@ -2586,7 +2586,7 @@ describe('server', function () { res.writeHead(200, headers); res.end(); }; - listen({handlePreflightRequest}, function (port) { + listen({handlePreflightRequest: handlePreflightRequest}, function (port) { request.options('http://localhost:%d/engine.io/default/'.s(port)) .set('Origin', 'http://engine.io') .query({ transport: 'polling' })