1'use strict'; 2 3// Backwards compat. cb() is undocumented and unused in core but 4// unfortunately might be used by modules. 5function destroy(err, cb) { 6 const r = this._readableState; 7 const w = this._writableState; 8 9 if ((w && w.destroyed) || (r && r.destroyed)) { 10 if (typeof cb === 'function') { 11 cb(); 12 } 13 14 return this; 15 } 16 17 if (err) { 18 // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 19 err.stack; // eslint-disable-line no-unused-expressions 20 21 if (w && !w.errored) { 22 w.errored = err; 23 } 24 if (r && !r.errored) { 25 r.errored = err; 26 } 27 } 28 29 // We set destroyed to true before firing error callbacks in order 30 // to make it re-entrance safe in case destroy() is called within callbacks 31 32 if (w) { 33 w.destroyed = true; 34 } 35 if (r) { 36 r.destroyed = true; 37 } 38 39 this._destroy(err || null, (err) => { 40 if (err) { 41 // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 42 err.stack; // eslint-disable-line no-unused-expressions 43 44 if (w && !w.errored) { 45 w.errored = err; 46 } 47 if (r && !r.errored) { 48 r.errored = err; 49 } 50 } 51 52 if (w) { 53 w.closed = true; 54 } 55 if (r) { 56 r.closed = true; 57 } 58 59 if (typeof cb === 'function') { 60 cb(err); 61 } 62 63 if (err) { 64 process.nextTick(emitErrorCloseNT, this, err); 65 } else { 66 process.nextTick(emitCloseNT, this); 67 } 68 }); 69 70 return this; 71} 72 73function emitErrorCloseNT(self, err) { 74 emitErrorNT(self, err); 75 emitCloseNT(self); 76} 77 78function emitCloseNT(self) { 79 const r = self._readableState; 80 const w = self._writableState; 81 82 if (r) { 83 r.closeEmitted = true; 84 } 85 86 if ((w && w.emitClose) || (r && r.emitClose)) { 87 self.emit('close'); 88 } 89} 90 91function emitErrorNT(self, err) { 92 const r = self._readableState; 93 const w = self._writableState; 94 95 if ((w && w.errorEmitted) || (r && r.errorEmitted)) { 96 return; 97 } 98 99 if (w) { 100 w.errorEmitted = true; 101 } 102 if (r) { 103 r.errorEmitted = true; 104 } 105 106 self.emit('error', err); 107} 108 109function undestroy() { 110 const r = this._readableState; 111 const w = this._writableState; 112 113 if (r) { 114 r.closed = false; 115 r.closeEmitted = false; 116 r.destroyed = false; 117 r.errored = null; 118 r.errorEmitted = false; 119 r.reading = false; 120 r.ended = false; 121 r.endEmitted = false; 122 } 123 124 if (w) { 125 w.closed = false; 126 w.closeEmitted = false; 127 w.destroyed = false; 128 w.errored = null; 129 w.ended = false; 130 w.ending = false; 131 w.finalCalled = false; 132 w.prefinished = false; 133 w.finished = false; 134 w.errorEmitted = false; 135 } 136} 137 138function errorOrDestroy(stream, err, sync) { 139 // We have tests that rely on errors being emitted 140 // in the same tick, so changing this is semver major. 141 // For now when you opt-in to autoDestroy we allow 142 // the error to be emitted nextTick. In a future 143 // semver major update we should change the default to this. 144 145 const r = stream._readableState; 146 const w = stream._writableState; 147 148 if ((w && w.destroyed) || (r && r.destroyed)) { 149 return this; 150 } 151 152 if ((r && r.autoDestroy) || (w && w.autoDestroy)) 153 stream.destroy(err); 154 else if (err) { 155 // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 156 err.stack; // eslint-disable-line no-unused-expressions 157 158 if (w && !w.errored) { 159 w.errored = err; 160 } 161 if (r && !r.errored) { 162 r.errored = err; 163 } 164 165 if (sync) { 166 process.nextTick(emitErrorNT, stream, err); 167 } else { 168 emitErrorNT(stream, err); 169 } 170 } 171} 172 173function isRequest(stream) { 174 return stream && stream.setHeader && typeof stream.abort === 'function'; 175} 176 177// Normalize destroy for legacy. 178function destroyer(stream, err) { 179 if (isRequest(stream)) return stream.abort(); 180 if (isRequest(stream.req)) return stream.req.abort(); 181 if (typeof stream.destroy === 'function') return stream.destroy(err); 182 if (typeof stream.close === 'function') return stream.close(); 183} 184 185module.exports = { 186 destroyer, 187 destroy, 188 undestroy, 189 errorOrDestroy 190}; 191