| | |
| | | /*! |
| | | * destroy |
| | | * Copyright(c) 2014 Jonathan Ong |
| | | * Copyright(c) 2015-2022 Douglas Christopher Wilson |
| | | * MIT Licensed |
| | | */ |
| | | |
| | |
| | | * @private |
| | | */ |
| | | |
| | | var EventEmitter = require('events').EventEmitter |
| | | var ReadStream = require('fs').ReadStream |
| | | var Stream = require('stream') |
| | | var Zlib = require('zlib') |
| | | |
| | | /** |
| | | * Module exports. |
| | |
| | | module.exports = destroy |
| | | |
| | | /** |
| | | * Destroy a stream. |
| | | * Destroy the given stream, and optionally suppress any future `error` events. |
| | | * |
| | | * @param {object} stream |
| | | * @param {boolean} suppress |
| | | * @public |
| | | */ |
| | | |
| | | function destroy(stream) { |
| | | if (stream instanceof ReadStream) { |
| | | return destroyReadStream(stream) |
| | | } |
| | | |
| | | if (!(stream instanceof Stream)) { |
| | | return stream |
| | | } |
| | | |
| | | if (typeof stream.destroy === 'function') { |
| | | function destroy (stream, suppress) { |
| | | if (isFsReadStream(stream)) { |
| | | destroyReadStream(stream) |
| | | } else if (isZlibStream(stream)) { |
| | | destroyZlibStream(stream) |
| | | } else if (hasDestroy(stream)) { |
| | | stream.destroy() |
| | | } |
| | | |
| | | if (isEventEmitter(stream) && suppress) { |
| | | stream.removeAllListeners('error') |
| | | stream.addListener('error', noop) |
| | | } |
| | | |
| | | return stream |
| | |
| | | * @private |
| | | */ |
| | | |
| | | function destroyReadStream(stream) { |
| | | function destroyReadStream (stream) { |
| | | stream.destroy() |
| | | |
| | | if (typeof stream.close === 'function') { |
| | | // node.js core bug work-around |
| | | stream.on('open', onOpenClose) |
| | | } |
| | | } |
| | | |
| | | return stream |
| | | /** |
| | | * Close a Zlib stream. |
| | | * |
| | | * Zlib streams below Node.js 4.5.5 have a buggy implementation |
| | | * of .close() when zlib encountered an error. |
| | | * |
| | | * @param {object} stream |
| | | * @private |
| | | */ |
| | | |
| | | function closeZlibStream (stream) { |
| | | if (stream._hadError === true) { |
| | | var prop = stream._binding === null |
| | | ? '_binding' |
| | | : '_handle' |
| | | |
| | | stream[prop] = { |
| | | close: function () { this[prop] = null } |
| | | } |
| | | } |
| | | |
| | | stream.close() |
| | | } |
| | | |
| | | /** |
| | | * Destroy a Zlib stream. |
| | | * |
| | | * Zlib streams don't have a destroy function in Node.js 6. On top of that |
| | | * simply calling destroy on a zlib stream in Node.js 8+ will result in a |
| | | * memory leak. So until that is fixed, we need to call both close AND destroy. |
| | | * |
| | | * PR to fix memory leak: https://github.com/nodejs/node/pull/23734 |
| | | * |
| | | * In Node.js 6+8, it's important that destroy is called before close as the |
| | | * stream would otherwise emit the error 'zlib binding closed'. |
| | | * |
| | | * @param {object} stream |
| | | * @private |
| | | */ |
| | | |
| | | function destroyZlibStream (stream) { |
| | | if (typeof stream.destroy === 'function') { |
| | | // node.js core bug work-around |
| | | // istanbul ignore if: node.js 0.8 |
| | | if (stream._binding) { |
| | | // node.js < 0.10.0 |
| | | stream.destroy() |
| | | if (stream._processing) { |
| | | stream._needDrain = true |
| | | stream.once('drain', onDrainClearBinding) |
| | | } else { |
| | | stream._binding.clear() |
| | | } |
| | | } else if (stream._destroy && stream._destroy !== Stream.Transform.prototype._destroy) { |
| | | // node.js >= 12, ^11.1.0, ^10.15.1 |
| | | stream.destroy() |
| | | } else if (stream._destroy && typeof stream.close === 'function') { |
| | | // node.js 7, 8 |
| | | stream.destroyed = true |
| | | stream.close() |
| | | } else { |
| | | // fallback |
| | | // istanbul ignore next |
| | | stream.destroy() |
| | | } |
| | | } else if (typeof stream.close === 'function') { |
| | | // node.js < 8 fallback |
| | | closeZlibStream(stream) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Determine if stream has destroy. |
| | | * @private |
| | | */ |
| | | |
| | | function hasDestroy (stream) { |
| | | return stream instanceof Stream && |
| | | typeof stream.destroy === 'function' |
| | | } |
| | | |
| | | /** |
| | | * Determine if val is EventEmitter. |
| | | * @private |
| | | */ |
| | | |
| | | function isEventEmitter (val) { |
| | | return val instanceof EventEmitter |
| | | } |
| | | |
| | | /** |
| | | * Determine if stream is fs.ReadStream stream. |
| | | * @private |
| | | */ |
| | | |
| | | function isFsReadStream (stream) { |
| | | return stream instanceof ReadStream |
| | | } |
| | | |
| | | /** |
| | | * Determine if stream is Zlib stream. |
| | | * @private |
| | | */ |
| | | |
| | | function isZlibStream (stream) { |
| | | return stream instanceof Zlib.Gzip || |
| | | stream instanceof Zlib.Gunzip || |
| | | stream instanceof Zlib.Deflate || |
| | | stream instanceof Zlib.DeflateRaw || |
| | | stream instanceof Zlib.Inflate || |
| | | stream instanceof Zlib.InflateRaw || |
| | | stream instanceof Zlib.Unzip |
| | | } |
| | | |
| | | /** |
| | | * No-op function. |
| | | * @private |
| | | */ |
| | | |
| | | function noop () {} |
| | | |
| | | /** |
| | | * On drain handler to clear binding. |
| | | * @private |
| | | */ |
| | | |
| | | // istanbul ignore next: node.js 0.8 |
| | | function onDrainClearBinding () { |
| | | this._binding.clear() |
| | | } |
| | | |
| | | /** |
| | |
| | | * @private |
| | | */ |
| | | |
| | | function onOpenClose() { |
| | | function onOpenClose () { |
| | | if (typeof this.fd === 'number') { |
| | | // actually close down the fd |
| | | this.close() |