Skip to content

Commit 82a68ce

Browse files
lpincaBridgeAR
authored andcommitted
stream: ensure writable.destroy() emits error once
Prevent the `'error'` event from being emitted multiple times if `writable.destroy()` is called with an error before the `_destroy()` callback is called. Emit the first error, discard all others. PR-URL: #26057Fixes: #26015 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 7e40ce1 commit 82a68ce

2 files changed

Lines changed: 40 additions & 5 deletions

File tree

‎lib/internal/streams/destroy.js‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@ function destroy(err, cb) {
1010
if(readableDestroyed||writableDestroyed){
1111
if(cb){
1212
cb(err);
13-
}elseif(err&&
14-
(!this._writableState||!this._writableState.errorEmitted)){
15-
process.nextTick(emitErrorNT,this,err);
13+
}elseif(err){
14+
if(!this._writableState){
15+
process.nextTick(emitErrorNT,this,err);
16+
}elseif(!this._writableState.errorEmitted){
17+
this._writableState.errorEmitted=true;
18+
process.nextTick(emitErrorNT,this,err);
19+
}
1620
}
21+
1722
returnthis;
1823
}
1924

@@ -31,9 +36,13 @@ function destroy(err, cb) {
3136

3237
this._destroy(err||null,(err)=>{
3338
if(!cb&&err){
34-
process.nextTick(emitErrorAndCloseNT,this,err);
35-
if(this._writableState){
39+
if(!this._writableState){
40+
process.nextTick(emitErrorAndCloseNT,this,err);
41+
}elseif(!this._writableState.errorEmitted){
3642
this._writableState.errorEmitted=true;
43+
process.nextTick(emitErrorAndCloseNT,this,err);
44+
}else{
45+
process.nextTick(emitCloseNT,this);
3746
}
3847
}elseif(cb){
3948
process.nextTick(emitCloseNT,this);

‎test/parallel/test-stream-writable-destroy.js‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,32 @@ const assert = require('assert');
152152
assert.strictEqual(write.destroyed,true);
153153
}
154154

155+
{
156+
constwritable=newWritable({
157+
destroy: common.mustCall(function(err,cb){
158+
process.nextTick(cb,newError('kaboom 1'));
159+
}),
160+
write(chunk,enc,cb){
161+
cb();
162+
}
163+
});
164+
165+
writable.on('close',common.mustCall());
166+
writable.on('error',common.expectsError({
167+
type: Error,
168+
message: 'kaboom 2'
169+
}));
170+
171+
writable.destroy();
172+
assert.strictEqual(writable.destroyed,true);
173+
assert.strictEqual(writable._writableState.errorEmitted,false);
174+
175+
// Test case where `writable.destroy()` is called again with an error before
176+
// the `_destroy()` callback is called.
177+
writable.destroy(newError('kaboom 2'));
178+
assert.strictEqual(writable._writableState.errorEmitted,true);
179+
}
180+
155181
{
156182
constwrite=newWritable({
157183
write(chunk,enc,cb){cb();}

0 commit comments

Comments
 (0)