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