diff --git a/.gitignore b/.gitignore index 8280239..578e603 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,23 @@ -*.seed -*.log -*.csv -*.dat -*.out -*.pid -*.gz - -pids -logs -results +# OS # +################### +.DS_Store +.idea +Thumbs.db +tmp/ +temp/ + +# Node.js # +################### node_modules +package-lock.json npm-debug.log -coverage/ +yarn-debug.log +yarn-error.log + + +# NYC # +################### +coverage +*.lcov +.nyc_output diff --git a/.travis.yml b/.travis.yml index 332e12f..33ef5fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ -sudo: false language: node_js node_js: - - '8' - - '10' -script: 'npm run ci' -after_script: - - 'npm i codecov && codecov' + - 10 + - 12 + - 14 +script: "npm run ci" +after_script: "npm i codecov && codecov" diff --git a/LICENSE b/LICENSE index a2efa34..24bea9d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ This software is licensed under the MIT License. -Copyright (c) 2015 - 2018 koajs and other contributors +Copyright (c) 2015 - 2021 koa.js and other contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e27b764..a533ea9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -@koa/cors -======= +# @koa/cors + +> [Cross-Origin Resource Sharing(CORS)](https://developer.mozilla.org/en/docs/Web/HTTP/Access_control_CORS) for Koa. [![NPM version][npm-image]][npm-url] [![build status][travis-image]][travis-url] [![Test coverage][codecov-image]][codecov-url] -[![David deps][david-image]][david-url] [![npm download][download-image]][download-url] [npm-image]: https://img.shields.io/npm/v/@koa/cors.svg?style=flat-square @@ -13,17 +13,16 @@ [travis-url]: https://travis-ci.org/koajs/cors [codecov-image]: https://codecov.io/github/koajs/cors/coverage.svg?branch=v2.x [codecov-url]: https://codecov.io/github/koajs/cors?branch=v2.x -[david-image]: https://img.shields.io/david/koajs/cors.svg?style=flat-square -[david-url]: https://david-dm.org/koajs/cors [download-image]: https://img.shields.io/npm/dm/@koa/cors.svg?style=flat-square [download-url]: https://npmjs.org/package/@koa/cors -[Cross-Origin Resource Sharing(CORS)](https://developer.mozilla.org/en/docs/Web/HTTP/Access_control_CORS) for koa - ## Installation ```bash -$ npm install @koa/cors --save +# npm +$ npm install @koa/cors +# yarn +$ yarn add @koa/cors ``` ## Quick start @@ -31,11 +30,11 @@ $ npm install @koa/cors --save Enable cors with default options: - origin: request Origin header -- allowMethods: GET,HEAD,PUT,POST,DELETE,PATCH +- allowMethods: GET,HEAD,PUT,PATCH,POST,DELETE ```js -const Koa = require('koa'); -const cors = require('@koa/cors'); +const Koa = require("koa"); +const cors = require("@koa/cors"); const app = new Koa(); app.use(cors()); @@ -60,6 +59,8 @@ app.use(cors()); */ ``` +You can find an example of origin option as a function [here](https://github.com/koajs/cors/issues/52#issuecomment-413887382). + ## License -[MIT](./LICENSE) +[MIT](LICENSE) diff --git a/index.js b/index.js index de19052..f48838f 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,24 @@ +/*! + * @koa/cors + * + * Copyright(c) 2021 koa.js and other contributors. + * MIT Licensed + */ + 'use strict'; +/** + * Module dependencies. + */ + const vary = require('vary'); +/** + * Expose `cors()`. + */ + +module.exports = cors; + /** * CORS middleware * @@ -16,61 +33,44 @@ const vary = require('vary'); * @return {Function} cors middleware * @api public */ -module.exports = function(options) { - const defaults = { - allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH', - }; - - options = { - ...defaults, - ...options, - }; - - if (Array.isArray(options.exposeHeaders)) { - options.exposeHeaders = options.exposeHeaders.join(','); - } - - if (Array.isArray(options.allowMethods)) { - options.allowMethods = options.allowMethods.join(','); - } - - if (Array.isArray(options.allowHeaders)) { - options.allowHeaders = options.allowHeaders.join(','); - } - - if (options.maxAge) { - options.maxAge = String(options.maxAge); - } - - options.keepHeadersOnError = options.keepHeadersOnError === undefined || !!options.keepHeadersOnError; - - return async function cors(ctx, next) { +function cors({ + maxAge, + allowMethods, + allowHeaders, + exposeHeaders, + origin: _origin, + keepHeadersOnError, + credentials: _credentials +} = { allowMethods: 'GET,HEAD,PUT,PATCH,POST,DELETE' }) { + if (Array.isArray(exposeHeaders)) exposeHeaders = exposeHeaders.join(','); + if (Array.isArray(allowMethods)) allowMethods = allowMethods.join(','); + if (Array.isArray(allowHeaders)) allowHeaders = allowHeaders.join(','); + if (maxAge) maxAge = String(maxAge); + keepHeadersOnError = keepHeadersOnError === undefined || !!keepHeadersOnError; + + return async (ctx, next) => { // If the Origin header is not present terminate this set of steps. // The request is outside the scope of this specification. const requestOrigin = ctx.get('Origin'); + if (!requestOrigin) return next(); + // Always set Vary header // https://github.com/rs/cors/issues/10 ctx.vary('Origin'); - if (!requestOrigin) return await next(); - let origin; - if (typeof options.origin === 'function') { - origin = options.origin(ctx); + if (typeof _origin === 'function') { + origin = _origin(ctx); if (origin instanceof Promise) origin = await origin; - if (!origin) return await next(); - } else { - origin = options.origin || requestOrigin; - } + if (!origin) return next(); + } else origin = _origin || requestOrigin; let credentials; - if (typeof options.credentials === 'function') { - credentials = options.credentials(ctx); + if (typeof _credentials === 'function') { + credentials = _credentials(ctx); if (credentials instanceof Promise) credentials = await credentials; - } else { - credentials = !!options.credentials; - } + } else credentials = !!_credentials; const headersSet = {}; @@ -79,69 +79,33 @@ module.exports = function(options) { headersSet[key] = value; } - if (ctx.method !== 'OPTIONS') { + if (ctx.method !== 'OPTIONS' || !ctx.get('Access-Control-Request-Method')) { // Simple Cross-Origin Request, Actual Request, and Redirects set('Access-Control-Allow-Origin', origin); - if (credentials === true) { - set('Access-Control-Allow-Credentials', 'true'); - } - - if (options.exposeHeaders) { - set('Access-Control-Expose-Headers', options.exposeHeaders); - } - - if (!options.keepHeadersOnError) { - return await next(); - } - try { - return await next(); - } catch (err) { - const errHeadersSet = err.headers || {}; - const varyWithOrigin = vary.append(errHeadersSet.vary || errHeadersSet.Vary || '', 'Origin'); - delete errHeadersSet.Vary; - - err.headers = { - ...errHeadersSet, - ...headersSet, - ...{ vary: varyWithOrigin }, - }; - throw err; - } - } else { - // Preflight Request - - // 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. - // The request is outside the scope of this specification. - if (!ctx.get('Access-Control-Request-Method')) { - // this not preflight request, ignore it - return await next(); - } - - ctx.set('Access-Control-Allow-Origin', origin); - - if (credentials === true) { - ctx.set('Access-Control-Allow-Credentials', 'true'); - } - - if (options.maxAge) { - ctx.set('Access-Control-Max-Age', options.maxAge); - } - - if (options.allowMethods) { - ctx.set('Access-Control-Allow-Methods', options.allowMethods); - } - - let allowHeaders = options.allowHeaders; - if (!allowHeaders) { - allowHeaders = ctx.get('Access-Control-Request-Headers'); - } - if (allowHeaders) { - ctx.set('Access-Control-Allow-Headers', allowHeaders); - } - - ctx.status = 204; + if (credentials === true) set('Access-Control-Allow-Credentials', 'true'); + if (exposeHeaders) set('Access-Control-Expose-Headers', exposeHeaders); + if (!keepHeadersOnError) return next(); + + return next() + // .catch((err) => { + // const errHeadersSet = err.headers || {}; + // const varyWithOrigin = vary.append(errHeadersSet.vary || errHeadersSet.Vary || '', 'Origin'); + // delete errHeadersSet.Vary; + + // err.headers = { ...errHeadersSet, ...headersSet, ...{ vary: varyWithOrigin } }; + // throw err; + // }); } + + // --> OPTIONS Request <-- // + // Preflight Request + ctx.set('Access-Control-Allow-Origin', origin); + if (credentials === true) ctx.set('Access-Control-Allow-Credentials', 'true'); + if (maxAge) ctx.set('Access-Control-Max-Age', maxAge); + if (allowMethods) ctx.set('Access-Control-Allow-Methods', allowMethods); + if (!allowHeaders) allowHeaders = ctx.get('Access-Control-Request-Headers'); + if (allowHeaders) ctx.set('Access-Control-Allow-Headers', allowHeaders); + ctx.status = 204; }; -}; +} diff --git a/package.json b/package.json index d4fe940..58bf751 100644 --- a/package.json +++ b/package.json @@ -1,49 +1,62 @@ { "name": "@koa/cors", "version": "3.1.0", - "description": "Cross-Origin Resource Sharing(CORS) for koa", + "description": "Cross-Origin Resource Sharing(CORS) for Koa.js", "main": "index.js", "files": [ "index.js" ], + "repository": { + "type": "git", + "url": "git://github.com/koajs/cors.git" + }, "scripts": { - "test": "NODE_ENV=test mocha --check-leaks -R spec -t 5000 test/*.test.js", - "test-cov": "NODE_ENV=test istanbul cover _mocha -- --check-leaks -t 5000 test/*.test.js", + "test": "cross-env NODE_ENV=test mocha --exit --check-leaks -R spec -t 5000 test/*.test.js", + "test-cov": "cross-env NODE_ENV=test nyc npm run test", "ci": "npm run lint && npm run test-cov", "lint": "eslint index.js test", "autod": "autod -w --prefix '^'" }, + "keywords": [ + "koa", + "koajs", + "@koa/cors", + "cors", + "koa-cors", + "middleware", + "Cross-Origin Resource Sharing" + ], + "author": { + "name": "fengmk2", + "email": "fengmk2@gmail.com", + "url": "http://fengmk2.com" + }, + "contributors": [ + { + "name": "Imed Jaberi", + "email": "imed-jaberi@outlook.com", + "url": "https://www.3imed-jaberi.com" + } + ], + "license": "MIT", "dependencies": { "vary": "^1.1.2" }, "devDependencies": { "autod": "*", - "eslint": "^5.15.1", - "eslint-config-egg": "^7.1.0", - "istanbul": "*", - "koa": "^2.5.1", - "mocha": "3", - "supertest": "^3.1.0" + "cross-env": "^7.0.3", + "eslint": "^7.30.0", + "eslint-config-egg": "^9.0.0", + "koa": "^2.13.1", + "mocha": "^9.0.2", + "nyc": "^15.1.0", + "supertest": "^6.1.3" }, - "homepage": "https://github.com/koajs/cors", - "repository": { - "type": "git", - "url": "git://github.com/koajs/cors.git" + "engines": { + "node": ">= 10" }, + "homepage": "https://github.com/koajs/cors", "bugs": { "url": "https://github.com/koajs/cors/issues" - }, - "keywords": [ - "cors", - "koa-cors", - "Cross-Origin Resource Sharing", - "@koa/cors", - "koa", - "koajs" - ], - "engines": { - "node": ">= 8.0.0" - }, - "author": "fengmk2 (http://fengmk2.com)", - "license": "MIT" + } } diff --git a/test/cors.test.js b/test/cors.test.js index c815601..09f2d69 100644 --- a/test/cors.test.js +++ b/test/cors.test.js @@ -39,7 +39,7 @@ describe('cors.test.js', function() { .set('Origin', 'http://koajs.com') .set('Access-Control-Request-Method', 'PUT') .expect('Access-Control-Allow-Origin', 'http://koajs.com') - .expect('Access-Control-Allow-Methods', 'GET,HEAD,PUT,POST,DELETE,PATCH') + .expect('Access-Control-Allow-Methods', 'GET,HEAD,PUT,PATCH,POST,DELETE') .expect(204, done); });