res: use http.ServerResponse._header when accessors exist (#930)

* Don't use http.ServerResponse._header when accessors exist

Structure of http.ServerResponse._header will change in future
Node versions. Avoid reading and setting it directly when
helpers exist.

* Add new header test case

* make things a little more strict
This commit is contained in:
jongleberry 2017-03-07 22:59:24 -08:00 committed by GitHub
parent 188c0968e1
commit e9d7abaf79
4 changed files with 58 additions and 3 deletions

View file

@ -121,8 +121,16 @@ const proto = module.exports = {
return; return;
} }
// unset all headers, and set those specified const { res } = this;
this.res._headers = {};
// first unset all headers
if (typeof res.getHeaderNames === 'function') {
res.getHeaderNames().forEach(name => res.removeHeader(name));
} else {
res._headers = {}; // Node < 7.7
}
// then set those specified
this.set(err.headers); this.set(err.headers);
// force text/plain // force text/plain

View file

@ -44,7 +44,10 @@ module.exports = {
*/ */
get header() { get header() {
return this.res._headers || {}; const { res } = this;
return typeof res.getHeaders === 'function'
? res.getHeaders()
: res._headers || {}; // Node < 7.7
}, },
/** /**

View file

@ -1,8 +1,10 @@
'use strict'; 'use strict';
const assert = require('assert');
const request = require('supertest'); const request = require('supertest');
const Koa = require('../..'); const Koa = require('../..');
const context = require('../helpers/context');
describe('ctx.onerror(err)', () => { describe('ctx.onerror(err)', () => {
it('should respond', done => { it('should respond', done => {
@ -169,5 +171,23 @@ describe('ctx.onerror(err)', () => {
.expect('Content-Type', 'text/plain; charset=utf-8') .expect('Content-Type', 'text/plain; charset=utf-8')
.expect('Internal Server Error', done); .expect('Internal Server Error', done);
}); });
it('should use res.getHeaderNames() accessor when available', () => {
let removed = 0;
const ctx = context();
ctx.app.emit = () => {};
ctx.res = {
getHeaderNames: () => ['content-type', 'content-length'],
removeHeader: () => removed++,
end: () => {},
emit: () => {}
};
ctx.onerror(new Error('error'));
assert.equal(removed, 2);
});
}); });
}); });

View file

@ -1,7 +1,9 @@
'use strict'; 'use strict';
const request = require('supertest');
const response = require('../helpers/context').response; const response = require('../helpers/context').response;
const Koa = require('../..');
describe('res.header', () => { describe('res.header', () => {
it('should return the response header object', () => { it('should return the response header object', () => {
@ -10,6 +12,28 @@ describe('res.header', () => {
res.header.should.eql({ 'x-foo': 'bar' }); res.header.should.eql({ 'x-foo': 'bar' });
}); });
it('should use res.getHeaders() accessor when available', () => {
const res = response();
res.res._headers = null;
res.res.getHeaders = () => ({ 'x-foo': 'baz' });
res.header.should.eql({ 'x-foo': 'baz' });
});
it('should return the response header object when no mocks are in use', async () => {
const app = new Koa();
let header;
app.use(ctx => {
ctx.set('x-foo', '42');
header = Object.assign({}, ctx.response.header);
});
await request(app.listen())
.get('/');
header.should.eql({ 'x-foo': '42' });
});
describe('when res._headers not present', () => { describe('when res._headers not present', () => {
it('should return empty object', () => { it('should return empty object', () => {
const res = response(); const res = response();