From 6d8cf43dcf3deb977f530ee391c90c5d138f8bfe Mon Sep 17 00:00:00 2001 From: Dayun Date: Mon, 31 Aug 2026 09:54:03 +0900 Subject: [PATCH] http: drain server request before destroying Signed-off-by: Dayun --- lib/internal/streams/destroy.js | 1 + .../test-http-server-for-await-keepalive.js | 75 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/parallel/test-http-server-for-await-keepalive.js diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js index 3119de5dd9d9..cb1a39b34764 100644 --- a/lib/internal/streams/destroy.js +++ b/lib/internal/streams/destroy.js @@ -330,6 +330,7 @@ function destroyer(stream, err) { // TODO: Remove isRequest branches. if (isServerRequest(stream)) { + stream._dump(); stream.socket = null; stream.destroy(err); } else if (isRequest(stream)) { diff --git a/test/parallel/test-http-server-for-await-keepalive.js b/test/parallel/test-http-server-for-await-keepalive.js new file mode 100644 index 000000000000..ad0e5822383d --- /dev/null +++ b/test/parallel/test-http-server-for-await-keepalive.js @@ -0,0 +1,75 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const agent = new http.Agent({ + keepAlive: true, + maxSockets: 1, +}); + +let serverRequests = 0; + +const server = http.createServer(async (req, res) => { + serverRequests++; + + if (serverRequests === 1) { + try { + for await (const chunk of req) { + throw new Error(`payload too large: ${chunk.length}`); + } + } catch { + res.end('payload too large'); + } + return; + } + + res.end('ok'); +}); + +server.listen(0, common.mustCall(() => { + const first = http.request({ + port: server.address().port, + method: 'POST', + agent, + }, common.mustCall((res) => { + res.resume(); + + res.on('end', common.mustCall(() => { + process.nextTick(common.mustCall(() => { + const second = http.request({ + port: server.address().port, + method: 'GET', + agent, + }, common.mustCall((res) => { + second.setTimeout(0); + assert.strictEqual(second.reusedSocket, true); + res.setEncoding('utf8'); + + let body = ''; + + res.on('data', (chunk) => { + body += chunk; + }); + + res.on('end', common.mustCall(() => { + assert.strictEqual(body, 'ok'); + assert.strictEqual(serverRequests, 2); + + agent.destroy(); + server.close(); + })); + })); + + second.setTimeout(common.platformTimeout(1000), () => { + assert.fail('second keep-alive request timed out'); + }); + + second.end(); + })); + })); + })); + + first.end(Buffer.alloc(1_000_000)); +}));