mirror of
https://github.com/nodejs/node.git
synced 2025-04-30 07:19:19 +00:00
stream: updated streams error handling
This improves error handling for streams in a few ways. 1. It ensures that no user defined methods (_read, _write, ...) are run after .destroy has been called. 2. It introduces an explicit error to tell the user if they are write to write, etc to the stream after it has been destroyed. 3. It makes streams always emit close as the last thing after they have been destroyed 4. Changes the default _destroy to not gracefully end streams. It also updates net, http2, zlib and fs to the new error handling. PR-URL: https://github.com/nodejs/node/pull/18438 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
parent
acac0f852a
commit
5e3f51648e
@ -1449,6 +1449,12 @@ An unspecified or non-specific system error has occurred within the Node.js
|
|||||||
process. The error object will have an `err.info` object property with
|
process. The error object will have an `err.info` object property with
|
||||||
additional details.
|
additional details.
|
||||||
|
|
||||||
|
<a id="ERR_STREAM_DESTROYED"></a>
|
||||||
|
### ERR_STREAM_DESTROYED
|
||||||
|
|
||||||
|
A stream method was called that cannot complete because the stream was
|
||||||
|
destroyed using `stream.destroy()`.
|
||||||
|
|
||||||
<a id="ERR_TLS_CERT_ALTNAME_INVALID"></a>
|
<a id="ERR_TLS_CERT_ALTNAME_INVALID"></a>
|
||||||
### ERR_TLS_CERT_ALTNAME_INVALID
|
### ERR_TLS_CERT_ALTNAME_INVALID
|
||||||
|
|
||||||
@ -1615,11 +1621,6 @@ The fulfilled value of a linking promise is not a `vm.Module` object.
|
|||||||
The current module's status does not allow for this operation. The specific
|
The current module's status does not allow for this operation. The specific
|
||||||
meaning of the error depends on the specific function.
|
meaning of the error depends on the specific function.
|
||||||
|
|
||||||
<a id="ERR_ZLIB_BINDING_CLOSED"></a>
|
|
||||||
### ERR_ZLIB_BINDING_CLOSED
|
|
||||||
|
|
||||||
An attempt was made to use a `zlib` object after it has already been closed.
|
|
||||||
|
|
||||||
<a id="ERR_ZLIB_INITIALIZATION_FAILED"></a>
|
<a id="ERR_ZLIB_INITIALIZATION_FAILED"></a>
|
||||||
### ERR_ZLIB_INITIALIZATION_FAILED
|
### ERR_ZLIB_INITIALIZATION_FAILED
|
||||||
|
|
||||||
|
@ -543,8 +543,10 @@ added: v8.0.0
|
|||||||
|
|
||||||
* Returns: {this}
|
* Returns: {this}
|
||||||
|
|
||||||
Destroy the stream, and emit the passed error. After this call, the
|
Destroy the stream, and emit the passed `error` and a `close` event.
|
||||||
writable stream has ended. Implementors should not override this method,
|
After this call, the writable stream has ended and subsequent calls
|
||||||
|
to `write` / `end` will give an `ERR_STREAM_DESTROYED` error.
|
||||||
|
Implementors should not override this method,
|
||||||
but instead implement [`writable._destroy`][writable-_destroy].
|
but instead implement [`writable._destroy`][writable-_destroy].
|
||||||
|
|
||||||
### Readable Streams
|
### Readable Streams
|
||||||
@ -1167,8 +1169,9 @@ myReader.on('readable', () => {
|
|||||||
added: v8.0.0
|
added: v8.0.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
Destroy the stream, and emit `'error'`. After this call, the
|
Destroy the stream, and emit `'error'` and `close`. After this call, the
|
||||||
readable stream will release any internal resources.
|
readable stream will release any internal resources and subsequent calls
|
||||||
|
to `push` will be ignored.
|
||||||
Implementors should not override this method, but instead implement
|
Implementors should not override this method, but instead implement
|
||||||
[`readable._destroy`][readable-_destroy].
|
[`readable._destroy`][readable-_destroy].
|
||||||
|
|
||||||
@ -1382,6 +1385,12 @@ constructor and implement the `writable._write()` method. The
|
|||||||
`writable._writev()` method *may* also be implemented.
|
`writable._writev()` method *may* also be implemented.
|
||||||
|
|
||||||
#### Constructor: new stream.Writable([options])
|
#### Constructor: new stream.Writable([options])
|
||||||
|
<!-- YAML
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/18438
|
||||||
|
description: Add `emitClose` option to specify if `close` is emitted on destroy
|
||||||
|
-->
|
||||||
|
|
||||||
* `options` {Object}
|
* `options` {Object}
|
||||||
* `highWaterMark` {number} Buffer level when
|
* `highWaterMark` {number} Buffer level when
|
||||||
@ -1395,6 +1404,8 @@ constructor and implement the `writable._write()` method. The
|
|||||||
it becomes possible to write JavaScript values other than string,
|
it becomes possible to write JavaScript values other than string,
|
||||||
`Buffer` or `Uint8Array` if supported by the stream implementation.
|
`Buffer` or `Uint8Array` if supported by the stream implementation.
|
||||||
Defaults to `false`
|
Defaults to `false`
|
||||||
|
* `emitClose` {boolean} Whether or not the stream should emit `close`
|
||||||
|
after it has been destroyed. Defaults to `true`
|
||||||
* `write` {Function} Implementation for the
|
* `write` {Function} Implementation for the
|
||||||
[`stream._write()`][stream-_write] method.
|
[`stream._write()`][stream-_write] method.
|
||||||
* `writev` {Function} Implementation for the
|
* `writev` {Function} Implementation for the
|
||||||
|
@ -135,10 +135,3 @@ Object.defineProperty(Duplex.prototype, 'destroyed', {
|
|||||||
this._writableState.destroyed = value;
|
this._writableState.destroyed = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Duplex.prototype._destroy = function(err, cb) {
|
|
||||||
this.push(null);
|
|
||||||
this.end();
|
|
||||||
|
|
||||||
process.nextTick(cb, err);
|
|
||||||
};
|
|
||||||
|
@ -106,6 +106,9 @@ function ReadableState(options, stream) {
|
|||||||
this.readableListening = false;
|
this.readableListening = false;
|
||||||
this.resumeScheduled = false;
|
this.resumeScheduled = false;
|
||||||
|
|
||||||
|
// Should close be emitted on destroy. Defaults to true.
|
||||||
|
this.emitClose = options.emitClose !== false;
|
||||||
|
|
||||||
// has it been destroyed
|
// has it been destroyed
|
||||||
this.destroyed = false;
|
this.destroyed = false;
|
||||||
|
|
||||||
@ -177,7 +180,6 @@ Object.defineProperty(Readable.prototype, 'destroyed', {
|
|||||||
Readable.prototype.destroy = destroyImpl.destroy;
|
Readable.prototype.destroy = destroyImpl.destroy;
|
||||||
Readable.prototype._undestroy = destroyImpl.undestroy;
|
Readable.prototype._undestroy = destroyImpl.undestroy;
|
||||||
Readable.prototype._destroy = function(err, cb) {
|
Readable.prototype._destroy = function(err, cb) {
|
||||||
this.push(null);
|
|
||||||
cb(err);
|
cb(err);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -236,6 +238,8 @@ function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) {
|
|||||||
addChunk(stream, state, chunk, true);
|
addChunk(stream, state, chunk, true);
|
||||||
} else if (state.ended) {
|
} else if (state.ended) {
|
||||||
stream.emit('error', new errors.Error('ERR_STREAM_PUSH_AFTER_EOF'));
|
stream.emit('error', new errors.Error('ERR_STREAM_PUSH_AFTER_EOF'));
|
||||||
|
} else if (state.destroyed) {
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
state.reading = false;
|
state.reading = false;
|
||||||
if (state.decoder && !encoding) {
|
if (state.decoder && !encoding) {
|
||||||
|
@ -132,7 +132,7 @@ function Transform(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function prefinish() {
|
function prefinish() {
|
||||||
if (typeof this._flush === 'function') {
|
if (typeof this._flush === 'function' && !this._readableState.destroyed) {
|
||||||
this._flush((er, data) => {
|
this._flush((er, data) => {
|
||||||
done(this, er, data);
|
done(this, er, data);
|
||||||
});
|
});
|
||||||
@ -194,7 +194,6 @@ Transform.prototype._read = function(n) {
|
|||||||
Transform.prototype._destroy = function(err, cb) {
|
Transform.prototype._destroy = function(err, cb) {
|
||||||
Duplex.prototype._destroy.call(this, err, (err2) => {
|
Duplex.prototype._destroy.call(this, err, (err2) => {
|
||||||
cb(err2);
|
cb(err2);
|
||||||
this.emit('close');
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -134,6 +134,9 @@ function WritableState(options, stream) {
|
|||||||
// True if the error was already emitted and should not be thrown again
|
// True if the error was already emitted and should not be thrown again
|
||||||
this.errorEmitted = false;
|
this.errorEmitted = false;
|
||||||
|
|
||||||
|
// Should close be emitted on destroy. Defaults to true.
|
||||||
|
this.emitClose = options.emitClose !== false;
|
||||||
|
|
||||||
// count buffered requests
|
// count buffered requests
|
||||||
this.bufferedRequestCount = 0;
|
this.bufferedRequestCount = 0;
|
||||||
|
|
||||||
@ -390,7 +393,9 @@ function doWrite(stream, state, writev, len, chunk, encoding, cb) {
|
|||||||
state.writecb = cb;
|
state.writecb = cb;
|
||||||
state.writing = true;
|
state.writing = true;
|
||||||
state.sync = true;
|
state.sync = true;
|
||||||
if (writev)
|
if (state.destroyed)
|
||||||
|
state.onwrite(new errors.Error('ERR_STREAM_DESTROYED', 'write'));
|
||||||
|
else if (writev)
|
||||||
stream._writev(chunk, state.onwrite);
|
stream._writev(chunk, state.onwrite);
|
||||||
else
|
else
|
||||||
stream._write(chunk, encoding, state.onwrite);
|
stream._write(chunk, encoding, state.onwrite);
|
||||||
@ -604,7 +609,7 @@ function callFinal(stream, state) {
|
|||||||
}
|
}
|
||||||
function prefinish(stream, state) {
|
function prefinish(stream, state) {
|
||||||
if (!state.prefinished && !state.finalCalled) {
|
if (!state.prefinished && !state.finalCalled) {
|
||||||
if (typeof stream._final === 'function') {
|
if (typeof stream._final === 'function' && !state.destroyed) {
|
||||||
state.pendingcb++;
|
state.pendingcb++;
|
||||||
state.finalCalled = true;
|
state.finalCalled = true;
|
||||||
process.nextTick(callFinal, stream, state);
|
process.nextTick(callFinal, stream, state);
|
||||||
@ -681,6 +686,5 @@ Object.defineProperty(Writable.prototype, 'destroyed', {
|
|||||||
Writable.prototype.destroy = destroyImpl.destroy;
|
Writable.prototype.destroy = destroyImpl.destroy;
|
||||||
Writable.prototype._undestroy = destroyImpl.undestroy;
|
Writable.prototype._undestroy = destroyImpl.undestroy;
|
||||||
Writable.prototype._destroy = function(err, cb) {
|
Writable.prototype._destroy = function(err, cb) {
|
||||||
this.end();
|
|
||||||
cb(err);
|
cb(err);
|
||||||
};
|
};
|
||||||
|
@ -1929,6 +1929,9 @@ function ReadStream(path, options) {
|
|||||||
if (options.highWaterMark === undefined)
|
if (options.highWaterMark === undefined)
|
||||||
options.highWaterMark = 64 * 1024;
|
options.highWaterMark = 64 * 1024;
|
||||||
|
|
||||||
|
// for backwards compat do not emit close on destroy.
|
||||||
|
options.emitClose = false;
|
||||||
|
|
||||||
Readable.call(this, options);
|
Readable.call(this, options);
|
||||||
|
|
||||||
// path will be ignored when fd is specified, so it can be falsy
|
// path will be ignored when fd is specified, so it can be falsy
|
||||||
@ -2084,6 +2087,9 @@ function WriteStream(path, options) {
|
|||||||
|
|
||||||
options = copyObject(getOptions(options, {}));
|
options = copyObject(getOptions(options, {}));
|
||||||
|
|
||||||
|
// for backwards compat do not emit close on destroy.
|
||||||
|
options.emitClose = false;
|
||||||
|
|
||||||
Writable.call(this, options);
|
Writable.call(this, options);
|
||||||
|
|
||||||
// path will be ignored when fd is specified, so it can be falsy
|
// path will be ignored when fd is specified, so it can be falsy
|
||||||
|
@ -843,6 +843,7 @@ E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running', Error);
|
|||||||
E('ERR_STDERR_CLOSE', 'process.stderr cannot be closed', Error);
|
E('ERR_STDERR_CLOSE', 'process.stderr cannot be closed', Error);
|
||||||
E('ERR_STDOUT_CLOSE', 'process.stdout cannot be closed', Error);
|
E('ERR_STDOUT_CLOSE', 'process.stdout cannot be closed', Error);
|
||||||
E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error);
|
E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error);
|
||||||
|
E('ERR_STREAM_DESTROYED', 'Cannot call %s after a stream was destroyed');
|
||||||
E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError);
|
E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError);
|
||||||
E('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF', Error);
|
E('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF', Error);
|
||||||
E('ERR_STREAM_READ_NOT_IMPLEMENTED', '_read() is not implemented', Error);
|
E('ERR_STREAM_READ_NOT_IMPLEMENTED', '_read() is not implemented', Error);
|
||||||
@ -908,7 +909,6 @@ E('ERR_VM_MODULE_NOT_LINKED',
|
|||||||
E('ERR_VM_MODULE_NOT_MODULE',
|
E('ERR_VM_MODULE_NOT_MODULE',
|
||||||
'Provided module is not an instance of Module', Error);
|
'Provided module is not an instance of Module', Error);
|
||||||
E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
|
E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
|
||||||
E('ERR_ZLIB_BINDING_CLOSED', 'zlib binding closed', Error);
|
|
||||||
E('ERR_ZLIB_INITIALIZATION_FAILED', 'Initialization failed', Error);
|
E('ERR_ZLIB_INITIALIZATION_FAILED', 'Initialization failed', Error);
|
||||||
|
|
||||||
function sysError(code, syscall, path, dest,
|
function sysError(code, syscall, path, dest,
|
||||||
|
@ -1475,6 +1475,7 @@ class Http2Stream extends Duplex {
|
|||||||
constructor(session, options) {
|
constructor(session, options) {
|
||||||
options.allowHalfOpen = true;
|
options.allowHalfOpen = true;
|
||||||
options.decodeStrings = false;
|
options.decodeStrings = false;
|
||||||
|
options.emitClose = false;
|
||||||
super(options);
|
super(options);
|
||||||
this[async_id_symbol] = -1;
|
this[async_id_symbol] = -1;
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ function destroy(err, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._destroy(err || null, (err) => {
|
this._destroy(err || null, (err) => {
|
||||||
|
process.nextTick(emitCloseNT, this);
|
||||||
if (!cb && err) {
|
if (!cb && err) {
|
||||||
process.nextTick(emitErrorNT, this, err);
|
process.nextTick(emitErrorNT, this, err);
|
||||||
if (this._writableState) {
|
if (this._writableState) {
|
||||||
@ -43,6 +44,14 @@ function destroy(err, cb) {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function emitCloseNT(self) {
|
||||||
|
if (self._writableState && !self._writableState.emitClose)
|
||||||
|
return;
|
||||||
|
if (self._readableState && !self._readableState.emitClose)
|
||||||
|
return;
|
||||||
|
self.emit('close');
|
||||||
|
}
|
||||||
|
|
||||||
function undestroy() {
|
function undestroy() {
|
||||||
if (this._readableState) {
|
if (this._readableState) {
|
||||||
this._readableState.destroyed = false;
|
this._readableState.destroyed = false;
|
||||||
|
@ -232,6 +232,11 @@ function Socket(options) {
|
|||||||
options = { fd: options }; // Legacy interface.
|
options = { fd: options }; // Legacy interface.
|
||||||
else if (options === undefined)
|
else if (options === undefined)
|
||||||
options = {};
|
options = {};
|
||||||
|
else
|
||||||
|
options = util._extend({}, options);
|
||||||
|
|
||||||
|
// For backwards compat do not emit close on destroy.
|
||||||
|
options.emitClose = false;
|
||||||
|
|
||||||
stream.Duplex.call(this, options);
|
stream.Duplex.call(this, options);
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ const {
|
|||||||
ERR_BUFFER_TOO_LARGE,
|
ERR_BUFFER_TOO_LARGE,
|
||||||
ERR_INVALID_ARG_TYPE,
|
ERR_INVALID_ARG_TYPE,
|
||||||
ERR_OUT_OF_RANGE,
|
ERR_OUT_OF_RANGE,
|
||||||
ERR_ZLIB_BINDING_CLOSED,
|
|
||||||
ERR_ZLIB_INITIALIZATION_FAILED
|
ERR_ZLIB_INITIALIZATION_FAILED
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
const Transform = require('_stream_transform');
|
const Transform = require('_stream_transform');
|
||||||
@ -392,7 +391,7 @@ Zlib.prototype.flush = function flush(kind, callback) {
|
|||||||
|
|
||||||
Zlib.prototype.close = function close(callback) {
|
Zlib.prototype.close = function close(callback) {
|
||||||
_close(this, callback);
|
_close(this, callback);
|
||||||
process.nextTick(emitCloseNT, this);
|
this.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
Zlib.prototype._transform = function _transform(chunk, encoding, cb) {
|
Zlib.prototype._transform = function _transform(chunk, encoding, cb) {
|
||||||
@ -510,7 +509,7 @@ function processChunkSync(self, chunk, flushFlag) {
|
|||||||
function processChunk(self, chunk, flushFlag, cb) {
|
function processChunk(self, chunk, flushFlag, cb) {
|
||||||
var handle = self._handle;
|
var handle = self._handle;
|
||||||
if (!handle)
|
if (!handle)
|
||||||
return cb(new ERR_ZLIB_BINDING_CLOSED());
|
assert(false, 'zlib binding closed');
|
||||||
|
|
||||||
handle.buffer = chunk;
|
handle.buffer = chunk;
|
||||||
handle.cb = cb;
|
handle.cb = cb;
|
||||||
@ -603,10 +602,6 @@ function _close(engine, callback) {
|
|||||||
engine._handle = null;
|
engine._handle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitCloseNT(self) {
|
|
||||||
self.emit('close');
|
|
||||||
}
|
|
||||||
|
|
||||||
// generic zlib
|
// generic zlib
|
||||||
// minimal 2-byte header
|
// minimal 2-byte header
|
||||||
function Deflate(opts) {
|
function Deflate(opts) {
|
||||||
|
@ -13,14 +13,14 @@ server.listen(0, common.mustCall(function() {
|
|||||||
// Test destroy returns this, even on multiple calls when it short-circuits.
|
// Test destroy returns this, even on multiple calls when it short-circuits.
|
||||||
assert.strictEqual(conn, conn.destroy().destroy());
|
assert.strictEqual(conn, conn.destroy().destroy());
|
||||||
conn.on('error', common.expectsError({
|
conn.on('error', common.expectsError({
|
||||||
code: 'ERR_SOCKET_CLOSED',
|
code: 'ERR_STREAM_DESTROYED',
|
||||||
message: 'Socket is closed',
|
message: 'Cannot call write after a stream was destroyed',
|
||||||
type: Error
|
type: Error
|
||||||
}));
|
}));
|
||||||
|
|
||||||
conn.write(Buffer.from('kaboom'), common.expectsError({
|
conn.write(Buffer.from('kaboom'), common.expectsError({
|
||||||
code: 'ERR_SOCKET_CLOSED',
|
code: 'ERR_STREAM_DESTROYED',
|
||||||
message: 'Socket is closed',
|
message: 'Cannot call write after a stream was destroyed',
|
||||||
type: Error
|
type: Error
|
||||||
}));
|
}));
|
||||||
server.close();
|
server.close();
|
||||||
|
@ -13,8 +13,9 @@ const { inherits } = require('util');
|
|||||||
|
|
||||||
duplex.resume();
|
duplex.resume();
|
||||||
|
|
||||||
duplex.on('end', common.mustCall());
|
duplex.on('end', common.mustNotCall());
|
||||||
duplex.on('finish', common.mustCall());
|
duplex.on('finish', common.mustNotCall());
|
||||||
|
duplex.on('close', common.mustCall());
|
||||||
|
|
||||||
duplex.destroy();
|
duplex.destroy();
|
||||||
assert.strictEqual(duplex.destroyed, true);
|
assert.strictEqual(duplex.destroyed, true);
|
||||||
@ -29,8 +30,8 @@ const { inherits } = require('util');
|
|||||||
|
|
||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
duplex.on('end', common.mustCall());
|
duplex.on('end', common.mustNotCall());
|
||||||
duplex.on('finish', common.mustCall());
|
duplex.on('finish', common.mustNotCall());
|
||||||
duplex.on('error', common.mustCall((err) => {
|
duplex.on('error', common.mustCall((err) => {
|
||||||
assert.strictEqual(err, expected);
|
assert.strictEqual(err, expected);
|
||||||
}));
|
}));
|
||||||
@ -78,6 +79,7 @@ const { inherits } = require('util');
|
|||||||
|
|
||||||
// error is swallowed by the custom _destroy
|
// error is swallowed by the custom _destroy
|
||||||
duplex.on('error', common.mustNotCall('no error event'));
|
duplex.on('error', common.mustNotCall('no error event'));
|
||||||
|
duplex.on('close', common.mustCall());
|
||||||
|
|
||||||
duplex.destroy(expected);
|
duplex.destroy(expected);
|
||||||
assert.strictEqual(duplex.destroyed, true);
|
assert.strictEqual(duplex.destroyed, true);
|
||||||
@ -159,8 +161,8 @@ const { inherits } = require('util');
|
|||||||
});
|
});
|
||||||
duplex.resume();
|
duplex.resume();
|
||||||
|
|
||||||
duplex.on('finish', common.mustCall());
|
duplex.on('finish', common.mustNotCall());
|
||||||
duplex.on('end', common.mustCall());
|
duplex.on('end', common.mustNotCall());
|
||||||
|
|
||||||
duplex.destroy();
|
duplex.destroy();
|
||||||
assert.strictEqual(duplex.destroyed, true);
|
assert.strictEqual(duplex.destroyed, true);
|
||||||
|
@ -11,7 +11,7 @@ const { inherits } = require('util');
|
|||||||
});
|
});
|
||||||
read.resume();
|
read.resume();
|
||||||
|
|
||||||
read.on('end', common.mustCall());
|
read.on('close', common.mustCall());
|
||||||
|
|
||||||
read.destroy();
|
read.destroy();
|
||||||
assert.strictEqual(read.destroyed, true);
|
assert.strictEqual(read.destroyed, true);
|
||||||
@ -25,7 +25,8 @@ const { inherits } = require('util');
|
|||||||
|
|
||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
read.on('end', common.mustCall());
|
read.on('end', common.mustNotCall('no end event'));
|
||||||
|
read.on('close', common.mustCall());
|
||||||
read.on('error', common.mustCall((err) => {
|
read.on('error', common.mustCall((err) => {
|
||||||
assert.strictEqual(err, expected);
|
assert.strictEqual(err, expected);
|
||||||
}));
|
}));
|
||||||
@ -47,6 +48,7 @@ const { inherits } = require('util');
|
|||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
read.on('end', common.mustNotCall('no end event'));
|
read.on('end', common.mustNotCall('no end event'));
|
||||||
|
read.on('close', common.mustCall());
|
||||||
read.on('error', common.mustCall((err) => {
|
read.on('error', common.mustCall((err) => {
|
||||||
assert.strictEqual(err, expected);
|
assert.strictEqual(err, expected);
|
||||||
}));
|
}));
|
||||||
@ -70,6 +72,7 @@ const { inherits } = require('util');
|
|||||||
|
|
||||||
// error is swallowed by the custom _destroy
|
// error is swallowed by the custom _destroy
|
||||||
read.on('error', common.mustNotCall('no error event'));
|
read.on('error', common.mustNotCall('no error event'));
|
||||||
|
read.on('close', common.mustCall());
|
||||||
|
|
||||||
read.destroy(expected);
|
read.destroy(expected);
|
||||||
assert.strictEqual(read.destroyed, true);
|
assert.strictEqual(read.destroyed, true);
|
||||||
@ -106,6 +109,7 @@ const { inherits } = require('util');
|
|||||||
const fail = common.mustNotCall('no end event');
|
const fail = common.mustNotCall('no end event');
|
||||||
|
|
||||||
read.on('end', fail);
|
read.on('end', fail);
|
||||||
|
read.on('close', common.mustCall());
|
||||||
|
|
||||||
read.destroy();
|
read.destroy();
|
||||||
|
|
||||||
@ -170,7 +174,18 @@ const { inherits } = require('util');
|
|||||||
|
|
||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
|
read.on('close', common.mustCall());
|
||||||
read.destroy(expected, common.mustCall(function(err) {
|
read.destroy(expected, common.mustCall(function(err) {
|
||||||
assert.strictEqual(expected, err);
|
assert.strictEqual(expected, err);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const read = new Readable({
|
||||||
|
read() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
read.destroy();
|
||||||
|
read.push('hi');
|
||||||
|
read.on('data', common.mustNotCall());
|
||||||
|
}
|
||||||
|
@ -11,9 +11,9 @@ const assert = require('assert');
|
|||||||
|
|
||||||
transform.resume();
|
transform.resume();
|
||||||
|
|
||||||
transform.on('end', common.mustCall());
|
transform.on('end', common.mustNotCall());
|
||||||
transform.on('close', common.mustCall());
|
transform.on('close', common.mustCall());
|
||||||
transform.on('finish', common.mustCall());
|
transform.on('finish', common.mustNotCall());
|
||||||
|
|
||||||
transform.destroy();
|
transform.destroy();
|
||||||
}
|
}
|
||||||
@ -26,8 +26,8 @@ const assert = require('assert');
|
|||||||
|
|
||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
transform.on('end', common.mustCall());
|
transform.on('end', common.mustNotCall());
|
||||||
transform.on('finish', common.mustCall());
|
transform.on('finish', common.mustNotCall());
|
||||||
transform.on('close', common.mustCall());
|
transform.on('close', common.mustCall());
|
||||||
transform.on('error', common.mustCall((err) => {
|
transform.on('error', common.mustCall((err) => {
|
||||||
assert.strictEqual(err, expected);
|
assert.strictEqual(err, expected);
|
||||||
@ -49,7 +49,7 @@ const assert = require('assert');
|
|||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
transform.on('finish', common.mustNotCall('no finish event'));
|
transform.on('finish', common.mustNotCall('no finish event'));
|
||||||
transform.on('close', common.mustNotCall('no close event'));
|
transform.on('close', common.mustCall());
|
||||||
transform.on('error', common.mustCall((err) => {
|
transform.on('error', common.mustCall((err) => {
|
||||||
assert.strictEqual(err, expected);
|
assert.strictEqual(err, expected);
|
||||||
}));
|
}));
|
||||||
@ -69,7 +69,7 @@ const assert = require('assert');
|
|||||||
transform.resume();
|
transform.resume();
|
||||||
|
|
||||||
transform.on('end', common.mustNotCall('no end event'));
|
transform.on('end', common.mustNotCall('no end event'));
|
||||||
transform.on('close', common.mustNotCall('no close event'));
|
transform.on('close', common.mustCall());
|
||||||
transform.on('finish', common.mustNotCall('no finish event'));
|
transform.on('finish', common.mustNotCall('no finish event'));
|
||||||
|
|
||||||
// error is swallowed by the custom _destroy
|
// error is swallowed by the custom _destroy
|
||||||
@ -110,7 +110,7 @@ const assert = require('assert');
|
|||||||
|
|
||||||
transform.on('finish', fail);
|
transform.on('finish', fail);
|
||||||
transform.on('end', fail);
|
transform.on('end', fail);
|
||||||
transform.on('close', fail);
|
transform.on('close', common.mustCall());
|
||||||
|
|
||||||
transform.destroy();
|
transform.destroy();
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ const assert = require('assert');
|
|||||||
cb(expected);
|
cb(expected);
|
||||||
}, 1);
|
}, 1);
|
||||||
|
|
||||||
transform.on('close', common.mustNotCall('no close event'));
|
transform.on('close', common.mustCall());
|
||||||
transform.on('finish', common.mustNotCall('no finish event'));
|
transform.on('finish', common.mustNotCall('no finish event'));
|
||||||
transform.on('end', common.mustNotCall('no end event'));
|
transform.on('end', common.mustNotCall('no end event'));
|
||||||
transform.on('error', common.mustCall((err) => {
|
transform.on('error', common.mustCall((err) => {
|
||||||
|
@ -10,7 +10,8 @@ const { inherits } = require('util');
|
|||||||
write(chunk, enc, cb) { cb(); }
|
write(chunk, enc, cb) { cb(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
write.on('finish', common.mustCall());
|
write.on('finish', common.mustNotCall());
|
||||||
|
write.on('close', common.mustCall());
|
||||||
|
|
||||||
write.destroy();
|
write.destroy();
|
||||||
assert.strictEqual(write.destroyed, true);
|
assert.strictEqual(write.destroyed, true);
|
||||||
@ -23,7 +24,8 @@ const { inherits } = require('util');
|
|||||||
|
|
||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
write.on('finish', common.mustCall());
|
write.on('finish', common.mustNotCall());
|
||||||
|
write.on('close', common.mustCall());
|
||||||
write.on('error', common.mustCall((err) => {
|
write.on('error', common.mustCall((err) => {
|
||||||
assert.strictEqual(err, expected);
|
assert.strictEqual(err, expected);
|
||||||
}));
|
}));
|
||||||
@ -45,6 +47,7 @@ const { inherits } = require('util');
|
|||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
write.on('finish', common.mustNotCall('no finish event'));
|
write.on('finish', common.mustNotCall('no finish event'));
|
||||||
|
write.on('close', common.mustCall());
|
||||||
write.on('error', common.mustCall((err) => {
|
write.on('error', common.mustCall((err) => {
|
||||||
assert.strictEqual(err, expected);
|
assert.strictEqual(err, expected);
|
||||||
}));
|
}));
|
||||||
@ -65,6 +68,7 @@ const { inherits } = require('util');
|
|||||||
const expected = new Error('kaboom');
|
const expected = new Error('kaboom');
|
||||||
|
|
||||||
write.on('finish', common.mustNotCall('no finish event'));
|
write.on('finish', common.mustNotCall('no finish event'));
|
||||||
|
write.on('close', common.mustCall());
|
||||||
|
|
||||||
// error is swallowed by the custom _destroy
|
// error is swallowed by the custom _destroy
|
||||||
write.on('error', common.mustNotCall('no error event'));
|
write.on('error', common.mustNotCall('no error event'));
|
||||||
@ -103,6 +107,7 @@ const { inherits } = require('util');
|
|||||||
const fail = common.mustNotCall('no finish event');
|
const fail = common.mustNotCall('no finish event');
|
||||||
|
|
||||||
write.on('finish', fail);
|
write.on('finish', fail);
|
||||||
|
write.on('close', common.mustCall());
|
||||||
|
|
||||||
write.destroy();
|
write.destroy();
|
||||||
|
|
||||||
@ -123,6 +128,7 @@ const { inherits } = require('util');
|
|||||||
cb(expected);
|
cb(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
write.on('close', common.mustCall());
|
||||||
write.on('finish', common.mustNotCall('no finish event'));
|
write.on('finish', common.mustNotCall('no finish event'));
|
||||||
write.on('error', common.mustCall((err) => {
|
write.on('error', common.mustCall((err) => {
|
||||||
assert.strictEqual(err, expected);
|
assert.strictEqual(err, expected);
|
||||||
@ -138,6 +144,7 @@ const { inherits } = require('util');
|
|||||||
write(chunk, enc, cb) { cb(); }
|
write(chunk, enc, cb) { cb(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
write.on('close', common.mustCall());
|
||||||
write.on('error', common.mustCall());
|
write.on('error', common.mustCall());
|
||||||
|
|
||||||
write.destroy(new Error('kaboom 1'));
|
write.destroy(new Error('kaboom 1'));
|
||||||
@ -155,7 +162,7 @@ const { inherits } = require('util');
|
|||||||
assert.strictEqual(write.destroyed, true);
|
assert.strictEqual(write.destroyed, true);
|
||||||
|
|
||||||
// the internal destroy() mechanism should not be triggered
|
// the internal destroy() mechanism should not be triggered
|
||||||
write.on('finish', common.mustNotCall());
|
write.on('close', common.mustNotCall());
|
||||||
write.destroy();
|
write.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +29,9 @@ zlib.gzip('hello', common.mustCall(function(err, out) {
|
|||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => unzip.write(out),
|
() => unzip.write(out),
|
||||||
{
|
{
|
||||||
code: 'ERR_ZLIB_BINDING_CLOSED',
|
code: 'ERR_STREAM_DESTROYED',
|
||||||
type: Error,
|
type: Error,
|
||||||
message: 'zlib binding closed'
|
message: 'Cannot call write after a stream was destroyed'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
Loading…
Reference in New Issue
Block a user