arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/www/node_modules/corser/lib/corser.js
www/node_modules/corser/lib/corser.jsRaw
  1/**
  2 * Specification: http://www.w3.org/TR/2012/WD-cors-20120403/
  3 * W3C Working Draft 3 April 2012
  4 */
  5"use strict";
  6
  7/*jshint node:true */
  8
  9var simpleMethods, simpleRequestHeaders, simpleResponseHeaders, toLowerCase, checkOriginMatch;
 10
 11// A method is said to be a simple method if it is a case-sensitive match for one of the following:
 12Object.defineProperty(exports, "simpleMethods", {
 13    get: function () {
 14        return [
 15            "GET",
 16            "HEAD",
 17            "POST"
 18        ];
 19    }
 20});
 21simpleMethods = exports.simpleMethods;
 22
 23// A header is said to be a simple header if the header field name is an ASCII case-insensitive match for one of
 24// the following:
 25Object.defineProperty(exports, "simpleRequestHeaders", {
 26    get: function () {
 27        return [
 28            "accept",
 29            "accept-language",
 30            "content-language",
 31            "content-type"
 32        ];
 33    }
 34});
 35simpleRequestHeaders = exports.simpleRequestHeaders;
 36
 37// A header is said to be a simple response header if the header field name is an ASCII case-insensitive
 38// match for one of the following:
 39Object.defineProperty(exports, "simpleResponseHeaders", {
 40    get: function () {
 41        return [
 42            "cache-control",
 43            "content-language",
 44            "content-type",
 45            "expires",
 46            "last-modified",
 47            "pragma"
 48        ];
 49    }
 50});
 51simpleResponseHeaders = exports.simpleResponseHeaders;
 52
 53toLowerCase = function (array) {
 54    return array.map(function (el) {
 55        return el.toLowerCase();
 56    });
 57};
 58
 59checkOriginMatch = function (originHeader, origins, callback) {
 60    if (typeof origins === "function") {
 61        origins(originHeader, function (err, allow) {
 62            callback(err, allow);
 63        });
 64    } else if (origins.length > 0) {
 65        callback(null, origins.some(function (origin) {
 66            return origin === originHeader;
 67        }));
 68    } else {
 69        // Always matching is acceptable since the list of origins can be unbounded.
 70        callback(null, true);
 71    }
 72};
 73
 74exports.create = function (options) {
 75    options = options || {};
 76    options.origins = options.origins || [];
 77    options.methods = options.methods || simpleMethods;
 78    if (options.hasOwnProperty("requestHeaders") === true) {
 79        options.requestHeaders = toLowerCase(options.requestHeaders);
 80    } else {
 81        options.requestHeaders = simpleRequestHeaders;
 82    }
 83    if (options.hasOwnProperty("responseHeaders") === true) {
 84        options.responseHeaders = toLowerCase(options.responseHeaders);
 85    } else {
 86        options.responseHeaders = simpleResponseHeaders;
 87    }
 88    options.maxAge = options.maxAge || null;
 89    options.supportsCredentials = options.supportsCredentials || false;
 90    if (options.hasOwnProperty("endPreflightRequests") === false) {
 91        options.endPreflightRequests = true;
 92    }
 93    return function (req, res, next) {
 94        var methodMatches, headersMatch, requestMethod, requestHeaders, exposedHeaders, endPreflight;
 95        // If the Origin header is not present terminate this set of steps.
 96        if (!req.headers.hasOwnProperty("origin")) {
 97            // The request is outside the scope of the CORS specification. If there is no Origin header,
 98            // it could be a same-origin request. Let's let the user-agent handle this situation.
 99            next();
100        } else {
101            // If the value of the Origin header is not a case-sensitive match for any of the values in
102            // list of origins, do not set any additional headers and terminate this set of steps.
103            checkOriginMatch(req.headers.origin, options.origins, function (err, originMatches) {
104                if (err !== null) {
105                    next(err);
106                } else {
107                    if (typeof originMatches !== "boolean" || originMatches === false) {
108                        next();
109                    } else {
110                        // Respond to preflight request.
111                        if (req.method === "OPTIONS") {
112                            endPreflight = function () {
113                                if (options.endPreflightRequests === true) {
114                                    res.writeHead(204);
115                                    res.end();
116                                } else {
117                                    next();
118                                }
119                            };
120                            // If there is no Access-Control-Request-Method header or if parsing failed, do not set
121                            // any additional headers and terminate this set of steps.
122                            if (!req.headers.hasOwnProperty("access-control-request-method")) {
123                                endPreflight();
124                            } else {
125                                requestMethod = req.headers["access-control-request-method"];
126                                // If there are no Access-Control-Request-Headers headers let header field-names be the
127                                // empty list. If parsing failed do not set any additional headers and terminate this set
128                                // of steps.
129                                // Checking for an empty header is a workaround for a bug Chrome 52:
130                                // https://bugs.chromium.org/p/chromium/issues/detail?id=633729
131                                if (req.headers.hasOwnProperty("access-control-request-headers") && req.headers["access-control-request-headers"] !== "") {
132                                    requestHeaders = toLowerCase(req.headers["access-control-request-headers"].split(/,\s*/));
133                                } else {
134                                    requestHeaders = [];
135                                }
136                                // If method is not a case-sensitive match for any of the values in list of methods do not
137                                // set any additional headers and terminate this set of steps.
138                                methodMatches = options.methods.indexOf(requestMethod) !== -1;
139                                if (methodMatches === false) {
140                                    endPreflight();
141                                } else {
142                                    // If any of the header field-names is not a ASCII case-insensitive match for any of
143                                    // the values in list of headers do not set any additional headers and terminate this
144                                    // set of steps.
145                                    headersMatch = requestHeaders.every(function (requestHeader) {
146                                        // Browsers automatically add Origin to Access-Control-Request-Headers. However,
147                                        // Origin is not one of the simple request headers. Therefore, the header is
148                                        // accepted even if it is not in the list of request headers because CORS would
149                                        // not work without it.
150                                        if (requestHeader === "origin") {
151                                            return true;
152                                        } else {
153                                            if (options.requestHeaders.indexOf(requestHeader) !== -1) {
154                                                return true;
155                                            } else {
156                                                return false;
157                                            }
158                                        }
159                                    });
160                                    if (headersMatch === false) {
161                                        endPreflight();
162                                    } else {
163                                        if (options.supportsCredentials === true) {
164                                            // If the resource supports credentials add a single Access-Control-Allow-Origin
165                                            // header, with the value of the Origin header as value, and add a single
166                                            // Access-Control-Allow-Credentials header with the literal string "true"
167                                            // as value.
168                                            res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
169                                            res.setHeader("Access-Control-Allow-Credentials", "true");
170                                        } else {
171                                            // Otherwise, add a single Access-Control-Allow-Origin header, with either the
172                                            // value of the Origin header or the string "*" as value.
173                                            if (options.origins.length > 0 || typeof options.origins === "function") {
174                                                res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
175                                            } else {
176                                                res.setHeader("Access-Control-Allow-Origin", "*");
177                                            }
178                                        }
179                                        // Optionally add a single Access-Control-Max-Age header with as value the amount
180                                        // of seconds the user agent is allowed to cache the result of the request.
181                                        if (options.maxAge !== null) {
182                                            res.setHeader("Access-Control-Max-Age", options.maxAge);
183                                        }
184                                        // Add one or more Access-Control-Allow-Methods headers consisting of (a subset
185                                        // of) the list of methods.
186                                        res.setHeader("Access-Control-Allow-Methods", options.methods.join(","));
187                                        // Add one or more Access-Control-Allow-Headers headers consisting of (a subset
188                                        // of) the list of headers.
189                                        res.setHeader("Access-Control-Allow-Headers", options.requestHeaders.join(","));
190                                        // And out.
191                                        endPreflight();
192                                    }
193                                }
194                            }
195                        } else {
196                            if (options.supportsCredentials === true) {
197                                // If the resource supports credentials add a single Access-Control-Allow-Origin header,
198                                // with the value of the Origin header as value, and add a single
199                                // Access-Control-Allow-Credentials header with the literal string "true" as value.
200                                res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
201                                res.setHeader("Access-Control-Allow-Credentials", "true");
202                            } else {
203                                // Otherwise, add a single Access-Control-Allow-Origin header, with either the value of
204                                // the Origin header or the literal string "*" as value.
205                                // If the list of origins is empty, use "*" as value.
206                                if (options.origins.length > 0 || typeof options.origins === "function") {
207                                    res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
208                                } else {
209                                    res.setHeader("Access-Control-Allow-Origin", "*");
210                                }
211                            }
212                            // If the list of exposed headers is not empty add one or more Access-Control-Expose-Headers
213                            // headers, with as values the header field names given in the list of exposed headers.
214                            exposedHeaders = options.responseHeaders.filter(function (optionsResponseHeader) {
215                                return simpleResponseHeaders.indexOf(optionsResponseHeader) === -1;
216                            });
217                            if (exposedHeaders.length > 0) {
218                                res.setHeader("Access-Control-Expose-Headers", exposedHeaders.join(","));
219                            }
220                            // And out.
221                            next();
222                        }
223                    }
224                }
225            });
226        }
227    };
228};