Spaces:
Sleeping
Sleeping
| /** | |
| * Specification: http://www.w3.org/TR/2012/WD-cors-20120403/ | |
| * W3C Working Draft 3 April 2012 | |
| */ | |
| ; | |
| /*jshint node:true */ | |
| var simpleMethods, simpleRequestHeaders, simpleResponseHeaders, toLowerCase, checkOriginMatch; | |
| // A method is said to be a simple method if it is a case-sensitive match for one of the following: | |
| Object.defineProperty(exports, "simpleMethods", { | |
| get: function () { | |
| return [ | |
| "GET", | |
| "HEAD", | |
| "POST" | |
| ]; | |
| } | |
| }); | |
| simpleMethods = exports.simpleMethods; | |
| // A header is said to be a simple header if the header field name is an ASCII case-insensitive match for one of | |
| // the following: | |
| Object.defineProperty(exports, "simpleRequestHeaders", { | |
| get: function () { | |
| return [ | |
| "accept", | |
| "accept-language", | |
| "content-language", | |
| "content-type" | |
| ]; | |
| } | |
| }); | |
| simpleRequestHeaders = exports.simpleRequestHeaders; | |
| // A header is said to be a simple response header if the header field name is an ASCII case-insensitive | |
| // match for one of the following: | |
| Object.defineProperty(exports, "simpleResponseHeaders", { | |
| get: function () { | |
| return [ | |
| "cache-control", | |
| "content-language", | |
| "content-type", | |
| "expires", | |
| "last-modified", | |
| "pragma" | |
| ]; | |
| } | |
| }); | |
| simpleResponseHeaders = exports.simpleResponseHeaders; | |
| toLowerCase = function (array) { | |
| return array.map(function (el) { | |
| return el.toLowerCase(); | |
| }); | |
| }; | |
| checkOriginMatch = function (originHeader, origins, callback) { | |
| if (typeof origins === "function") { | |
| origins(originHeader, function (err, allow) { | |
| callback(err, allow); | |
| }); | |
| } else if (origins.length > 0) { | |
| callback(null, origins.some(function (origin) { | |
| return origin === originHeader; | |
| })); | |
| } else { | |
| // Always matching is acceptable since the list of origins can be unbounded. | |
| callback(null, true); | |
| } | |
| }; | |
| exports.create = function (options) { | |
| options = options || {}; | |
| options.origins = options.origins || []; | |
| options.methods = options.methods || simpleMethods; | |
| if (options.hasOwnProperty("requestHeaders") === true) { | |
| options.requestHeaders = toLowerCase(options.requestHeaders); | |
| } else { | |
| options.requestHeaders = simpleRequestHeaders; | |
| } | |
| if (options.hasOwnProperty("responseHeaders") === true) { | |
| options.responseHeaders = toLowerCase(options.responseHeaders); | |
| } else { | |
| options.responseHeaders = simpleResponseHeaders; | |
| } | |
| options.maxAge = options.maxAge || null; | |
| options.supportsCredentials = options.supportsCredentials || false; | |
| if (options.hasOwnProperty("endPreflightRequests") === false) { | |
| options.endPreflightRequests = true; | |
| } | |
| return function (req, res, next) { | |
| var methodMatches, headersMatch, requestMethod, requestHeaders, exposedHeaders, endPreflight; | |
| // If the Origin header is not present terminate this set of steps. | |
| if (!req.headers.hasOwnProperty("origin")) { | |
| // The request is outside the scope of the CORS specification. If there is no Origin header, | |
| // it could be a same-origin request. Let's let the user-agent handle this situation. | |
| next(); | |
| } else { | |
| // If the value of the Origin header is not a case-sensitive match for any of the values in | |
| // list of origins, do not set any additional headers and terminate this set of steps. | |
| checkOriginMatch(req.headers.origin, options.origins, function (err, originMatches) { | |
| if (err !== null) { | |
| next(err); | |
| } else { | |
| if (typeof originMatches !== "boolean" || originMatches === false) { | |
| next(); | |
| } else { | |
| // Respond to preflight request. | |
| if (req.method === "OPTIONS") { | |
| endPreflight = function () { | |
| if (options.endPreflightRequests === true) { | |
| res.writeHead(204); | |
| res.end(); | |
| } else { | |
| next(); | |
| } | |
| }; | |
| // If there is no Access-Control-Request-Method header or if parsing failed, do not set | |
| // any additional headers and terminate this set of steps. | |
| if (!req.headers.hasOwnProperty("access-control-request-method")) { | |
| endPreflight(); | |
| } else { | |
| requestMethod = req.headers["access-control-request-method"]; | |
| // If there are no Access-Control-Request-Headers headers let header field-names be the | |
| // empty list. If parsing failed do not set any additional headers and terminate this set | |
| // of steps. | |
| // Checking for an empty header is a workaround for a bug Chrome 52: | |
| // https://bugs.chromium.org/p/chromium/issues/detail?id=633729 | |
| if (req.headers.hasOwnProperty("access-control-request-headers") && req.headers["access-control-request-headers"] !== "") { | |
| requestHeaders = toLowerCase(req.headers["access-control-request-headers"].split(/,\s*/)); | |
| } else { | |
| requestHeaders = []; | |
| } | |
| // If method is not a case-sensitive match for any of the values in list of methods do not | |
| // set any additional headers and terminate this set of steps. | |
| methodMatches = options.methods.indexOf(requestMethod) !== -1; | |
| if (methodMatches === false) { | |
| endPreflight(); | |
| } else { | |
| // If any of the header field-names is not a ASCII case-insensitive match for any of | |
| // the values in list of headers do not set any additional headers and terminate this | |
| // set of steps. | |
| headersMatch = requestHeaders.every(function (requestHeader) { | |
| // Browsers automatically add Origin to Access-Control-Request-Headers. However, | |
| // Origin is not one of the simple request headers. Therefore, the header is | |
| // accepted even if it is not in the list of request headers because CORS would | |
| // not work without it. | |
| if (requestHeader === "origin") { | |
| return true; | |
| } else { | |
| if (options.requestHeaders.indexOf(requestHeader) !== -1) { | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| }); | |
| if (headersMatch === false) { | |
| endPreflight(); | |
| } else { | |
| if (options.supportsCredentials === true) { | |
| // If the resource supports credentials add a single Access-Control-Allow-Origin | |
| // header, with the value of the Origin header as value, and add a single | |
| // Access-Control-Allow-Credentials header with the literal string "true" | |
| // as value. | |
| res.setHeader("Access-Control-Allow-Origin", req.headers.origin); | |
| res.setHeader("Access-Control-Allow-Credentials", "true"); | |
| } else { | |
| // Otherwise, add a single Access-Control-Allow-Origin header, with either the | |
| // value of the Origin header or the string "*" as value. | |
| if (options.origins.length > 0 || typeof options.origins === "function") { | |
| res.setHeader("Access-Control-Allow-Origin", req.headers.origin); | |
| } else { | |
| res.setHeader("Access-Control-Allow-Origin", "*"); | |
| } | |
| } | |
| // Optionally add a single Access-Control-Max-Age header with as value the amount | |
| // of seconds the user agent is allowed to cache the result of the request. | |
| if (options.maxAge !== null) { | |
| res.setHeader("Access-Control-Max-Age", options.maxAge); | |
| } | |
| // Add one or more Access-Control-Allow-Methods headers consisting of (a subset | |
| // of) the list of methods. | |
| res.setHeader("Access-Control-Allow-Methods", options.methods.join(",")); | |
| // Add one or more Access-Control-Allow-Headers headers consisting of (a subset | |
| // of) the list of headers. | |
| res.setHeader("Access-Control-Allow-Headers", options.requestHeaders.join(",")); | |
| // And out. | |
| endPreflight(); | |
| } | |
| } | |
| } | |
| } else { | |
| if (options.supportsCredentials === true) { | |
| // If the resource supports credentials add a single Access-Control-Allow-Origin header, | |
| // with the value of the Origin header as value, and add a single | |
| // Access-Control-Allow-Credentials header with the literal string "true" as value. | |
| res.setHeader("Access-Control-Allow-Origin", req.headers.origin); | |
| res.setHeader("Access-Control-Allow-Credentials", "true"); | |
| } else { | |
| // Otherwise, add a single Access-Control-Allow-Origin header, with either the value of | |
| // the Origin header or the literal string "*" as value. | |
| // If the list of origins is empty, use "*" as value. | |
| if (options.origins.length > 0 || typeof options.origins === "function") { | |
| res.setHeader("Access-Control-Allow-Origin", req.headers.origin); | |
| } else { | |
| res.setHeader("Access-Control-Allow-Origin", "*"); | |
| } | |
| } | |
| // If the list of exposed headers is not empty add one or more Access-Control-Expose-Headers | |
| // headers, with as values the header field names given in the list of exposed headers. | |
| exposedHeaders = options.responseHeaders.filter(function (optionsResponseHeader) { | |
| return simpleResponseHeaders.indexOf(optionsResponseHeader) === -1; | |
| }); | |
| if (exposedHeaders.length > 0) { | |
| res.setHeader("Access-Control-Expose-Headers", exposedHeaders.join(",")); | |
| } | |
| // And out. | |
| next(); | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| }; | |
| }; | |