| (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; |
| |
| }()); |