diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index 7499e37c7b7afb..493a464d07b295 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -57,6 +57,10 @@ function destroyer(stream, reading, writing, final, callback) { return callback(); } + if (!err && reading && !writing && stream.writable) { + return callback(); + } + if (err || !final || !stream.readable) { destroyImpl.destroyer(stream, err); } diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js index 17532854f974fd..5175b7e07c53c2 100644 --- a/test/parallel/test-stream-pipeline.js +++ b/test/parallel/test-stream-pipeline.js @@ -1016,3 +1016,19 @@ const { promisify } = require('util'); req.on('error', common.mustNotCall()); }); } + +{ + // Might still want to be able to use the writable side + // of src. This is in the case where e.g. the Duplex input + // is not directly connected to its output. Such a case could + // happen when the Duplex is reading from a socket and then echos + // the data back on the same socket. + const src = new PassThrough(); + assert.strictEqual(src.writable, true); + const dst = new PassThrough(); + pipeline(src, dst, common.mustCall((err) => { + assert.strictEqual(src.writable, true); + assert.strictEqual(src.destroyed, false); + })); + src.push(null); +}