pax_global_header00006660000000000000000000000064142156750770014530gustar00rootroot0000000000000052 comment=f387a486a42869ab8ef1c37a90922925063aeb57 destroy-1.2.0/000077500000000000000000000000001421567507700132215ustar00rootroot00000000000000destroy-1.2.0/.eslintignore000066400000000000000000000000261421567507700157220ustar00rootroot00000000000000coverage node_modules destroy-1.2.0/.eslintrc.yml000066400000000000000000000000351421567507700156430ustar00rootroot00000000000000root: true extends: standard destroy-1.2.0/.github/000077500000000000000000000000001421567507700145615ustar00rootroot00000000000000destroy-1.2.0/.github/workflows/000077500000000000000000000000001421567507700166165ustar00rootroot00000000000000destroy-1.2.0/.github/workflows/ci.yml000066400000000000000000000130161421567507700177350ustar00rootroot00000000000000name: ci on: - pull_request - push jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: name: - Node.js 0.8 - Node.js 0.10 - Node.js 0.12 - io.js 1.x - io.js 2.x - io.js 3.x - Node.js 4.x - Node.js 5.x - Node.js 6.x - Node.js 7.x - Node.js 8.x - Node.js 9.x - Node.js 10.x - Node.js 11.x - Node.js 12.x - Node.js 13.x - Node.js 14.x - Node.js 15.x - Node.js 16.x - Node.js 17.x include: - name: Node.js 0.8 node-version: "0.8" npm-i: mocha@2.5.3 npm-rm: nyc - name: Node.js 0.10 node-version: "0.10" npm-i: mocha@3.5.3 nyc@10.3.2 - name: Node.js 0.12 node-version: "0.12" npm-i: mocha@3.5.3 nyc@10.3.2 - name: io.js 1.x node-version: "1.8" npm-i: mocha@3.5.3 nyc@10.3.2 - name: io.js 2.x node-version: "2.5" npm-i: mocha@3.5.3 nyc@10.3.2 - name: io.js 3.x node-version: "3.3" npm-i: mocha@3.5.3 nyc@10.3.2 - name: Node.js 4.x node-version: "4.9" npm-i: mocha@5.2.0 nyc@11.9.0 - name: Node.js 5.x node-version: "5.12" npm-i: mocha@5.2.0 nyc@11.9.0 - name: Node.js 6.x node-version: "6.17" npm-i: mocha@6.2.3 nyc@14.1.1 - name: Node.js 7.x node-version: "7.10" npm-i: mocha@6.2.3 nyc@14.1.1 - name: Node.js 8.x node-version: "8.17" npm-i: mocha@7.2.0 - name: Node.js 9.x node-version: "9.11" npm-i: mocha@7.2.0 - name: Node.js 10.x node-version: "10.24" npm-i: mocha@8.4.0 - name: Node.js 11.x node-version: "11.15" npm-i: mocha@8.4.0 - name: Node.js 12.x node-version: "12.22" - name: Node.js 13.x node-version: "13.14" - name: Node.js 14.x node-version: "14.19" - name: Node.js 15.x node-version: "15.14" - name: Node.js 16.x node-version: "16.14" - name: Node.js 17.x node-version: "17.7" steps: - uses: actions/checkout@v2 - name: Install Node.js ${{ matrix.node-version }} shell: bash -eo pipefail -l {0} run: | nvm install --default ${{ matrix.node-version }} if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then nvm install --alias=npm 0.10 npm install -g npm@1.2.8000 NPM=$(which npm) nvm use ${{ matrix.node-version }} ln -fs "$NPM" "$(which npm)" sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")" npm config set strict-ssl false fi dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" - name: Configure npm run: npm config set shrinkwrap false - name: Remove npm module(s) ${{ matrix.npm-rm }} run: npm rm --silent --save-dev ${{ matrix.npm-rm }} if: matrix.npm-rm != '' - name: Install npm module(s) ${{ matrix.npm-i }} run: npm install --save-dev ${{ matrix.npm-i }} if: matrix.npm-i != '' - name: Setup Node.js version-specific dependencies shell: bash run: | # eslint for linting # - remove on Node.js < 10 if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ grep -E '^eslint(-|$)' | \ sort -r | \ xargs -n1 npm rm --silent --save-dev fi - name: Install Node.js dependencies run: npm install - name: List environment id: list_env shell: bash run: | echo "node@$(node -v)" echo "npm@$(npm -v)" npm -s ls ||: (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print "::set-output name=" $2 "::" $3 }' - name: Run tests shell: bash run: | if npm -ps ls nyc | grep -q nyc; then npm run test-ci cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov" else npm test fi - name: Lint code if: steps.list_env.outputs.eslint != '' run: npm run lint - name: Collect code coverage if: steps.list_env.outputs.nyc != '' run: | if [[ -d ./coverage ]]; then mv ./coverage "./${{ matrix.name }}" mkdir ./coverage mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}" fi - name: Upload code coverage uses: actions/upload-artifact@v2 if: steps.list_env.outputs.nyc != '' with: name: coverage path: ./coverage retention-days: 1 coverage: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install lcov shell: bash run: sudo apt-get -y install lcov - name: Collect coverage reports uses: actions/download-artifact@v2 with: name: coverage path: ./coverage - name: Merge coverage reports shell: bash run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./coverage/lcov.info - name: Upload coverage report uses: coverallsapp/github-action@master with: github-token: ${{ secrets.GITHUB_TOKEN }} destroy-1.2.0/.gitignore000066400000000000000000000001141421567507700152050ustar00rootroot00000000000000 .DS_Store* *.log *.gz .nyc_output node_modules coverage package-lock.json destroy-1.2.0/HISTORY.md000066400000000000000000000012201421567507700146770ustar00rootroot000000000000001.2.0 / 2022-03-20 ================== * Add `suppress` argument 1.1.1 / 2022-02-28 ================== * Work around Zlib close bug in Node.js < 4.5.5 1.1.0 / 2022-01-25 ================== * Add Zlib steam support and Node.js leak work around 1.0.4 / 2016-01-15 ================== * perf: enable strict mode 1.0.3 / 2014-08-14 ================== * Rename from `dethroy` to `destroy` 1.0.2 / 2014-08-14 ================== * Work around `fd` leak in Node.js 0.10 for `fs.ReadStream` 1.0.1 / 2014-06-10 ================== * Don't call `.close` on object without `.destroy` 1.0.0 / 2013-12-30 ================== * Initial release destroy-1.2.0/LICENSE000066400000000000000000000022251421567507700142270ustar00rootroot00000000000000 The MIT License (MIT) Copyright (c) 2014 Jonathan Ong me@jongleberry.com Copyright (c) 2015-2022 Douglas Christopher Wilson doug@somethingdoug.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. destroy-1.2.0/README.md000066400000000000000000000046331421567507700145060ustar00rootroot00000000000000# destroy [![NPM version][npm-image]][npm-url] [![Build Status][github-actions-ci-image]][github-actions-ci-url] [![Test coverage][coveralls-image]][coveralls-url] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url] Destroy a stream. This module is meant to ensure a stream gets destroyed, handling different APIs and Node.js bugs. ## API ```js var destroy = require('destroy') ``` ### destroy(stream [, suppress]) Destroy the given stream, and optionally suppress any future `error` events. In most cases, this is identical to a simple `stream.destroy()` call. The rules are as follows for a given stream: 1. If the `stream` is an instance of `ReadStream`, then call `stream.destroy()` and add a listener to the `open` event to call `stream.close()` if it is fired. This is for a Node.js bug that will leak a file descriptor if `.destroy()` is called before `open`. 2. If the `stream` is an instance of a zlib stream, then call `stream.destroy()` and close the underlying zlib handle if open, otherwise call `stream.close()`. This is for consistency across Node.js versions and a Node.js bug that will leak a native zlib handle. 3. If the `stream` is not an instance of `Stream`, then nothing happens. 4. If the `stream` has a `.destroy()` method, then call it. The function returns the `stream` passed in as the argument. ## Example ```js var destroy = require('destroy') var fs = require('fs') var stream = fs.createReadStream('package.json') // ... and later destroy(stream) ``` [npm-image]: https://img.shields.io/npm/v/destroy.svg?style=flat-square [npm-url]: https://npmjs.org/package/destroy [github-tag]: http://img.shields.io/github/tag/stream-utils/destroy.svg?style=flat-square [github-url]: https://github.com/stream-utils/destroy/tags [coveralls-image]: https://img.shields.io/coveralls/stream-utils/destroy.svg?style=flat-square [coveralls-url]: https://coveralls.io/r/stream-utils/destroy?branch=master [license-image]: http://img.shields.io/npm/l/destroy.svg?style=flat-square [license-url]: LICENSE.md [downloads-image]: http://img.shields.io/npm/dm/destroy.svg?style=flat-square [downloads-url]: https://npmjs.org/package/destroy [github-actions-ci-image]: https://img.shields.io/github/workflow/status/stream-utils/destroy/ci/master?label=ci&style=flat-square [github-actions-ci-url]: https://github.com/stream-utils/destroy/actions/workflows/ci.yml destroy-1.2.0/index.js000066400000000000000000000102421421567507700146650ustar00rootroot00000000000000/*! * 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() } } destroy-1.2.0/package.json000066400000000000000000000021501421567507700155050ustar00rootroot00000000000000{ "name": "destroy", "description": "destroy a stream if possible", "version": "1.2.0", "author": { "name": "Jonathan Ong", "email": "me@jongleberry.com", "url": "http://jongleberry.com", "twitter": "https://twitter.com/jongleberry" }, "contributors": [ "Douglas Christopher Wilson " ], "license": "MIT", "repository": "stream-utils/destroy", "devDependencies": { "eslint": "7.32.0", "eslint-config-standard": "14.1.1", "eslint-plugin-import": "2.25.4", "eslint-plugin-node": "11.1.0", "eslint-plugin-promise": "5.2.0", "eslint-plugin-standard": "4.1.0", "mocha": "9.2.2", "nyc": "15.1.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" }, "scripts": { "lint": "eslint .", "test": "mocha --reporter spec", "test-ci": "nyc --reporter=lcovonly --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test" }, "files": [ "index.js", "LICENSE" ], "keywords": [ "stream", "streams", "destroy", "cleanup", "leak", "fd" ] } destroy-1.2.0/test/000077500000000000000000000000001421567507700142005ustar00rootroot00000000000000destroy-1.2.0/test/.eslintrc.yml000066400000000000000000000000231421567507700166170ustar00rootroot00000000000000env: mocha: true destroy-1.2.0/test/test.js000066400000000000000000000076051421567507700155250ustar00rootroot00000000000000 var assert = require('assert') var fs = require('fs') var net = require('net') var zlib = require('zlib') var destroy = require('..') describe('destroy(stream)', function () { it('should destroy a stream', function () { var stream = fs.createReadStream('package.json') assert(!isdestroyed(stream)) destroy(stream) assert(isdestroyed(stream)) }) it('should handle falsey values', function () { destroy() }) it('should handle random object', function () { destroy({}) }) describe('ReadStream', function () { it('should not leak fd when called sync to open', function (done) { // this test will timeout on a fd leak var _close = fs.close var _open = fs.open var waitclose = false function cleanup () { fs.close = _close fs.open = _open } fs.close = function close () { _close.apply(this, arguments) cleanup() done() } fs.open = function open () { waitclose = true _open.apply(this, arguments) } var stream = fs.createReadStream('package.json') destroy(stream) assert(isdestroyed(stream)) if (waitclose) { return } cleanup() done() }) }) describe('Socket', function () { it('should destroy a socket', function (done) { var server = net.createServer(function (connection) { socket.on('close', function () { server.close(done) }) destroy(connection) }) var socket server.listen(0, function () { socket = net.connect(this.address().port) }) }) }) ;['Gunzip', 'Inflate', 'InflateRaw', 'Unzip'].forEach(function (type) { var method = 'create' + type describe('Zlib.' + type, function () { it('should destroy a zlib stream', function () { var stream = zlib[method]() assert(!isdestroyed(stream)) destroy(stream) assert(isdestroyed(stream)) }) it('should destroy a zlib stream after write', function () { var stream = zlib[method]() assert(!isdestroyed(stream)) stream.write('') destroy(stream) assert(isdestroyed(stream)) }) it('should destroy a zlib stream after error', function (done) { var stream = zlib[method]() assert(!isdestroyed(stream)) stream.on('error', function () { destroy(stream) assert(isdestroyed(stream)) done() }) stream.write('foobar_invalid') }) }) }) ;['Gzip', 'Deflate', 'DeflateRaw'].forEach(function (type) { var method = 'create' + type describe('Zlib.' + type, function () { it('should destroy a zlib stream', function () { var stream = zlib[method]() assert(!isdestroyed(stream)) destroy(stream) assert(isdestroyed(stream)) }) it('should destroy a zlib stream after write', function () { var stream = zlib[method]() assert(!isdestroyed(stream)) stream.write('') destroy(stream) assert(isdestroyed(stream)) }) }) }) }) describe('destroy(stream, suppress)', function () { it('should destroy a stream', function () { var stream = fs.createReadStream('package.json') assert(!isdestroyed(stream)) destroy(stream, true) assert(isdestroyed(stream)) }) it('should handle falsey values', function () { destroy(0, true) }) it('should handle random object', function () { destroy({}, true) }) it('should suppress errors after destroy', function () { var stream = fs.createReadStream('package2.json') assert(!isdestroyed(stream)) destroy(stream, true) assert(isdestroyed(stream)) }) }) function isdestroyed (stream) { // readable for 0.8, destroyed for 0.10+, _closed for zlib < 8 return stream.readable === false || stream.destroyed === true || stream._closed === true }