diff --git a/lib/request.js b/lib/request.js index ce613a71a..320ac5868 100755 --- a/lib/request.js +++ b/lib/request.js @@ -586,6 +586,12 @@ internals.event = function ({ request }, event, err) { request._isPayloadPending = false; + if (event === 'close' && + request.raw.res.finished) { + + return; + } + if (event === 'end') { return; } diff --git a/lib/transmit.js b/lib/transmit.js index 781508ba2..88f4b2cf9 100755 --- a/lib/transmit.js +++ b/lib/transmit.js @@ -246,6 +246,7 @@ internals.pipe = function (request, stream) { request.raw.req.on('aborted', aborted); request.raw.req.on('close', close); + request.raw.res.on('close', close); request.raw.res.on('error', end); request.raw.res.on('finish', end); @@ -265,12 +266,20 @@ internals.pipe = function (request, stream) { internals.end = function (env, event, err) { const { request, stream, team } = env; + if (!team) { // Used instead of cleaning up emitter listeners return; } env.team = null; + if (event === 'close' && + request.raw.res.finished) { + + team.attend(); + return; + } + if (err) { request.raw.res.destroy(); Response.drain(stream); diff --git a/test/request.js b/test/request.js index d324e29b0..6cdd2897a 100755 --- a/test/request.js +++ b/test/request.js @@ -1770,4 +1770,24 @@ describe('Request', () => { await server.stop({ timeout: 1 }); }); }); + + describe('event()', () => { + + it('does not emit request error on normal close', async () => { + + const server = Hapi.server(); + const events = []; + server.events.on('request', (request, event, tags) => events.push(tags)); + + server.route({ method: 'GET', path: '/', handler: () => 'ok' }); + + await server.start(); + + const { payload } = await Wreck.get('http://localhost:' + server.info.port); + expect(payload.toString()).to.equal('ok'); + await server.stop(); + + expect(events).to.have.length(0); + }); + }); }); diff --git a/test/transmit.js b/test/transmit.js index c374785a5..e7cf6eeac 100755 --- a/test/transmit.js +++ b/test/transmit.js @@ -1875,6 +1875,41 @@ describe('transmission', () => { expect(res.statusCode).to.equal(500); }); }); + + describe('end()', () => { + + it('node v8 and v10 coverage', async () => { + + const AbortStream = class extends Stream.Readable { + + constructor(request) { + + super(); + this.request = request; + } + + _read(size) { + + if (this.isDone) { + return; + } + + this.isDone = true; + this.push('here is the response'); + this.push(null); + + this.request.raw.res.finished = true; + this.request.raw.req.emit('close'); + } + }; + + const server = Hapi.server(); + server.route({ method: 'GET', path: '/', handler: (request, h) => new AbortStream(request) }); + + const res = await server.inject({ url: '/' }); + expect(res.statusCode).to.equal(200); + }); + }); });