• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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