123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- (function () {
-
- 'use strict';
-
- var assign = require('object-assign');
- var vary = require('vary');
-
- var defaults = {
- origin: '*',
- methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
- preflightContinue: false,
- optionsSuccessStatus: 204
- };
-
- function isString(s) {
- return typeof s === 'string' || s instanceof String;
- }
-
- function isOriginAllowed(origin, allowedOrigin) {
- if (Array.isArray(allowedOrigin)) {
- for (var i = 0; i < allowedOrigin.length; ++i) {
- if (isOriginAllowed(origin, allowedOrigin[i])) {
- return true;
- }
- }
- return false;
- } else if (isString(allowedOrigin)) {
- return origin === allowedOrigin;
- } else if (allowedOrigin instanceof RegExp) {
- return allowedOrigin.test(origin);
- } else {
- return !!allowedOrigin;
- }
- }
-
- function configureOrigin(options, req) {
- var requestOrigin = req.headers.origin,
- headers = [],
- isAllowed;
-
- if (!options.origin || options.origin === '*') {
- // allow any origin
- headers.push([{
- key: 'Access-Control-Allow-Origin',
- value: '*'
- }]);
- } else if (isString(options.origin)) {
- // fixed origin
- headers.push([{
- key: 'Access-Control-Allow-Origin',
- value: options.origin
- }]);
- headers.push([{
- key: 'Vary',
- value: 'Origin'
- }]);
- } else {
- isAllowed = isOriginAllowed(requestOrigin, options.origin);
- // reflect origin
- headers.push([{
- key: 'Access-Control-Allow-Origin',
- value: isAllowed ? requestOrigin : false
- }]);
- headers.push([{
- key: 'Vary',
- value: 'Origin'
- }]);
- }
-
- return headers;
- }
-
- function configureMethods(options) {
- var methods = options.methods;
- if (methods.join) {
- methods = options.methods.join(','); // .methods is an array, so turn it into a string
- }
- return {
- key: 'Access-Control-Allow-Methods',
- value: methods
- };
- }
-
- function configureCredentials(options) {
- if (options.credentials === true) {
- return {
- key: 'Access-Control-Allow-Credentials',
- value: 'true'
- };
- }
- return null;
- }
-
- function configureAllowedHeaders(options, req) {
- var allowedHeaders = options.allowedHeaders || options.headers;
- var headers = [];
-
- if (!allowedHeaders) {
- allowedHeaders = req.headers['access-control-request-headers']; // .headers wasn't specified, so reflect the request headers
- headers.push([{
- key: 'Vary',
- value: 'Access-Control-Request-Headers'
- }]);
- } else if (allowedHeaders.join) {
- allowedHeaders = allowedHeaders.join(','); // .headers is an array, so turn it into a string
- }
- if (allowedHeaders && allowedHeaders.length) {
- headers.push([{
- key: 'Access-Control-Allow-Headers',
- value: allowedHeaders
- }]);
- }
-
- return headers;
- }
-
- function configureExposedHeaders(options) {
- var headers = options.exposedHeaders;
- if (!headers) {
- return null;
- } else if (headers.join) {
- headers = headers.join(','); // .headers is an array, so turn it into a string
- }
- if (headers && headers.length) {
- return {
- key: 'Access-Control-Expose-Headers',
- value: headers
- };
- }
- return null;
- }
-
- function configureMaxAge(options) {
- var maxAge = (typeof options.maxAge === 'number' || options.maxAge) && options.maxAge.toString()
- if (maxAge && maxAge.length) {
- return {
- key: 'Access-Control-Max-Age',
- value: maxAge
- };
- }
- return null;
- }
-
- function applyHeaders(headers, res) {
- for (var i = 0, n = headers.length; i < n; i++) {
- var header = headers[i];
- if (header) {
- if (Array.isArray(header)) {
- applyHeaders(header, res);
- } else if (header.key === 'Vary' && header.value) {
- vary(res, header.value);
- } else if (header.value) {
- res.setHeader(header.key, header.value);
- }
- }
- }
- }
-
- function cors(options, req, res, next) {
- var headers = [],
- method = req.method && req.method.toUpperCase && req.method.toUpperCase();
-
- if (method === 'OPTIONS') {
- // preflight
- headers.push(configureOrigin(options, req));
- headers.push(configureCredentials(options, req));
- headers.push(configureMethods(options, req));
- headers.push(configureAllowedHeaders(options, req));
- headers.push(configureMaxAge(options, req));
- headers.push(configureExposedHeaders(options, req));
- applyHeaders(headers, res);
-
- if (options.preflightContinue) {
- next();
- } else {
- // Safari (and potentially other browsers) need content-length 0,
- // for 204 or they just hang waiting for a body
- res.statusCode = options.optionsSuccessStatus;
- res.setHeader('Content-Length', '0');
- res.end();
- }
- } else {
- // actual response
- headers.push(configureOrigin(options, req));
- headers.push(configureCredentials(options, req));
- headers.push(configureExposedHeaders(options, req));
- applyHeaders(headers, res);
- next();
- }
- }
-
- function middlewareWrapper(o) {
- // if options are static (either via defaults or custom options passed in), wrap in a function
- var optionsCallback = null;
- if (typeof o === 'function') {
- optionsCallback = o;
- } else {
- optionsCallback = function (req, cb) {
- cb(null, o);
- };
- }
-
- return function corsMiddleware(req, res, next) {
- optionsCallback(req, function (err, options) {
- if (err) {
- next(err);
- } else {
- var corsOptions = assign({}, defaults, options);
- var originCallback = null;
- if (corsOptions.origin && typeof corsOptions.origin === 'function') {
- originCallback = corsOptions.origin;
- } else if (corsOptions.origin) {
- originCallback = function (origin, cb) {
- cb(null, corsOptions.origin);
- };
- }
-
- if (originCallback) {
- originCallback(req.headers.origin, function (err2, origin) {
- if (err2 || !origin) {
- next(err2);
- } else {
- corsOptions.origin = origin;
- cors(corsOptions, req, res, next);
- }
- });
- } else {
- next();
- }
- }
- });
- };
- }
-
- // can pass either an options hash, an options delegate, or nothing
- module.exports = middlewareWrapper;
-
- }());
|