@@ -147,6 +147,9 @@ function WritableState(options, stream, isDuplex) {
147147 // Should close be emitted on destroy. Defaults to true.
148148 this . emitClose = options . emitClose !== false ;
149149
150+ // Should .destroy() be called after 'finish' (and potentially 'end')
151+ this . autoDestroy = ! ! options . autoDestroy ;
152+
150153 // count buffered requests
151154 this . bufferedRequestCount = 0 ;
152155
@@ -235,14 +238,14 @@ function Writable(options) {
235238
236239// Otherwise people can pipe Writable streams, which is just wrong.
237240Writable . prototype . pipe = function ( ) {
238- this . emit ( 'error' , new ERR_STREAM_CANNOT_PIPE ( ) ) ;
241+ errorOrDestroy ( this , new ERR_STREAM_CANNOT_PIPE ( ) ) ;
239242} ;
240243
241244
242245function writeAfterEnd ( stream , cb ) {
243246 var er = new ERR_STREAM_WRITE_AFTER_END ( ) ;
244247 // TODO: defer error events consistently everywhere, not just the cb
245- stream . emit ( 'error' , er ) ;
248+ errorOrDestroy ( stream , er ) ;
246249 process . nextTick ( cb , er ) ;
247250}
248251
@@ -258,7 +261,7 @@ function validChunk(stream, state, chunk, cb) {
258261 er = new ERR_INVALID_ARG_TYPE ( 'chunk' , [ 'string' , 'Buffer' ] , chunk ) ;
259262 }
260263 if ( er ) {
261- stream . emit ( 'error' , er ) ;
264+ errorOrDestroy ( stream , er ) ;
262265 process . nextTick ( cb , er ) ;
263266 return false ;
264267 }
@@ -422,13 +425,13 @@ function onwriteError(stream, state, sync, er, cb) {
422425 // after error
423426 process . nextTick ( finishMaybe , stream , state ) ;
424427 stream . _writableState . errorEmitted = true ;
425- stream . emit ( 'error' , er ) ;
428+ errorOrDestroy ( stream , er ) ;
426429 } else {
427430 // the caller expect this to happen before if
428431 // it is async
429432 cb ( er ) ;
430433 stream . _writableState . errorEmitted = true ;
431- stream . emit ( 'error' , er ) ;
434+ errorOrDestroy ( stream , er ) ;
432435 // this can emit finish, but finish must
433436 // always follow error
434437 finishMaybe ( stream , state ) ;
@@ -612,7 +615,7 @@ function callFinal(stream, state) {
612615 stream . _final ( ( err ) => {
613616 state . pendingcb -- ;
614617 if ( err ) {
615- stream . emit ( 'error' , err ) ;
618+ errorOrDestroy ( stream , err ) ;
616619 }
617620 state . prefinished = true ;
618621 stream . emit ( 'prefinish' ) ;
@@ -632,13 +635,39 @@ function prefinish(stream, state) {
632635 }
633636}
634637
638+ function readableAutoDestroy ( rState ) {
639+ // In case of duplex streams we need a way to detect
640+ // if the readable side is ready for autoDestroy as well
641+ return ! rState || ( rState . autoDestroy && rState . endEmitted ) ;
642+ }
643+
644+ function errorOrDestroy ( stream , err ) {
645+ // We have tests that rely on errors being emitted
646+ // in the same tick, so changing this is semver major.
647+ // For now when you opt-in to autoDestroy we allow
648+ // the error to be emitted nextTick. In a future
649+ // semver major update we should change the default to this.
650+
651+ const rState = stream . _readableState ;
652+ const wState = stream . _writableState ;
653+
654+ if ( ( rState && rState . autoDestroy ) || ( wState && wState . autoDestroy ) )
655+ stream . destroy ( err ) ;
656+ else
657+ stream . emit ( 'error' , err ) ;
658+ }
659+
635660function finishMaybe ( stream , state ) {
636661 var need = needFinish ( state ) ;
637662 if ( need ) {
638663 prefinish ( stream , state ) ;
639664 if ( state . pendingcb === 0 ) {
640665 state . finished = true ;
641666 stream . emit ( 'finish' ) ;
667+
668+ if ( state . autoDestroy && readableAutoDestroy ( stream . _readableState ) ) {
669+ stream . destroy ( ) ;
670+ }
642671 }
643672 }
644673 return need ;
0 commit comments