You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

210 lines
4.2 KiB

1 month ago
/*!
* destroy
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2015-2022 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var EventEmitter = require('events').EventEmitter
var ReadStream = require('fs').ReadStream
var Stream = require('stream')
var Zlib = require('zlib')
/**
* Module exports.
* @public
*/
module.exports = destroy
/**
* Destroy the given stream, and optionally suppress any future `error` events.
*
* @param {object} stream
* @param {boolean} suppress
* @public
*/
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
}
/**
* Destroy a ReadStream.
*
* @param {object} stream
* @private
*/
function destroyReadStream (stream) {
stream.destroy()
if (typeof stream.close === 'function') {
// node.js core bug work-around
stream.on('open', onOpenClose)
}
}
/**
* 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()
}
/**
* On open handler to close stream.
* @private
*/
function onOpenClose () {
if (typeof this.fd === 'number') {
// actually close down the fd
this.close()
}
}