From 7c6b543d5839e3bfdef3818ddd28a9da9c38821f 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 5a83849086294f..d7352565454d89 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -370,18 +370,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 47d5e948ab7970..7be803a87c4ce0 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 ce54af2cce709d41f0eac7a94271ef0447f15cbf 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 d7352565454d89..ced8d0f81a911c 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -377,7 +377,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; } @@ -387,7 +387,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 7be803a87c4ce0..f73e16f0d532c7 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 };