From 0d32840caad075dd2ad94819e7a06a129aa893a7 Mon Sep 17 00:00:00 2001 From: devin ivy Date: Mon, 28 Jun 2021 17:55:15 -0400 Subject: [PATCH] Fix req end during response transmission (#4264) * Write test that fails on node v16 for req ending during response transmission * Fix request close during response transmission for node v16 --- lib/transmit.js | 1 - test/transmit.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/lib/transmit.js b/lib/transmit.js index 0005eedcb..0c173e336 100755 --- a/lib/transmit.js +++ b/lib/transmit.js @@ -257,7 +257,6 @@ internals.pipe = function (request, stream) { const end = internals.end.bind(null, env, null); request.raw.req.on('aborted', aborted); - request.raw.req.on('close', close); request.raw.res.on('close', close); request.raw.res.on('error', end); diff --git a/test/transmit.js b/test/transmit.js index 2bc3786ff..d7af8065f 100755 --- a/test/transmit.js +++ b/test/transmit.js @@ -7,11 +7,13 @@ const Net = require('net'); const Path = require('path'); const Stream = require('stream'); const Zlib = require('zlib'); +const Events = require('events'); const Boom = require('@hapi/boom'); const Code = require('@hapi/code'); const Hapi = require('..'); const Hoek = require('@hapi/hoek'); +const Bounce = require('@hapi/bounce'); const Inert = require('@hapi/inert'); const Lab = require('@hapi/lab'); const LegacyReadableStream = require('legacy-readable-stream'); @@ -1921,6 +1923,63 @@ describe('transmission', () => { const res = await server.inject('/'); expect(res.statusCode).to.equal(500); }); + + it('permits ending reading request stream while transmitting response.', async (flags) => { + + const server = Hapi.server(); + + server.route({ + method: 'post', + path: '/', + options: { + payload: { + output: 'stream' + } + }, + handler: (request, h) => { + + const stream = new Stream.PassThrough(); + + // Start transmitting stream response... + stream.push('hello '); + + Bounce.background(async () => { + + await Events.once(request.raw.res, 'pipe'); + + // ...but also only read and end the request once the response is transmitting... + request.raw.req.on('data', Hoek.ignore); + await Events.once(request.raw.req, 'end'); + + // ...and finally end the intended response once the request stream has ended. + stream.end('world'); + }); + + return h.response(stream); + } + }); + + flags.onCleanup = () => server.stop(); + await server.start(); + + const req = Http.request({ + hostname: 'localhost', + port: server.info.port, + method: 'post' + }); + + req.end('{}'); + + const [res] = await Events.once(req, 'response'); + + let result = ''; + for await (const chunk of res) { + result += chunk.toString(); + } + + // If not permitted then result will be "hello " without "world" + expect(result).to.equal('hello world'); + }); }); describe('length()', () => {