index.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*!
  2. * destroy
  3. * Copyright(c) 2014 Jonathan Ong
  4. * Copyright(c) 2015-2022 Douglas Christopher Wilson
  5. * MIT Licensed
  6. */
  7. 'use strict'
  8. /**
  9. * Module dependencies.
  10. * @private
  11. */
  12. var EventEmitter = require('events').EventEmitter
  13. var ReadStream = require('fs').ReadStream
  14. var Stream = require('stream')
  15. var Zlib = require('zlib')
  16. /**
  17. * Module exports.
  18. * @public
  19. */
  20. module.exports = destroy
  21. /**
  22. * Destroy the given stream, and optionally suppress any future `error` events.
  23. *
  24. * @param {object} stream
  25. * @param {boolean} suppress
  26. * @public
  27. */
  28. function destroy (stream, suppress) {
  29. if (isFsReadStream(stream)) {
  30. destroyReadStream(stream)
  31. } else if (isZlibStream(stream)) {
  32. destroyZlibStream(stream)
  33. } else if (hasDestroy(stream)) {
  34. stream.destroy()
  35. }
  36. if (isEventEmitter(stream) && suppress) {
  37. stream.removeAllListeners('error')
  38. stream.addListener('error', noop)
  39. }
  40. return stream
  41. }
  42. /**
  43. * Destroy a ReadStream.
  44. *
  45. * @param {object} stream
  46. * @private
  47. */
  48. function destroyReadStream (stream) {
  49. stream.destroy()
  50. if (typeof stream.close === 'function') {
  51. // node.js core bug work-around
  52. stream.on('open', onOpenClose)
  53. }
  54. }
  55. /**
  56. * Close a Zlib stream.
  57. *
  58. * Zlib streams below Node.js 4.5.5 have a buggy implementation
  59. * of .close() when zlib encountered an error.
  60. *
  61. * @param {object} stream
  62. * @private
  63. */
  64. function closeZlibStream (stream) {
  65. if (stream._hadError === true) {
  66. var prop = stream._binding === null
  67. ? '_binding'
  68. : '_handle'
  69. stream[prop] = {
  70. close: function () { this[prop] = null }
  71. }
  72. }
  73. stream.close()
  74. }
  75. /**
  76. * Destroy a Zlib stream.
  77. *
  78. * Zlib streams don't have a destroy function in Node.js 6. On top of that
  79. * simply calling destroy on a zlib stream in Node.js 8+ will result in a
  80. * memory leak. So until that is fixed, we need to call both close AND destroy.
  81. *
  82. * PR to fix memory leak: https://github.com/nodejs/node/pull/23734
  83. *
  84. * In Node.js 6+8, it's important that destroy is called before close as the
  85. * stream would otherwise emit the error 'zlib binding closed'.
  86. *
  87. * @param {object} stream
  88. * @private
  89. */
  90. function destroyZlibStream (stream) {
  91. if (typeof stream.destroy === 'function') {
  92. // node.js core bug work-around
  93. // istanbul ignore if: node.js 0.8
  94. if (stream._binding) {
  95. // node.js < 0.10.0
  96. stream.destroy()
  97. if (stream._processing) {
  98. stream._needDrain = true
  99. stream.once('drain', onDrainClearBinding)
  100. } else {
  101. stream._binding.clear()
  102. }
  103. } else if (stream._destroy && stream._destroy !== Stream.Transform.prototype._destroy) {
  104. // node.js >= 12, ^11.1.0, ^10.15.1
  105. stream.destroy()
  106. } else if (stream._destroy && typeof stream.close === 'function') {
  107. // node.js 7, 8
  108. stream.destroyed = true
  109. stream.close()
  110. } else {
  111. // fallback
  112. // istanbul ignore next
  113. stream.destroy()
  114. }
  115. } else if (typeof stream.close === 'function') {
  116. // node.js < 8 fallback
  117. closeZlibStream(stream)
  118. }
  119. }
  120. /**
  121. * Determine if stream has destroy.
  122. * @private
  123. */
  124. function hasDestroy (stream) {
  125. return stream instanceof Stream &&
  126. typeof stream.destroy === 'function'
  127. }
  128. /**
  129. * Determine if val is EventEmitter.
  130. * @private
  131. */
  132. function isEventEmitter (val) {
  133. return val instanceof EventEmitter
  134. }
  135. /**
  136. * Determine if stream is fs.ReadStream stream.
  137. * @private
  138. */
  139. function isFsReadStream (stream) {
  140. return stream instanceof ReadStream
  141. }
  142. /**
  143. * Determine if stream is Zlib stream.
  144. * @private
  145. */
  146. function isZlibStream (stream) {
  147. return stream instanceof Zlib.Gzip ||
  148. stream instanceof Zlib.Gunzip ||
  149. stream instanceof Zlib.Deflate ||
  150. stream instanceof Zlib.DeflateRaw ||
  151. stream instanceof Zlib.Inflate ||
  152. stream instanceof Zlib.InflateRaw ||
  153. stream instanceof Zlib.Unzip
  154. }
  155. /**
  156. * No-op function.
  157. * @private
  158. */
  159. function noop () {}
  160. /**
  161. * On drain handler to clear binding.
  162. * @private
  163. */
  164. // istanbul ignore next: node.js 0.8
  165. function onDrainClearBinding () {
  166. this._binding.clear()
  167. }
  168. /**
  169. * On open handler to close stream.
  170. * @private
  171. */
  172. function onOpenClose () {
  173. if (typeof this.fd === 'number') {
  174. // actually close down the fd
  175. this.close()
  176. }
  177. }