Skip to content

Commit 873d061

Browse files
committed
stream: destroying stream without error is abort
If an autoDestroy stream is destroyed by user without an error we automatically convert it to an AbortError in order to avoid a weird state.
1 parent f217025 commit 873d061

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed

doc/api/stream.md

+12
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,10 @@ This is a destructive and immediate way to destroy a stream. Previous calls to
421421
Use `end()` instead of destroy if data should flush before close, or wait for
422422
the `'drain'` event before destroying the stream.
423423

424+
If `.destroy()` is called without an `error` and `autoDestroy` is
425+
enabled, then if the stream has not completed it will be
426+
automatically destroyed with an `AbortError`.
427+
424428
```cjs
425429
const { Writable } = require('stream');
426430

@@ -1101,6 +1105,10 @@ further errors except from `_destroy()` may be emitted as `'error'`.
11011105
Implementors should not override this method, but instead implement
11021106
[`readable._destroy()`][readable-_destroy].
11031107

1108+
If `.destroy()` is called without an `error` and `autoDestroy` is
1109+
enabled, then if the stream has not completed it will be
1110+
automatically destroyed with an `AbortError`.
1111+
11041112
##### `readable.closed`
11051113

11061114
<!-- YAML
@@ -1805,6 +1813,10 @@ unless `emitClose` is set in false.
18051813
Once `destroy()` has been called, any further calls will be a no-op and no
18061814
further errors except from `_destroy()` may be emitted as `'error'`.
18071815

1816+
If `.destroy()` is called without an `error` and `autoDestroy` is
1817+
enabled, then if the stream has not completed it will be
1818+
automatically destroyed with an `AbortError`.
1819+
18081820
### `stream.finished(stream[, options], callback)`
18091821

18101822
<!-- YAML

lib/internal/streams/destroy.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22

3+
const console = require('console');
34
const {
45
aggregateTwoErrors,
56
codes: {
@@ -14,7 +15,10 @@ const {
1415
kDestroyed,
1516
isDestroyed,
1617
isFinished,
17-
isServerRequest
18+
isServerRequest,
19+
isReadable,
20+
isReadableFinished,
21+
isWritableEnded
1822
} = require('internal/streams/utils');
1923

2024
const kDestroy = Symbol('kDestroy');
@@ -86,6 +90,14 @@ function _destroy(self, err, cb) {
8690
const r = self._readableState;
8791
const w = self._writableState;
8892

93+
if (!err) {
94+
if (r?.autoDestroy && !isReadableFinished(self)) {
95+
err = new AbortError();
96+
} else if (w?.autoDestroy && !isWritableEnded(self)) {
97+
err = new AbortError();
98+
}
99+
}
100+
89101
checkError(err, w, r);
90102

91103
if (w) {
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
const { Readable, Writable } = require('stream');
4+
const common = require('../common');
5+
const assert = require('assert');
6+
7+
{
8+
const w = new Writable({
9+
write() {
10+
11+
}
12+
});
13+
w.on('error', common.mustCall((err) => {
14+
assert.strictEqual(err.name, 'AbortError');
15+
}));
16+
w.destroy();
17+
}
18+
19+
{
20+
const r = new Readable({
21+
read() {
22+
23+
}
24+
});
25+
r.on('error', common.mustCall((err) => {
26+
assert.strictEqual(err.name, 'AbortError');
27+
}));
28+
r.destroy();
29+
}

0 commit comments

Comments
 (0)