Skip to content

Commit 26afb4e

Browse files
committed
Keep headers after an error
1 parent c3a4281 commit 26afb4e

File tree

3 files changed

+120
-30
lines changed

3 files changed

+120
-30
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ app.use(cors());
5454
* - {String|Array} allowHeaders `Access-Control-Allow-Headers`
5555
* - {String|Number} maxAge `Access-Control-Max-Age` in seconds
5656
* - {Boolean} credentials `Access-Control-Allow-Credentials`
57+
* - {Boolean} keepHeadersOnError Add set headers to `err.header` if an error is thrown
5758
* @return {Function}
5859
* @api public
5960
*/

index.js

+55-30
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ var copy = require('copy-to');
2020
* - {String|Array} allowHeaders `Access-Control-Allow-Headers`
2121
* - {String|Number} maxAge `Access-Control-Max-Age` in seconds
2222
* - {Boolean} credentials `Access-Control-Allow-Credentials`
23+
* - {Boolean} keepHeadersOnError Add set headers to `err.header` if an error is thrown
2324
* @return {Function}
2425
* @api public
2526
*/
@@ -49,6 +50,8 @@ module.exports = function (options) {
4950

5051
options.credentials = !!options.credentials;
5152

53+
options.keepHeadersOnError = options.keepHeadersOnError === undefined || !!options.keepHeadersOnError;
54+
5255
return function* cors(next) {
5356
// If the Origin header is not present terminate this set of steps. The request is outside the scope of this specification.
5457
var requestOrigin = this.get('Origin');
@@ -70,54 +73,76 @@ module.exports = function (options) {
7073
origin = options.origin || requestOrigin;
7174
}
7275

76+
var headersSet = {};
77+
78+
function set(self, key, value) {
79+
self.set(key, value);
80+
headersSet[key] = value;
81+
}
82+
7383
if (this.method !== 'OPTIONS') {
7484
// Simple Cross-Origin Request, Actual Request, and Redirects
7585

76-
this.set('Access-Control-Allow-Origin', origin);
86+
set(this, 'Access-Control-Allow-Origin', origin);
7787

7888
if (options.credentials === true) {
79-
this.set('Access-Control-Allow-Credentials', 'true');
89+
set(this, 'Access-Control-Allow-Credentials', 'true');
8090
}
8191

8292
if (options.exposeHeaders) {
83-
this.set('Access-Control-Expose-Headers', options.exposeHeaders);
93+
set(this, 'Access-Control-Expose-Headers', options.exposeHeaders);
8494
}
85-
86-
yield next;
8795
} else {
8896
// Preflight Request
8997

9098
// If there is no Access-Control-Request-Method header or if parsing failed,
9199
// do not set any additional headers and terminate this set of steps.
92100
// The request is outside the scope of this specification.
93-
if (!this.get('Access-Control-Request-Method')) {
94-
// this not preflight request, ignore it
95-
return yield next;
96-
}
97-
98-
this.set('Access-Control-Allow-Origin', origin);
99-
100-
if (options.credentials === true) {
101-
this.set('Access-Control-Allow-Credentials', 'true');
102-
}
103-
104-
if (options.maxAge) {
105-
this.set('Access-Control-Max-Age', options.maxAge);
106-
}
107-
108-
if (options.allowMethods) {
109-
this.set('Access-Control-Allow-Methods', options.allowMethods);
101+
if (this.get('Access-Control-Request-Method')) {
102+
set(this, 'Access-Control-Allow-Origin', origin);
103+
104+
if (options.credentials === true) {
105+
set(this, 'Access-Control-Allow-Credentials', 'true');
106+
}
107+
108+
if (options.maxAge) {
109+
set(this, 'Access-Control-Max-Age', options.maxAge);
110+
}
111+
112+
if (options.allowMethods) {
113+
set(this, 'Access-Control-Allow-Methods', options.allowMethods);
114+
}
115+
116+
var allowHeaders = options.allowHeaders;
117+
if (!allowHeaders) {
118+
allowHeaders = this.get('Access-Control-Request-Headers');
119+
}
120+
if (allowHeaders) {
121+
set(this, 'Access-Control-Allow-Headers', allowHeaders);
122+
}
123+
124+
this.status = 204;
110125
}
126+
}
111127

112-
var allowHeaders = options.allowHeaders;
113-
if (!allowHeaders) {
114-
allowHeaders = this.get('Access-Control-Request-Headers');
115-
}
116-
if (allowHeaders) {
117-
this.set('Access-Control-Allow-Headers', allowHeaders);
128+
if (options.keepHeadersOnError) {
129+
try {
130+
yield next;
131+
} catch (err) {
132+
err.headers = err.headers || {};
133+
if (Object.assign) {
134+
Object.assign(err.headers, headersSet);
135+
} else {
136+
for (var key in headersSet) {
137+
if (headersSet.hasOwnProperty(key)) {
138+
err.headers[key] = headersSet[key];
139+
}
140+
}
141+
}
142+
throw err;
118143
}
119-
120-
this.status = 204;
144+
} else {
145+
yield next;
121146
}
122147
};
123148
};

test/cors.test.js

+64
Original file line numberDiff line numberDiff line change
@@ -360,4 +360,68 @@ describe('cors.test.js', function () {
360360
.expect(204, done);
361361
});
362362
});
363+
364+
describe('options.headersKeptOnError', function () {
365+
it('should keep CORS headers after an error', function (done) {
366+
var app = koa();
367+
app.use(cors());
368+
app.use(function* () {
369+
this.body = {foo: 'bar'};
370+
throw new Error('Whoops!');
371+
});
372+
373+
request(app.listen())
374+
.get('/')
375+
.set('Origin', 'http://koajs.com')
376+
.expect('Access-Control-Allow-Origin', 'http://koajs.com')
377+
.expect(/Error/)
378+
.expect(500, done);
379+
});
380+
381+
it('should not keep unrelated headers', function (done) {
382+
var app = koa();
383+
app.use(cors());
384+
app.use(function* () {
385+
this.body = {foo: 'bar'};
386+
this.set('X-Example', 'Value');
387+
throw new Error('Whoops!');
388+
});
389+
390+
request(app.listen())
391+
.get('/')
392+
.set('Origin', 'http://koajs.com')
393+
.expect('Access-Control-Allow-Origin', 'http://koajs.com')
394+
.expect(/Error/)
395+
.expect(500, function (err, res) {
396+
if (err) {
397+
return done(err);
398+
}
399+
assert(!res.headers['x-example']);
400+
done();
401+
});
402+
});
403+
404+
it('should not keep CORS headers after an error if keepHeadersOnError is false', function (done) {
405+
var app = koa();
406+
app.use(cors({
407+
keepHeadersOnError: false
408+
}));
409+
app.use(function* () {
410+
this.body = {foo: 'bar'};
411+
throw new Error('Whoops!');
412+
});
413+
414+
request(app.listen())
415+
.get('/')
416+
.set('Origin', 'http://koajs.com')
417+
.expect(/Error/)
418+
.expect(500, function (err, res) {
419+
if (err) {
420+
return done(err);
421+
}
422+
assert(!res.headers['access-control-allow-origin']);
423+
done();
424+
});
425+
});
426+
});
363427
});

0 commit comments

Comments
 (0)