mirror of
https://github.com/nodejs/node.git
synced 2025-05-10 17:57:53 +00:00

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>
209 lines
4.3 KiB
JavaScript
209 lines
4.3 KiB
JavaScript
'use strict';
|
|
|
|
const common = require('../common');
|
|
const { Writable } = require('stream');
|
|
const assert = require('assert');
|
|
const { inherits } = require('util');
|
|
|
|
{
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
write.on('finish', common.mustNotCall());
|
|
write.on('close', common.mustCall());
|
|
|
|
write.destroy();
|
|
assert.strictEqual(write.destroyed, true);
|
|
}
|
|
|
|
{
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
const expected = new Error('kaboom');
|
|
|
|
write.on('finish', common.mustNotCall());
|
|
write.on('close', common.mustCall());
|
|
write.on('error', common.mustCall((err) => {
|
|
assert.strictEqual(err, expected);
|
|
}));
|
|
|
|
write.destroy(expected);
|
|
assert.strictEqual(write.destroyed, true);
|
|
}
|
|
|
|
{
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
write._destroy = function(err, cb) {
|
|
assert.strictEqual(err, expected);
|
|
cb(err);
|
|
};
|
|
|
|
const expected = new Error('kaboom');
|
|
|
|
write.on('finish', common.mustNotCall('no finish event'));
|
|
write.on('close', common.mustCall());
|
|
write.on('error', common.mustCall((err) => {
|
|
assert.strictEqual(err, expected);
|
|
}));
|
|
|
|
write.destroy(expected);
|
|
assert.strictEqual(write.destroyed, true);
|
|
}
|
|
|
|
{
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); },
|
|
destroy: common.mustCall(function(err, cb) {
|
|
assert.strictEqual(err, expected);
|
|
cb();
|
|
})
|
|
});
|
|
|
|
const expected = new Error('kaboom');
|
|
|
|
write.on('finish', common.mustNotCall('no finish event'));
|
|
write.on('close', common.mustCall());
|
|
|
|
// error is swallowed by the custom _destroy
|
|
write.on('error', common.mustNotCall('no error event'));
|
|
|
|
write.destroy(expected);
|
|
assert.strictEqual(write.destroyed, true);
|
|
}
|
|
|
|
{
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
write._destroy = common.mustCall(function(err, cb) {
|
|
assert.strictEqual(err, null);
|
|
cb();
|
|
});
|
|
|
|
write.destroy();
|
|
assert.strictEqual(write.destroyed, true);
|
|
}
|
|
|
|
{
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
write._destroy = common.mustCall(function(err, cb) {
|
|
assert.strictEqual(err, null);
|
|
process.nextTick(() => {
|
|
this.end();
|
|
cb();
|
|
});
|
|
});
|
|
|
|
const fail = common.mustNotCall('no finish event');
|
|
|
|
write.on('finish', fail);
|
|
write.on('close', common.mustCall());
|
|
|
|
write.destroy();
|
|
|
|
write.removeListener('finish', fail);
|
|
write.on('finish', common.mustCall());
|
|
assert.strictEqual(write.destroyed, true);
|
|
}
|
|
|
|
{
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
const expected = new Error('kaboom');
|
|
|
|
write._destroy = common.mustCall(function(err, cb) {
|
|
assert.strictEqual(err, null);
|
|
cb(expected);
|
|
});
|
|
|
|
write.on('close', common.mustCall());
|
|
write.on('finish', common.mustNotCall('no finish event'));
|
|
write.on('error', common.mustCall((err) => {
|
|
assert.strictEqual(err, expected);
|
|
}));
|
|
|
|
write.destroy();
|
|
assert.strictEqual(write.destroyed, true);
|
|
}
|
|
|
|
{
|
|
// double error case
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
write.on('close', common.mustCall());
|
|
write.on('error', common.mustCall());
|
|
|
|
write.destroy(new Error('kaboom 1'));
|
|
write.destroy(new Error('kaboom 2'));
|
|
assert.strictEqual(write._writableState.errorEmitted, true);
|
|
assert.strictEqual(write.destroyed, true);
|
|
}
|
|
|
|
{
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
write.destroyed = true;
|
|
assert.strictEqual(write.destroyed, true);
|
|
|
|
// the internal destroy() mechanism should not be triggered
|
|
write.on('close', common.mustNotCall());
|
|
write.destroy();
|
|
}
|
|
|
|
{
|
|
function MyWritable() {
|
|
assert.strictEqual(this.destroyed, false);
|
|
this.destroyed = false;
|
|
Writable.call(this);
|
|
}
|
|
|
|
inherits(MyWritable, Writable);
|
|
|
|
new MyWritable();
|
|
}
|
|
|
|
{
|
|
// destroy and destroy callback
|
|
const write = new Writable({
|
|
write(chunk, enc, cb) { cb(); }
|
|
});
|
|
|
|
write.destroy();
|
|
|
|
const expected = new Error('kaboom');
|
|
|
|
write.destroy(expected, common.mustCall(function(err) {
|
|
assert.strictEqual(expected, err);
|
|
}));
|
|
}
|
|
|
|
{
|
|
// Checks that `._undestroy()` restores the state so that `final` will be
|
|
// called again.
|
|
const write = new Writable({
|
|
write: common.mustNotCall(),
|
|
final: common.mustCall((cb) => cb(), 2)
|
|
});
|
|
|
|
write.end();
|
|
write.destroy();
|
|
write._undestroy();
|
|
write.end();
|
|
}
|