From dc162493a55e749c8c2dc29e688a675de5173757 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 24 Dec 2019 16:22:39 +0100 Subject: [PATCH 1/3] zlib: allow writes after readable 'end' to finish Call the callback for writes that occur after the stream is closed. This also requires changes to the code to not call `.destroy()` on the stream in `.on('end')`, and to ignore chunks written afterwards. Previously, these writes would just queue up silently, as their `_write()` callback would never have been called. Fixes: https://github.com/nodejs/node/issues/30976 --- lib/zlib.js | 14 +++++--------- test/parallel/test-zlib-write-after-end.js | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 test/parallel/test-zlib-write-after-end.js diff --git a/lib/zlib.js b/lib/zlib.js index 3b9e522a06c7e5..960adbd4c84516 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -275,7 +275,7 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) { this._defaultFlushFlag = flush; this._finishFlushFlag = finishFlush; this._defaultFullFlushFlag = fullFlush; - this.once('end', this.close); + this.once('end', _close.bind(this, this)); this._info = opts && opts.info; } ObjectSetPrototypeOf(ZlibBase.prototype, Transform.prototype); @@ -487,7 +487,7 @@ function processChunkSync(self, chunk, flushFlag) { function processChunk(self, chunk, flushFlag, cb) { const handle = self._handle; - assert(handle, 'zlib binding closed'); + if (!handle) return process.nextTick(cb); handle.buffer = chunk; handle.cb = cb; @@ -513,13 +513,9 @@ function processCallback() { const self = this[owner_symbol]; const state = self._writeState; - if (self._hadError) { - this.buffer = null; - return; - } - - if (self.destroyed) { + if (self._hadError || self.destroyed) { this.buffer = null; + this.cb(); return; } @@ -539,7 +535,7 @@ function processCallback() { } if (self.destroyed) { - return; + return this.cb(); } // Exhausted the output buffer, or used all the input create a new one. diff --git a/test/parallel/test-zlib-write-after-end.js b/test/parallel/test-zlib-write-after-end.js new file mode 100644 index 00000000000000..2b31ff30dc8591 --- /dev/null +++ b/test/parallel/test-zlib-write-after-end.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +// Regression test for https://github.com/nodejs/node/issues/30976 +// Writes to a stream should finish even after the readable side has been ended. + +const data = zlib.deflateRawSync('Welcome'); + +const inflate = zlib.createInflateRaw(); + +inflate.resume(); +inflate.write(data, common.mustCall()); +inflate.write(Buffer.from([0x00]), common.mustCall()); +inflate.write(Buffer.from([0x00]), common.mustCall()); +inflate.flush(common.mustCall()); From af073c5111f443a1f94bbd9abe23a939e90f702d Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 24 Dec 2019 17:05:47 +0100 Subject: [PATCH 2/3] fixup! zlib: allow writes after readable 'end' to finish Co-Authored-By: Denys Otrishko <9109612+lundibundi@users.noreply.github.com> --- lib/zlib.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/zlib.js b/lib/zlib.js index 960adbd4c84516..31b594c3607787 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -535,7 +535,8 @@ function processCallback() { } if (self.destroyed) { - return this.cb(); + this.cb(); + return; } // Exhausted the output buffer, or used all the input create a new one. From a17b173b27556da1e7d0d1f1554d301653b947c6 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 25 Dec 2019 23:04:42 +0100 Subject: [PATCH 3/3] fixup! zlib: allow writes after readable 'end' to finish Co-Authored-By: Ben Noordhuis --- lib/zlib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zlib.js b/lib/zlib.js index 31b594c3607787..e85cfa9b1f1b5c 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -275,7 +275,7 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) { this._defaultFlushFlag = flush; this._finishFlushFlag = finishFlush; this._defaultFullFlushFlag = fullFlush; - this.once('end', _close.bind(this, this)); + this.once('end', _close.bind(null, this)); this._info = opts && opts.info; } ObjectSetPrototypeOf(ZlibBase.prototype, Transform.prototype);