Skip to content

Commit f64c640

Browse files
committed
stream: don't emit end after close
Readable stream could emit 'end' after 'close'. PR-URL: #33076 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 4bc7025 commit f64c640

File tree

5 files changed

+30
-4
lines changed

5 files changed

+30
-4
lines changed

lib/_stream_readable.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ function ReadableState(options, stream, isDuplex) {
153153
// Indicates whether the stream has finished destroying.
154154
this.closed = false;
155155

156+
// True if close has been emitted or would have been emitted
157+
// depending on emitClose.
158+
this.closeEmitted = false;
159+
156160
// Crypto is kind of old and crusty. Historically, its default string
157161
// encoding is 'binary' so we have to make this configurable.
158162
// Everything else in the universe uses 'utf8', though.
@@ -1216,7 +1220,8 @@ function endReadableNT(state, stream) {
12161220
debug('endReadableNT', state.endEmitted, state.length);
12171221

12181222
// Check that we didn't get one last unshift.
1219-
if (!state.errorEmitted && !state.endEmitted && state.length === 0) {
1223+
if (!state.errorEmitted && !state.closeEmitted &&
1224+
!state.endEmitted && state.length === 0) {
12201225
state.endEmitted = true;
12211226
stream.emit('end');
12221227

lib/internal/streams/destroy.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ function emitCloseNT(self) {
7676
if (w) {
7777
w.closeEmitted = true;
7878
}
79+
if (r) {
80+
r.closeEmitted = true;
81+
}
7982

8083
if ((w && w.emitClose) || (r && r.emitClose)) {
8184
self.emit('close');
@@ -106,12 +109,13 @@ function undestroy() {
106109

107110
if (r) {
108111
r.closed = false;
112+
r.closeEmitted = false;
109113
r.destroyed = false;
110114
r.errored = false;
115+
r.errorEmitted = false;
111116
r.reading = false;
112117
r.ended = false;
113118
r.endEmitted = false;
114-
r.errorEmitted = false;
115119
}
116120

117121
if (w) {

test/parallel/test-stream-duplex-destroy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const assert = require('assert');
124124

125125
duplex.removeListener('end', fail);
126126
duplex.removeListener('finish', fail);
127-
duplex.on('end', common.mustCall());
127+
duplex.on('end', common.mustNotCall());
128128
duplex.on('finish', common.mustCall());
129129
assert.strictEqual(duplex.destroyed, true);
130130
}

test/parallel/test-stream-readable-destroy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ const assert = require('assert');
113113
read.destroy();
114114

115115
read.removeListener('end', fail);
116-
read.on('end', common.mustCall());
116+
read.on('end', common.mustNotCall());
117117
assert.strictEqual(read.destroyed, true);
118118
}
119119

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const { Readable } = require('stream');
5+
6+
{
7+
// Don't emit 'end' after 'close'.
8+
9+
const r = new Readable();
10+
11+
r.on('end', common.mustNotCall());
12+
r.resume();
13+
r.destroy();
14+
r.on('close', common.mustCall(() => {
15+
r.push(null);
16+
}));
17+
}

0 commit comments

Comments
 (0)