1'use strict'; 2 3// Undocumented cb() API, needed for core, not for public API. 4// The cb() will be invoked synchronously if _destroy is synchronous. 5function destroy(err, cb) { 6 const readableDestroyed = this._readableState && 7 this._readableState.destroyed; 8 const writableDestroyed = this._writableState && 9 this._writableState.destroyed; 10 11 if (readableDestroyed || writableDestroyed) { 12 if (cb) { 13 cb(err); 14 } else if (err) { 15 if (!this._writableState) { 16 process.nextTick(emitErrorNT, this, err); 17 } else if (!this._writableState.errorEmitted) { 18 this._writableState.errorEmitted = true; 19 process.nextTick(emitErrorNT, this, err); 20 } 21 } 22 23 return this; 24 } 25 26 // We set destroyed to true before firing error callbacks in order 27 // to make it re-entrance safe in case destroy() is called within callbacks 28 29 if (this._readableState) { 30 this._readableState.destroyed = true; 31 } 32 33 // If this is a duplex stream mark the writable part as destroyed as well 34 if (this._writableState) { 35 this._writableState.destroyed = true; 36 } 37 38 this._destroy(err || null, (err) => { 39 if (!cb && err) { 40 if (!this._writableState) { 41 process.nextTick(emitErrorAndCloseNT, this, err); 42 } else if (!this._writableState.errorEmitted) { 43 this._writableState.errorEmitted = true; 44 process.nextTick(emitErrorAndCloseNT, this, err); 45 } else { 46 process.nextTick(emitCloseNT, this); 47 } 48 } else if (cb) { 49 process.nextTick(emitCloseNT, this); 50 cb(err); 51 } else { 52 process.nextTick(emitCloseNT, this); 53 } 54 }); 55 56 return this; 57} 58 59function emitErrorAndCloseNT(self, err) { 60 emitErrorNT(self, err); 61 emitCloseNT(self); 62} 63 64function emitCloseNT(self) { 65 if (self._writableState && !self._writableState.emitClose) 66 return; 67 if (self._readableState && !self._readableState.emitClose) 68 return; 69 self.emit('close'); 70} 71 72function undestroy() { 73 if (this._readableState) { 74 this._readableState.destroyed = false; 75 this._readableState.reading = false; 76 this._readableState.ended = false; 77 this._readableState.endEmitted = false; 78 } 79 80 if (this._writableState) { 81 this._writableState.destroyed = false; 82 this._writableState.ended = false; 83 this._writableState.ending = false; 84 this._writableState.finalCalled = false; 85 this._writableState.prefinished = false; 86 this._writableState.finished = false; 87 this._writableState.errorEmitted = false; 88 } 89} 90 91function emitErrorNT(self, err) { 92 self.emit('error', err); 93} 94 95function errorOrDestroy(stream, err) { 96 // We have tests that rely on errors being emitted 97 // in the same tick, so changing this is semver major. 98 // For now when you opt-in to autoDestroy we allow 99 // the error to be emitted nextTick. In a future 100 // semver major update we should change the default to this. 101 102 const rState = stream._readableState; 103 const wState = stream._writableState; 104 105 if ((rState && rState.autoDestroy) || (wState && wState.autoDestroy)) 106 stream.destroy(err); 107 else 108 stream.emit('error', err); 109} 110 111 112module.exports = { 113 destroy, 114 undestroy, 115 errorOrDestroy 116}; 117