You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					239 lines
				
				6.5 KiB
			
		
		
			
		
	
	
					239 lines
				
				6.5 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								(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;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}());
							 |