From 4a3d2db68764bbcd44f1229b70c161ddc490c0e7 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Tue, 21 Apr 2020 12:29:37 +0200 Subject: [PATCH 1/3] stream: finished should complete with read-only Duplex If passed a Duplex where readable or writable has been explicitly disabled then don't assume 'close' will be emitted. Fixes: https://github.com/nodejs/node/issues/32965 --- lib/internal/streams/end-of-stream.js | 3 ++- test/parallel/test-stream-finished.js | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index 4742391fd71a7a..9c8eac26668427 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -76,7 +76,8 @@ function eos(stream, opts, callback) { state && state.autoDestroy && state.emitClose && - state.closed === false + state.closed === false && + (stream.readable === readable && stream.writable === writable) ); let writableFinished = stream.writableFinished || diff --git a/test/parallel/test-stream-finished.js b/test/parallel/test-stream-finished.js index ab35d402e31cfd..44029c95d2a036 100644 --- a/test/parallel/test-stream-finished.js +++ b/test/parallel/test-stream-finished.js @@ -1,7 +1,7 @@ 'use strict'; const common = require('../common'); -const { Writable, Readable, Transform, finished } = require('stream'); +const { Writable, Readable, Transform, finished, Duplex } = require('stream'); const assert = require('assert'); const EE = require('events'); const fs = require('fs'); @@ -352,3 +352,19 @@ testClosed((opts) => new Writable({ write() {}, ...opts })); r.push(null); r.destroy(); } + +{ + const d = new Duplex({ + final(cb) { }, // Never close writable side for test purpose + read() { + this.push(null); + } + }); + + d.on('end', common.mustCall()); + + finished(d, { readable: true, writable: false }, common.mustCall()); + + d.end(); + d.resume(); +} From e269eff9ec7139232958e6e1ede002ee2968489d Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Tue, 21 Apr 2020 16:24:45 +0200 Subject: [PATCH 2/3] fixup: cast to boolean --- lib/internal/streams/end-of-stream.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index 9c8eac26668427..1708215ba4da0f 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -77,7 +77,7 @@ function eos(stream, opts, callback) { state.autoDestroy && state.emitClose && state.closed === false && - (stream.readable === readable && stream.writable === writable) + (!!stream.readable === readable && !!stream.writable === writable) ); let writableFinished = stream.writableFinished || From 1fa3af2666562a2cc4814363e41b4d23c461792a Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Tue, 21 Apr 2020 18:56:33 +0200 Subject: [PATCH 3/3] fixup: another test + fix --- lib/internal/streams/end-of-stream.js | 3 ++- test/parallel/test-stream-finished.js | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index 1708215ba4da0f..f1f1b3e5a77877 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -77,7 +77,8 @@ function eos(stream, opts, callback) { state.autoDestroy && state.emitClose && state.closed === false && - (!!stream.readable === readable && !!stream.writable === writable) + isReadable(stream) === readable && + isWritable(stream) === writable ); let writableFinished = stream.writableFinished || diff --git a/test/parallel/test-stream-finished.js b/test/parallel/test-stream-finished.js index 44029c95d2a036..17ab976c6136f4 100644 --- a/test/parallel/test-stream-finished.js +++ b/test/parallel/test-stream-finished.js @@ -368,3 +368,19 @@ testClosed((opts) => new Writable({ write() {}, ...opts })); d.end(); d.resume(); } + +{ + const d = new Duplex({ + final(cb) { }, // Never close writable side for test purpose + read() { + this.push(null); + } + }); + + d.on('end', common.mustCall()); + + d.end(); + finished(d, { readable: true, writable: false }, common.mustCall()); + + d.resume(); +}