diff --git a/lib/transmit.js b/lib/transmit.js index 0e9559c5b..be1e7f940 100755 --- a/lib/transmit.js +++ b/lib/transmit.js @@ -34,7 +34,15 @@ exports.send = function (request, callback) { return internals.fail(request, err, callback); } - return internals.transmit(response, callback); + return internals.transmit(response, (err) => { + + if (err) { + request._setResponse(err); + return internals.fail(request, err, callback); + } + + return callback(); + }); }); }; @@ -274,23 +282,19 @@ internals.transmit = function (response, callback) { response.headers.etag = response.headers.etag.slice(0, -1) + '-' + (response.headers['content-encoding'] || encoding) + '"'; } - // Write headers - - const headers = Object.keys(response.headers); - for (let i = 0; i < headers.length; ++i) { - const header = headers[i]; - const value = response.headers[header]; - if (value !== undefined) { - request.raw.res.setHeader(header, value); - } - } + // Connection: close const isInjection = Shot.isInjection(request.raw.req); if (!isInjection && !request.connection._started) { - request.raw.res.setHeader('connection', 'close'); + response._header('connection', 'close'); } - request.raw.res.writeHead(response.statusCode); + // Write headers + + const res = internals.writeHead(response); + if (res) { + return process.nextTick(callback, res); + } // Write payload @@ -369,6 +373,34 @@ internals.transmit = function (response, callback) { }; +internals.writeHead = function (response) { + + const res = response.request.raw.res; + + try { + const headers = Object.keys(response.headers); + for (let i = 0; i < headers.length; ++i) { + const header = headers[i]; + const value = response.headers[header]; + if (value !== undefined) { + res.setHeader(header, value); + } + } + + res.writeHead(response.statusCode); + } + catch (err) { + + // Reset headers and return err + + res._headers = null; + res._headerNames = {}; + + return Boom.wrap(err); + } +}; + + internals.Empty = function () { Stream.Readable.call(this); diff --git a/test/transmit.js b/test/transmit.js index e12662570..6945af920 100755 --- a/test/transmit.js +++ b/test/transmit.js @@ -2344,6 +2344,55 @@ describe('transmission', () => { done(); }); }); + + it('returns 500 when node rejects a header', (done) => { + + const server = new Hapi.Server(); + server.connection(); + + const handler = function (request, reply) { + + return reply('ok').header('', 'test'); + }; + + server.route({ method: 'GET', path: '/', handler: handler }); + server.inject('/', (res) => { + + expect(res.statusCode).to.equal(500); + done(); + }); + }); + + it('returns 500 for out of range status code', (done) => { + + const server = new Hapi.Server(); + server.connection(); + + const handler = function (request, reply) { + + // Patch writeHead to always fail on out of range headers + + const origWriteHead = request.raw.res.writeHead; + request.raw.res.writeHead = function (statusCode) { + + statusCode |= 0; + if (statusCode < 100 || statusCode > 999) { + throw new RangeError(`Invalid status code: ${statusCode}`); + } + + return origWriteHead.apply(this, arguments); + }; + + return reply('ok').code(1); + }; + + server.route({ method: 'GET', path: '/', handler: handler }); + server.inject('/', (res) => { + + expect(res.statusCode).to.equal(500); + done(); + }); + }); }); describe('cache()', () => {