| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f2e3a67 commit b6e1d22
18 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -499,6 +499,15 @@ write('hello', () => { | |||
| 499 | 499 | ||
| 500 | 500 | A Writable stream in object mode will always ignore the `encoding` argument. | |
| 501 | 501 | ||
| 502 | + ##### writable.destroy([error]) | ||
| 503 | + <!-- YAML | ||
| 504 | + added: REPLACEME | ||
| 505 | + --> | ||
| 506 | + | ||
| 507 | + Destroy the stream, and emit the passed error. After this call, the | ||
| 508 | + writible stream has ended. Implementors should not override this method, | ||
| 509 | + but instead implement [`writable._destroy`][writable-_destroy]. | ||
| 510 | + | ||
| 502 | 511 | ### Readable Streams | |
| 503 | 512 | ||
| 504 | 513 | Readable streams are an abstraction for a *source* from which data is | |
@@ -1070,6 +1079,16 @@ myReader.on('readable', () => { | |||
| 1070 | 1079 | }); | |
| 1071 | 1080 | ``` | |
| 1072 | 1081 | ||
| 1082 | + ##### readable.destroy([error]) | ||
| 1083 | + <!-- YAML | ||
| 1084 | + added: REPLACEME | ||
| 1085 | + --> | ||
| 1086 | + | ||
| 1087 | + Destroy the stream, and emit `'error'`. After this call, the | ||
| 1088 | + readable stream will release any internal resources. | ||
| 1089 | + Implementors should not override this method, but instead implement | ||
| 1090 | + [`readable._destroy`][readable-_destroy]. | ||
| 1091 | + | ||
| 1073 | 1092 | ### Duplex and Transform Streams | |
| 1074 | 1093 | ||
| 1075 | 1094 | #### Class: stream.Duplex | |
@@ -1109,6 +1128,16 @@ Examples of Transform streams include: | |||
| 1109 | 1128 | * [zlib streams][zlib] | |
| 1110 | 1129 | * [crypto streams][crypto] | |
| 1111 | 1130 | ||
| 1131 | + ##### transform.destroy([error]) | ||
| 1132 | + <!-- YAML | ||
| 1133 | + added: REPLACEME | ||
| 1134 | + --> | ||
| 1135 | + | ||
| 1136 | + Destroy the stream, and emit `'error'`. After this call, the | ||
| 1137 | + transform stream would release any internal resources. | ||
| 1138 | + implementors should not override this method, but instead implement | ||
| 1139 | + [`readable._destroy`][readable-_destroy]. | ||
| 1140 | + The default implementation of `_destroy` for `Transform` also emit `'close'`. | ||
| 1112 | 1141 | ||
| 1113 | 1142 | ## API for Stream Implementers | |
| 1114 | 1143 | ||
@@ -1248,6 +1277,8 @@ constructor and implement the `writable._write()` method. The | |||
| 1248 | 1277 | [`stream._write()`][stream-_write] method. | |
| 1249 | 1278 | * `writev` {Function} Implementation for the | |
| 1250 | 1279 | [`stream._writev()`][stream-_writev] method. | |
| 1280 | + * `destroy` {Function} Implementation for the | ||
| 1281 | + [`stream._destroy()`][writable-_destroy] method. | ||
| 1251 | 1282 | ||
| 1252 | 1283 | For example: | |
| 1253 | 1284 | ||
@@ -1358,6 +1389,15 @@ The `writable._writev()` method is prefixed with an underscore because it is | |||
| 1358 | 1389 | internal to the class that defines it, and should never be called directly by | |
| 1359 | 1390 | user programs. | |
| 1360 | 1391 | ||
| 1392 | + #### writable.\_destroy(err, callback) | ||
| 1393 | + <!-- YAML | ||
| 1394 | + added: REPLACEME | ||
| 1395 | + --> | ||
| 1396 | + | ||
| 1397 | + * `err` {Error} An error. | ||
| 1398 | + * `callback` {Function} A callback function that takes an optional error argument | ||
| 1399 | + which is invoked when the writable is destroyed. | ||
| 1400 | + | ||
| 1361 | 1401 | #### Errors While Writing | |
| 1362 | 1402 | ||
| 1363 | 1403 | It is recommended that errors occurring during the processing of the | |
@@ -1428,6 +1468,8 @@ constructor and implement the `readable._read()` method. | |||
| 1428 | 1468 | a single value instead of a Buffer of size n. Defaults to `false` | |
| 1429 | 1469 | * `read` {Function} Implementation for the [`stream._read()`][stream-_read] | |
| 1430 | 1470 | method. | |
| 1471 | + * `destroy` {Function} Implementation for the [`stream._destroy()`][readable-_destroy] | ||
| 1472 | + method. | ||
| 1431 | 1473 | ||
| 1432 | 1474 | For example: | |
| 1433 | 1475 | ||
@@ -2079,4 +2121,8 @@ readable buffer so there is nothing for a user to consume. | |||
| 2079 | 2121 | [stream-read]: #stream_readable_read_size | |
| 2080 | 2122 | [stream-resume]: #stream_readable_resume | |
| 2081 | 2123 | [stream-write]: #stream_writable_write_chunk_encoding_callback | |
| 2082 | - [zlib]: zlib.html | ||
| 2124 | + [readable-_destroy]: #stream_readable_destroy_err_callback | ||
| 2125 | + [writable-_destroy]: #stream_writable_destroy_err_callback | ||
| 2126 | + [TCP sockets]: net.html#net_class_net_socket | ||
| 2127 | + [Transform]: #stream_class_stream_transform | ||
| 2128 | + [Writable]: #stream_class_stream_writable | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -76,3 +76,33 @@ function onend() { | |||
| 76 | 76 | function onEndNT(self) { | |
| 77 | 77 | self.end(); | |
| 78 | 78 | } | |
| 79 | + | ||
| 80 | + Object.defineProperty(Duplex.prototype, 'destroyed', { | ||
| 81 | + get() { | ||
| 82 | + if (this._readableState === undefined || | ||
| 83 | + this._writableState === undefined) { | ||
| 84 | + return false; | ||
| 85 | + } | ||
| 86 | + return this._readableState.destroyed && this._writableState.destroyed; | ||
| 87 | + }, | ||
| 88 | + set(value) { | ||
| 89 | + // we ignore the value if the stream | ||
| 90 | + // has not been initialized yet | ||
| 91 | + if (this._readableState === undefined || | ||
| 92 | + this._writableState === undefined) { | ||
| 93 | + return; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + // backward compatibility, the user is explicitly | ||
| 97 | + // managing destroyed | ||
| 98 | + this._readableState.destroyed = value; | ||
| 99 | + this._writableState.destroyed = value; | ||
| 100 | + } | ||
| 101 | + }); | ||
| 102 | + | ||
| 103 | + Duplex.prototype._destroy = function(err, cb) { | ||
| 104 | + this.push(null); | ||
| 105 | + this.end(); | ||
| 106 | + | ||
| 107 | + process.nextTick(cb, err); | ||
| 108 | + }; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -30,6 +30,7 @@ const Buffer = require('buffer').Buffer; | |||
| 30 | 30 | const util = require('util'); | |
| 31 | 31 | const debug = util.debuglog('stream'); | |
| 32 | 32 | const BufferList = require('internal/streams/BufferList'); | |
| 33 | + const destroyImpl = require('internal/streams/destroy'); | ||
| 33 | 34 | var StringDecoder; | |
| 34 | 35 | ||
| 35 | 36 | util.inherits(Readable, Stream); | |
@@ -99,6 +100,9 @@ function ReadableState(options, stream) { | |||
| 99 | 100 | this.readableListening = false; | |
| 100 | 101 | this.resumeScheduled = false; | |
| 101 | 102 | ||
| 103 | + // has it been destroyed | ||
| 104 | + this.destroyed = false; | ||
| 105 | + | ||
| 102 | 106 | // Crypto is kind of old and crusty. Historically, its default string | |
| 103 | 107 | // encoding is 'binary' so we have to make this configurable. | |
| 104 | 108 | // Everything else in the universe uses 'utf8', though. | |
@@ -129,12 +133,44 @@ function Readable(options) { | |||
| 129 | 133 | // legacy | |
| 130 | 134 | this.readable = true; | |
| 131 | 135 | ||
| 132 | - if (options && typeof options.read === 'function') | ||
| 133 | - this._read = options.read; | ||
| 136 | + if (options) { | ||
| 137 | + if (typeof options.read === 'function') | ||
| 138 | + this._read = options.read; | ||
| 139 | + | ||
| 140 | + if (typeof options.destroy === 'function') | ||
| 141 | + this._destroy = options.destroy; | ||
| 142 | + } | ||
| 134 | 143 | ||
| 135 | 144 | Stream.call(this); | |
| 136 | 145 | } | |
| 137 | 146 | ||
| 147 | + Object.defineProperty(Readable.prototype, 'destroyed', { | ||
| 148 | + get() { | ||
| 149 | + if (this._readableState === undefined) { | ||
| 150 | + return false; | ||
| 151 | + } | ||
| 152 | + return this._readableState.destroyed; | ||
| 153 | + }, | ||
| 154 | + set(value) { | ||
| 155 | + // we ignore the value if the stream | ||
| 156 | + // has not been initialized yet | ||
| 157 | + if (!this._readableState) { | ||
| 158 | + return; | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + // backward compatibility, the user is explicitly | ||
| 162 | + // managing destroyed | ||
| 163 | + this._readableState.destroyed = value; | ||
| 164 | + } | ||
| 165 | + }); | ||
| 166 | + | ||
| 167 | + Readable.prototype.destroy = destroyImpl.destroy; | ||
| 168 | + Readable.prototype._undestroy = destroyImpl.undestroy; | ||
| 169 | + Readable.prototype._destroy = function(err, cb) { | ||
| 170 | + this.push(null); | ||
| 171 | + cb(err); | ||
| 172 | + }; | ||
| 173 | + | ||
| 138 | 174 | // Manually shove something into the read() buffer. | |
| 139 | 175 | // This returns true if the highWaterMark has not been hit yet, | |
| 140 | 176 | // similar to how Writable.write() returns true if you should | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -196,6 +196,14 @@ Transform.prototype._read = function(n) { | |||
| 196 | 196 | }; | |
| 197 | 197 | ||
| 198 | 198 | ||
| 199 | + Transform.prototype._destroy = function(err, cb) { | ||
| 200 | + Duplex.prototype._destroy.call(this, err, (err2) => { | ||
| 201 | + cb(err2); | ||
| 202 | + this.emit('close'); | ||
| 203 | + }); | ||
| 204 | + }; | ||
| 205 | + | ||
| 206 | + | ||
| 199 | 207 | function done(stream, er, data) { | |
| 200 | 208 | if (er) | |
| 201 | 209 | return stream.emit('error', er); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,6 +32,7 @@ const util = require('util'); | |||
| 32 | 32 | const internalUtil = require('internal/util'); | |
| 33 | 33 | const Stream = require('stream'); | |
| 34 | 34 | const Buffer = require('buffer').Buffer; | |
| 35 | + const destroyImpl = require('internal/streams/destroy'); | ||
| 35 | 36 | ||
| 36 | 37 | util.inherits(Writable, Stream); | |
| 37 | 38 | ||
@@ -66,6 +67,9 @@ function WritableState(options, stream) { | |||
| 66 | 67 | // when 'finish' is emitted | |
| 67 | 68 | this.finished = false; | |
| 68 | 69 | ||
| 70 | + // has it been destroyed | ||
| 71 | + this.destroyed = false; | ||
| 72 | + | ||
| 69 | 73 | // should we decode strings into buffers before passing to _write? | |
| 70 | 74 | // this is here so that some node-core streams can optimize string | |
| 71 | 75 | // handling at a lower level. | |
@@ -192,6 +196,9 @@ function Writable(options) { | |||
| 192 | 196 | ||
| 193 | 197 | if (typeof options.writev === 'function') | |
| 194 | 198 | this._writev = options.writev; | |
| 199 | + | ||
| 200 | + if (typeof options.destroy === 'function') | ||
| 201 | + this._destroy = options.destroy; | ||
| 195 | 202 | } | |
| 196 | 203 | ||
| 197 | 204 | Stream.call(this); | |
@@ -563,3 +570,30 @@ function onCorkedFinish(corkReq, state, err) { | |||
| 563 | 570 | state.corkedRequestsFree = corkReq; | |
| 564 | 571 | } | |
| 565 | 572 | } | |
| 573 | + | ||
| 574 | + Object.defineProperty(Writable.prototype, 'destroyed', { | ||
| 575 | + get() { | ||
| 576 | + if (this._writableState === undefined) { | ||
| 577 | + return false; | ||
| 578 | + } | ||
| 579 | + return this._writableState.destroyed; | ||
| 580 | + }, | ||
| 581 | + set(value) { | ||
| 582 | + // we ignore the value if the stream | ||
| 583 | + // has not been initialized yet | ||
| 584 | + if (!this._writableState) { | ||
| 585 | + return; | ||
| 586 | + } | ||
| 587 | + | ||
| 588 | + // backward compatibility, the user is explicitly | ||
| 589 | + // managing destroyed | ||
| 590 | + this._writableState.destroyed = value; | ||
| 591 | + } | ||
| 592 | + }); | ||
| 593 | + | ||
| 594 | + Writable.prototype.destroy = destroyImpl.destroy; | ||
| 595 | + Writable.prototype._undestroy = destroyImpl.undestroy; | ||
| 596 | + Writable.prototype._destroy = function(err, cb) { | ||
| 597 | + this.end(); | ||
| 598 | + cb(err); | ||
| 599 | + }; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1986,11 +1986,10 @@ ReadStream.prototype._read = function(n) { | |||
| 1986 | 1986 | }; | |
| 1987 | 1987 | ||
| 1988 | 1988 | ||
| 1989 | - ReadStream.prototype.destroy = function() { | ||
| 1990 | - if (this.destroyed) | ||
| 1991 | - return; | ||
| 1992 | - this.destroyed = true; | ||
| 1993 | - this.close(); | ||
| 1989 | + ReadStream.prototype._destroy = function(err, cb) { | ||
| 1990 | + this.close(function(err2) { | ||
| 1991 | + cb(err || err2); | ||
| 1992 | + }); | ||
| 1994 | 1993 | }; | |
| 1995 | 1994 | ||
| 1996 | 1995 | ||
@@ -2157,7 +2156,7 @@ WriteStream.prototype._writev = function(data, cb) { | |||
| 2157 | 2156 | }; | |
| 2158 | 2157 | ||
| 2159 | 2158 | ||
| 2160 | - WriteStream.prototype.destroy = ReadStream.prototype.destroy; | ||
| 2159 | + WriteStream.prototype._destroy = ReadStream.prototype._destroy; | ||
| 2161 | 2160 | WriteStream.prototype.close = ReadStream.prototype.close; | |
| 2162 | 2161 | ||
| 2163 | 2162 | // There is no shutdown() for files. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,10 +18,12 @@ function setupStdio() { | |||
| 18 | 18 | function getStdout() { | |
| 19 | 19 | if (stdout) return stdout; | |
| 20 | 20 | stdout = createWritableStdioStream(1); | |
| 21 | - stdout.destroy = stdout.destroySoon = function(er) { | ||
| 21 | + stdout.destroySoon = stdout.destroy; | ||
| 22 | + stdout._destroy = function(er, cb) { | ||
| 23 | + // avoid errors if we already emitted | ||
| 22 | 24 | const errors = lazyErrors(); | |
| 23 | 25 | er = er || new errors.Error('ERR_STDOUT_CLOSE'); | |
| 24 | - stdout.emit('error', er); | ||
| 26 | + cb(er); | ||
| 25 | 27 | }; | |
| 26 | 28 | if (stdout.isTTY) { | |
| 27 | 29 | process.on('SIGWINCH', () => stdout._refreshSize()); | |
@@ -32,10 +34,12 @@ function setupStdio() { | |||
| 32 | 34 | function getStderr() { | |
| 33 | 35 | if (stderr) return stderr; | |
| 34 | 36 | stderr = createWritableStdioStream(2); | |
| 35 | - stderr.destroy = stderr.destroySoon = function(er) { | ||
| 37 | + stderr.destroySoon = stderr.destroy; | ||
| 38 | + stderr._destroy = function(er, cb) { | ||
| 39 | + // avoid errors if we already emitted | ||
| 36 | 40 | const errors = lazyErrors(); | |
| 37 | 41 | er = er || new errors.Error('ERR_STDERR_CLOSE'); | |
| 38 | - stderr.emit('error', er); | ||
| 42 | + cb(er); | ||
| 39 | 43 | }; | |
| 40 | 44 | if (stderr.isTTY) { | |
| 41 | 45 | process.on('SIGWINCH', () => stderr._refreshSize()); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,65 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + // undocumented cb() API, needed for core, not for public API | ||
| 4 | + function destroy(err, cb) { | ||
| 5 | + const readableDestroyed = this._readableState && | ||
| 6 | + this._readableState.destroyed; | ||
| 7 | + const writableDestroyed = this._writableState && | ||
| 8 | + this._writableState.destroyed; | ||
| 9 | + | ||
| 10 | + if (readableDestroyed || writableDestroyed) { | ||
| 11 | + if (err && (!this._writableState || !this._writableState.errorEmitted)) { | ||
| 12 | + process.nextTick(emitErrorNT, this, err); | ||
| 13 | + } | ||
| 14 | + return; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + // we set destroyed to true before firing error callbacks in order | ||
| 18 | + // to make it re-entrance safe in case destroy() is called within callbacks | ||
| 19 | + | ||
| 20 | + if (this._readableState) { | ||
| 21 | + this._readableState.destroyed = true; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + // if this is a duplex stream mark the writable part as destroyed as well | ||
| 25 | + if (this._writableState) { | ||
| 26 | + this._writableState.destroyed = true; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + this._destroy(err || null, (err) => { | ||
| 30 | + if (!cb && err) { | ||
| 31 | + process.nextTick(emitErrorNT, this, err); | ||
| 32 | + if (this._writableState) { | ||
| 33 | + this._writableState.errorEmitted = true; | ||
| 34 | + } | ||
| 35 | + } else if (cb) { | ||
| 36 | + cb(err); | ||
| 37 | + } | ||
| 38 | + }); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + function undestroy() { | ||
| 42 | + if (this._readableState) { | ||
| 43 | + this._readableState.destroyed = false; | ||
| 44 | + this._readableState.reading = false; | ||
| 45 | + this._readableState.ended = false; | ||
| 46 | + this._readableState.endEmitted = false; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + if (this._writableState) { | ||
| 50 | + this._writableState.destroyed = false; | ||
| 51 | + this._writableState.ended = false; | ||
| 52 | + this._writableState.ending = false; | ||
| 53 | + this._writableState.finished = false; | ||
| 54 | + this._writableState.errorEmitted = false; | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + function emitErrorNT(self, err) { | ||
| 59 | + self.emit('error', err); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + module.exports = { | ||
| 63 | + destroy, | ||
| 64 | + undestroy | ||
| 65 | + }; | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments