diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index 528137418d42db..fd1216b227973f 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -118,7 +118,7 @@ function eos(stream, options, callback) { return callback.call(stream, errored); } - if (readable && !readableFinished) { + if (readable && !readableFinished && isReadableNodeStream(stream, true)) { if (!isReadableFinished(stream, false)) return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); diff --git a/lib/internal/streams/utils.js b/lib/internal/streams/utils.js index 854daae9de41b8..7838494b3153b1 100644 --- a/lib/internal/streams/utils.js +++ b/lib/internal/streams/utils.js @@ -9,11 +9,15 @@ const { const kDestroyed = Symbol('kDestroyed'); const kIsDisturbed = Symbol('kIsDisturbed'); -function isReadableNodeStream(obj) { +function isReadableNodeStream(obj, strict = false) { return !!( obj && typeof obj.pipe === 'function' && typeof obj.on === 'function' && + ( + !strict || + (typeof obj.pause === 'function' && typeof obj.resume === 'function') + ) && (!obj._writableState || obj._readableState?.readable !== false) && // Duplex (!obj._writableState || obj._readableState) // Writable has .pipe. ); diff --git a/test/parallel/test-stream-finished.js b/test/parallel/test-stream-finished.js index 51838a382ee3bb..b05b8c542190eb 100644 --- a/test/parallel/test-stream-finished.js +++ b/test/parallel/test-stream-finished.js @@ -643,3 +643,20 @@ testClosed((opts) => new Writable({ write() {}, ...opts })); const s = new Stream(); finished(s, common.mustNotCall()); } + +{ + const server = http.createServer(common.mustCall(function(req, res) { + fs.createReadStream(__filename).pipe(res); + finished(res, common.mustCall(function(err) { + assert.strictEqual(err, undefined); + })); + })).listen(0, function() { + http.request( + { method: 'GET', port: this.address().port }, + common.mustCall(function(res) { + res.resume(); + server.close(); + }) + ).end(); + }); +}