Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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
11 changes: 5 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -13,29 +13,28 @@
[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

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());
Expand All @@ -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)
170 changes: 67 additions & 103 deletions index.js
Original file line number Diff line number Diff line change
@@ -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
*
Expand All @@ -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 = {};

Expand All @@ -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;
};
};
}
Loading