Skip to content

Commit a940143

Browse files
committed
zlib: align with streams
- Ensure automatic destruction only happens after both 'end' and 'finish' has been emitted through autoDestroy. - Ensure close() callback is always invoked. - Ensure 'error' is only emitted once. PR-URL: #32220 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 543c046 commit a940143

File tree

2 files changed

+34
-17
lines changed

2 files changed

+34
-17
lines changed

lib/zlib.js

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,15 @@ const {
5555
} = require('internal/util/types');
5656
const binding = internalBinding('zlib');
5757
const assert = require('internal/assert');
58+
const finished = require('internal/streams/end-of-stream');
5859
const {
5960
Buffer,
6061
kMaxLength
6162
} = require('buffer');
6263
const { owner_symbol } = require('internal/async_hooks').symbols;
6364

6465
const kFlushFlag = Symbol('kFlushFlag');
66+
const kError = Symbol('kError');
6567

6668
const constants = internalBinding('constants').zlib;
6769
const {
@@ -173,14 +175,13 @@ function zlibOnError(message, errno, code) {
173175
const self = this[owner_symbol];
174176
// There is no way to cleanly recover.
175177
// Continuing only obscures problems.
176-
_close(self);
177-
self._hadError = true;
178178

179179
// eslint-disable-next-line no-restricted-syntax
180180
const error = new Error(message);
181181
error.errno = errno;
182182
error.code = code;
183-
self.emit('error', error);
183+
self.destroy(error);
184+
self[kError] = error;
184185
}
185186

186187
// 1. Returns false for undefined and NaN
@@ -260,8 +261,8 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) {
260261
}
261262
}
262263

263-
Transform.call(this, { autoDestroy: false, ...opts });
264-
this._hadError = false;
264+
Transform.call(this, { autoDestroy: true, ...opts });
265+
this[kError] = null;
265266
this.bytesWritten = 0;
266267
this._handle = handle;
267268
handle[owner_symbol] = this;
@@ -274,7 +275,6 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) {
274275
this._defaultFlushFlag = flush;
275276
this._finishFlushFlag = finishFlush;
276277
this._defaultFullFlushFlag = fullFlush;
277-
this.once('end', this.close);
278278
this._info = opts && opts.info;
279279
}
280280
ObjectSetPrototypeOf(ZlibBase.prototype, Transform.prototype);
@@ -368,7 +368,7 @@ ZlibBase.prototype.flush = function(kind, callback) {
368368
};
369369

370370
ZlibBase.prototype.close = function(callback) {
371-
_close(this, callback);
371+
if (callback) finished(this, callback);
372372
this.destroy();
373373
};
374374

@@ -432,6 +432,8 @@ function processChunkSync(self, chunk, flushFlag) {
432432
availOutBefore); // out_len
433433
if (error)
434434
throw error;
435+
else if (self[kError])
436+
throw self[kError];
435437

436438
availOutAfter = state[0];
437439
availInAfter = state[1];
@@ -512,7 +514,7 @@ function processCallback() {
512514
const self = this[owner_symbol];
513515
const state = self._writeState;
514516

515-
if (self._hadError || self.destroyed) {
517+
if (self.destroyed) {
516518
this.buffer = null;
517519
this.cb();
518520
return;
@@ -578,10 +580,7 @@ function processCallback() {
578580
this.cb();
579581
}
580582

581-
function _close(engine, callback) {
582-
if (callback)
583-
process.nextTick(callback);
584-
583+
function _close(engine) {
585584
// Caller may invoke .close after a zlib error (which will null _handle).
586585
if (!engine._handle)
587586
return;
@@ -678,7 +677,7 @@ ObjectSetPrototypeOf(Zlib, ZlibBase);
678677
function paramsAfterFlushCallback(level, strategy, callback) {
679678
assert(this._handle, 'zlib binding closed');
680679
this._handle.params(level, strategy);
681-
if (!this._hadError) {
680+
if (!this.destroyed) {
682681
this._level = level;
683682
this._strategy = strategy;
684683
if (callback) callback();

test/parallel/test-zlib-destroy.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
11
'use strict';
22

3-
require('../common');
3+
const common = require('../common');
44

55
const assert = require('assert');
66
const zlib = require('zlib');
77

88
// Verify that the zlib transform does clean up
99
// the handle when calling destroy.
1010

11-
const ts = zlib.createGzip();
12-
ts.destroy();
13-
assert.strictEqual(ts._handle, null);
11+
{
12+
const ts = zlib.createGzip();
13+
ts.destroy();
14+
assert.strictEqual(ts._handle, null);
15+
16+
ts.on('close', common.mustCall(() => {
17+
ts.close(common.mustCall());
18+
}));
19+
}
20+
21+
{
22+
// Ensure 'error' is only emitted once.
23+
const decompress = zlib.createGunzip(15);
24+
25+
decompress.on('error', common.mustCall((err) => {
26+
decompress.close();
27+
}));
28+
29+
decompress.write('something invalid');
30+
decompress.destroy(new Error('asd'));
31+
}

0 commit comments

Comments
 (0)