From 782ab3c3170c95f4cfd09501b092605ab8183073 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Tue, 19 May 2026 20:41:33 -0700 Subject: [PATCH 1/2] http: call _writeRaw callback when destroyed Fixes: https://github.com/nodejs/node/issues/36673 Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5 --- lib/_http_outgoing.js | 20 +++++++++++++++----- test/parallel/test-http-outgoing-destroy.js | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 6bf7a1f9f68d..37177e50c05a 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -411,18 +411,28 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback, byteL OutgoingMessage.prototype._writeRaw = _writeRaw; function _writeRaw(data, encoding, callback, size) { + if (typeof encoding === 'function') { + callback = encoding; + encoding = null; + } + + if (this.destroyed) { + if (typeof callback === 'function') { + process.nextTick(callback, new ERR_STREAM_DESTROYED('write')); + } + return false; + } + const conn = this[kSocket]; if (conn?.destroyed) { // The socket was destroyed. If we're still trying to write to it, // then we haven't gotten the 'close' event yet. + if (typeof callback === 'function') { + process.nextTick(callback, new ERR_STREAM_DESTROYED('write')); + } return false; } - if (typeof encoding === 'function') { - callback = encoding; - encoding = null; - } - if (conn && conn._httpMessage === this && conn.writable) { // There might be pending data in the this.output buffer. if (this.outputData.length) { diff --git a/test/parallel/test-http-outgoing-destroy.js b/test/parallel/test-http-outgoing-destroy.js index 47d5e948ab79..7be803a87c4c 100644 --- a/test/parallel/test-http-outgoing-destroy.js +++ b/test/parallel/test-http-outgoing-destroy.js @@ -15,3 +15,23 @@ const OutgoingMessage = http.OutgoingMessage; })); msg.on('error', common.mustNotCall()); } + +{ + const msg = new OutgoingMessage(); + msg.destroy(); + + assert.strictEqual(msg._writeRaw('asd', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + })), false); + msg.on('error', common.mustNotCall()); +} + +{ + const msg = new OutgoingMessage(); + msg.socket = { destroyed: true }; + + assert.strictEqual(msg._writeRaw('asd', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + })), false); + msg.on('error', common.mustNotCall()); +} From ccfc6665e854f799dead14baf39051cd99741d4d Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Wed, 20 May 2026 18:50:52 -0700 Subject: [PATCH 2/2] http: preserve destroy error in _writeRaw callback --- lib/_http_outgoing.js | 4 ++-- test/parallel/test-http-outgoing-destroy.js | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 37177e50c05a..b5a376d32e3d 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -418,7 +418,7 @@ function _writeRaw(data, encoding, callback, size) { if (this.destroyed) { if (typeof callback === 'function') { - process.nextTick(callback, new ERR_STREAM_DESTROYED('write')); + process.nextTick(callback, this.errored ?? new ERR_STREAM_DESTROYED('write')); } return false; } @@ -428,7 +428,7 @@ function _writeRaw(data, encoding, callback, size) { // The socket was destroyed. If we're still trying to write to it, // then we haven't gotten the 'close' event yet. if (typeof callback === 'function') { - process.nextTick(callback, new ERR_STREAM_DESTROYED('write')); + process.nextTick(callback, this.errored ?? new ERR_STREAM_DESTROYED('write')); } return false; } diff --git a/test/parallel/test-http-outgoing-destroy.js b/test/parallel/test-http-outgoing-destroy.js index 7be803a87c4c..f73e16f0d532 100644 --- a/test/parallel/test-http-outgoing-destroy.js +++ b/test/parallel/test-http-outgoing-destroy.js @@ -26,6 +26,17 @@ const OutgoingMessage = http.OutgoingMessage; msg.on('error', common.mustNotCall()); } +{ + const msg = new OutgoingMessage(); + const destroyError = new Error('destroyed'); + msg.destroy(destroyError); + + assert.strictEqual(msg._writeRaw('asd', common.mustCall((err) => { + assert.strictEqual(err, destroyError); + })), false); + msg.on('error', common.mustNotCall()); +} + { const msg = new OutgoingMessage(); msg.socket = { destroyed: true };