From a9f370e474657c046d7cfd6313235cc6ec187a58 Mon Sep 17 00:00:00 2001 From: rprieto Date: Thu, 8 May 2014 22:47:51 +1000 Subject: [PATCH] Splitting the handlers (preparation for creating a separate "actual" handler) --- src/index.js | 64 +++------------------------------------------ src/origin.js | 11 ++++++++ src/preflight.js | 46 ++++++++++++++++++++++++++++++++ test/origin.spec.js | 31 ++++++++++++++++++++++ 4 files changed, 92 insertions(+), 60 deletions(-) create mode 100644 src/origin.js create mode 100644 src/preflight.js create mode 100644 test/origin.spec.js diff --git a/src/index.js b/src/index.js index 2a5f5de..e9154c7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,62 +1,6 @@ -var restify = require('restify'); -var util = require('util'); - -DEFAULT_ALLOW_HEADERS = restify.CORS.ALLOW_HEADERS; - -var HTTP_NO_CONTENT = 204; - -function matchOrigin(req, origins) { - var origin = req.headers["origin"]; - function belongs(o) { - if (origin === o || o === "*") { - origin = o; - return true; - } - return false; - } - if (origin && origins.some(belongs)) { - return origin; - } else { - return false; - } -} - -function preflightHandler(options) { - return function(req, res, next) { - if (req.method !== 'OPTIONS') return next(); - - // 6.2.1 and 6.2.2 - if (matchOrigin(req, options.origins) === false) return next(); - - // 6.2.3 - requestedMethod = req.headers['access-control-request-method']; - if (!requestedMethod) return next(); - - // 6.2.4 - requestedHeaders = req.headers['access-control-request-headers']; - requestedHeaders = requestedHeaders ? requestedHeaders.split(', ') : []; - - allowedMethods = [requestedMethod, 'OPTIONS']; - allowedHeaders = DEFAULT_ALLOW_HEADERS.concat(['x-requested-with']) - .concat(options.allowHeaders); - - res.once('header', function() { - - // 6.2.7 - res.header('Access-Control-Allow-Origin', req.headers['origin']); - res.header('Access-Control-Allow-Credentials', true); - - // 6.2.9 - res.header('Access-Control-Allow-Methods', allowedMethods.join(', ')); - - // 6.2.10 - res.header('Access-Control-Allow-Headers', allowedHeaders.join(', ')); - - }); - - res.send(HTTP_NO_CONTENT); - }; -} +var util = require('util'); +var restify = require('restify'); +var preflight = require('./preflight'); module.exports = function(options) { @@ -71,7 +15,7 @@ module.exports = function(options) { headers: options.exposeHeaders }), - preflight: preflightHandler({ + preflight: preflight.handler({ origins: options.origins, allowHeaders: options.allowHeaders }) diff --git a/src/origin.js b/src/origin.js new file mode 100644 index 0000000..6194aa5 --- /dev/null +++ b/src/origin.js @@ -0,0 +1,11 @@ + +exports.match = function(origin, list) { + function belongs(o) { + return (origin === o || o === "*"); + } + if (origin && list.some(belongs)) { + return origin; + } else { + return false; + } +}; diff --git a/src/preflight.js b/src/preflight.js new file mode 100644 index 0000000..765a100 --- /dev/null +++ b/src/preflight.js @@ -0,0 +1,46 @@ +var restify = require('restify'); +var origin = require('./origin'); + +var DEFAULT_ALLOW_HEADERS = restify.CORS.ALLOW_HEADERS; +var HTTP_NO_CONTENT = 204; + +exports.handler = function(options) { + + return function(req, res, next) { + if (req.method !== 'OPTIONS') return next(); + + // 6.2.1 and 6.2.2 + originHeader = req.headers['origin']; + if (origin.match(originHeader, options.origins) === false) return next(); + + // 6.2.3 + requestedMethod = req.headers['access-control-request-method']; + if (!requestedMethod) return next(); + + // 6.2.4 + requestedHeaders = req.headers['access-control-request-headers']; + requestedHeaders = requestedHeaders ? requestedHeaders.split(', ') : []; + + allowedMethods = [requestedMethod, 'OPTIONS']; + allowedHeaders = DEFAULT_ALLOW_HEADERS.concat(['x-requested-with']) + .concat(options.allowHeaders); + + res.once('header', function() { + + // 6.2.7 + res.header('Access-Control-Allow-Origin', originHeader); + res.header('Access-Control-Allow-Credentials', true); + + // 6.2.9 + res.header('Access-Control-Allow-Methods', allowedMethods.join(', ')); + + // 6.2.10 + res.header('Access-Control-Allow-Headers', allowedHeaders.join(', ')); + + }); + + res.send(HTTP_NO_CONTENT); + }; + +}; + diff --git a/test/origin.spec.js b/test/origin.spec.js new file mode 100644 index 0000000..873fce3 --- /dev/null +++ b/test/origin.spec.js @@ -0,0 +1,31 @@ +var should = require('should'); +var origin = require('../src/origin'); + +describe('Origin list', function() { + + var list = [ + 'http://api.myapp.com', + 'http://www.myapp.com' + ]; + + it('returns null if the origin is not in the list', function() { + var o = origin.match('http://random-website.com', list); + o.should.eql(false); + }); + + it('does not do partial matches', function() { + var o = origin.match('api.myapp.com', list); + o.should.eql(false); + }); + + it('returns the origin if it matched', function() { + var o = origin.match('http://api.myapp.com', list); + o.should.eql('http://api.myapp.com'); + }); + + it('returns the origin if the list contains *', function() { + var o = origin.match('http://random-website.com', ['*']); + o.should.eql('http://random-website.com'); + }); + +});