pax_global_header00006660000000000000000000000064142026154560014517gustar00rootroot0000000000000052 comment=6a2ec37ebfde72c31e4ab44dace6af2153d0d55d raw-body-2.4.3/000077500000000000000000000000001420261545600132515ustar00rootroot00000000000000raw-body-2.4.3/.eslintignore000066400000000000000000000000261420261545600157520ustar00rootroot00000000000000coverage node_modules raw-body-2.4.3/.eslintrc.yml000066400000000000000000000002321420261545600156720ustar00rootroot00000000000000root: true extends: - standard - plugin:markdown/recommended plugins: - markdown overrides: - files: '**/*.md' processor: 'markdown/markdown' raw-body-2.4.3/.github/000077500000000000000000000000001420261545600146115ustar00rootroot00000000000000raw-body-2.4.3/.github/workflows/000077500000000000000000000000001420261545600166465ustar00rootroot00000000000000raw-body-2.4.3/.github/workflows/ci.yml000066400000000000000000000113471420261545600177720ustar00rootroot00000000000000name: ci on: - pull_request - push jobs: test: runs-on: ubuntu-latest strategy: 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@2.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.2 nyc@14.1.1 - name: Node.js 7.x node-version: "7.10" npm-i: mocha@6.2.2 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.5" 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 nvm use ${{ matrix.node-version }} 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 else npm test fi - name: Lint code if: steps.list_env.outputs.eslint != '' run: npm run lint - name: Collect code coverage uses: coverallsapp/github-action@master if: steps.list_env.outputs.nyc != '' with: github-token: ${{ secrets.GITHUB_TOKEN }} flag-name: run-${{ matrix.test_number }} parallel: true coverage: needs: test runs-on: ubuntu-latest steps: - name: Uploade code coverage uses: coverallsapp/github-action@master with: github-token: ${{ secrets.github_token }} parallel-finished: true raw-body-2.4.3/.gitignore000066400000000000000000000001101420261545600152310ustar00rootroot00000000000000*.tgz .nyc_output coverage node_modules npm-debug.log package-lock.json raw-body-2.4.3/HISTORY.md000066400000000000000000000130411420261545600147330ustar00rootroot000000000000002.4.3 / 2022-02-14 ================== * deps: bytes@3.1.2 2.4.2 / 2021-11-16 ================== * deps: bytes@3.1.1 * deps: http-errors@1.8.1 - deps: setprototypeof@1.2.0 - deps: toidentifier@1.0.1 2.4.1 / 2019-06-25 ================== * deps: http-errors@1.7.3 - deps: inherits@2.0.4 2.4.0 / 2019-04-17 ================== * deps: bytes@3.1.0 - Add petabyte (`pb`) support * deps: http-errors@1.7.2 - Set constructor name when possible - deps: setprototypeof@1.1.1 - deps: statuses@'>= 1.5.0 < 2' * deps: iconv-lite@0.4.24 - Added encoding MIK 2.3.3 / 2018-05-08 ================== * deps: http-errors@1.6.3 - deps: depd@~1.1.2 - deps: setprototypeof@1.1.0 - deps: statuses@'>= 1.3.1 < 2' * deps: iconv-lite@0.4.23 - Fix loading encoding with year appended - Fix deprecation warnings on Node.js 10+ 2.3.2 / 2017-09-09 ================== * deps: iconv-lite@0.4.19 - Fix ISO-8859-1 regression - Update Windows-1255 2.3.1 / 2017-09-07 ================== * deps: bytes@3.0.0 * deps: http-errors@1.6.2 - deps: depd@1.1.1 * perf: skip buffer decoding on overage chunk 2.3.0 / 2017-08-04 ================== * Add TypeScript definitions * Use `http-errors` for standard emitted errors * deps: bytes@2.5.0 * deps: iconv-lite@0.4.18 - Add support for React Native - Add a warning if not loaded as utf-8 - Fix CESU-8 decoding in Node.js 8 - Improve speed of ISO-8859-1 encoding 2.2.0 / 2017-01-02 ================== * deps: iconv-lite@0.4.15 - Added encoding MS-31J - Added encoding MS-932 - Added encoding MS-936 - Added encoding MS-949 - Added encoding MS-950 - Fix GBK/GB18030 handling of Euro character 2.1.7 / 2016-06-19 ================== * deps: bytes@2.4.0 * perf: remove double-cleanup on happy path 2.1.6 / 2016-03-07 ================== * deps: bytes@2.3.0 - Drop partial bytes on all parsed units - Fix parsing byte string that looks like hex 2.1.5 / 2015-11-30 ================== * deps: bytes@2.2.0 * deps: iconv-lite@0.4.13 2.1.4 / 2015-09-27 ================== * Fix masking critical errors from `iconv-lite` * deps: iconv-lite@0.4.12 - Fix CESU-8 decoding in Node.js 4.x 2.1.3 / 2015-09-12 ================== * Fix sync callback when attaching data listener causes sync read - Node.js 0.10 compatibility issue 2.1.2 / 2015-07-05 ================== * Fix error stack traces to skip `makeError` * deps: iconv-lite@0.4.11 - Add encoding CESU-8 2.1.1 / 2015-06-14 ================== * Use `unpipe` module for unpiping requests 2.1.0 / 2015-05-28 ================== * deps: iconv-lite@0.4.10 - Improved UTF-16 endianness detection - Leading BOM is now removed when decoding - The encoding UTF-16 without BOM now defaults to UTF-16LE when detection fails 2.0.2 / 2015-05-21 ================== * deps: bytes@2.1.0 - Slight optimizations 2.0.1 / 2015-05-10 ================== * Fix a false-positive when unpiping in Node.js 0.8 2.0.0 / 2015-05-08 ================== * Return a promise without callback instead of thunk * deps: bytes@2.0.1 - units no longer case sensitive when parsing 1.3.4 / 2015-04-15 ================== * Fix hanging callback if request aborts during read * deps: iconv-lite@0.4.8 - Add encoding alias UNICODE-1-1-UTF-7 1.3.3 / 2015-02-08 ================== * deps: iconv-lite@0.4.7 - Gracefully support enumerables on `Object.prototype` 1.3.2 / 2015-01-20 ================== * deps: iconv-lite@0.4.6 - Fix rare aliases of single-byte encodings 1.3.1 / 2014-11-21 ================== * deps: iconv-lite@0.4.5 - Fix Windows-31J and X-SJIS encoding support 1.3.0 / 2014-07-20 ================== * Fully unpipe the stream on error - Fixes `Cannot switch to old mode now` error on Node.js 0.10+ 1.2.3 / 2014-07-20 ================== * deps: iconv-lite@0.4.4 - Added encoding UTF-7 1.2.2 / 2014-06-19 ================== * Send invalid encoding error to callback 1.2.1 / 2014-06-15 ================== * deps: iconv-lite@0.4.3 - Added encodings UTF-16BE and UTF-16 with BOM 1.2.0 / 2014-06-13 ================== * Passing string as `options` interpreted as encoding * Support all encodings from `iconv-lite` 1.1.7 / 2014-06-12 ================== * use `string_decoder` module from npm 1.1.6 / 2014-05-27 ================== * check encoding for old streams1 * support node.js < 0.10.6 1.1.5 / 2014-05-14 ================== * bump bytes 1.1.4 / 2014-04-19 ================== * allow true as an option * bump bytes 1.1.3 / 2014-03-02 ================== * fix case when length=null 1.1.2 / 2013-12-01 ================== * be less strict on state.encoding check 1.1.1 / 2013-11-27 ================== * add engines 1.1.0 / 2013-11-27 ================== * add err.statusCode and err.type * allow for encoding option to be true * pause the stream instead of dumping on error * throw if the stream's encoding is set 1.0.1 / 2013-11-19 ================== * dont support streams1, throw if dev set encoding 1.0.0 / 2013-11-17 ================== * rename `expected` option to `length` 0.2.0 / 2013-11-15 ================== * republish 0.1.1 / 2013-11-15 ================== * use bytes 0.1.0 / 2013-11-11 ================== * generator support 0.0.3 / 2013-10-10 ================== * update repo 0.0.2 / 2013-09-14 ================== * dump stream on bad headers * listen to events after defining received and buffers 0.0.1 / 2013-09-14 ================== * Initial release raw-body-2.4.3/LICENSE000066400000000000000000000022351420261545600142600ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013-2014 Jonathan Ong Copyright (c) 2014-2015 Douglas Christopher Wilson 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. raw-body-2.4.3/README.md000066400000000000000000000144561420261545600145420ustar00rootroot00000000000000# raw-body [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Node.js Version][node-version-image]][node-version-url] [![Build status][github-actions-ci-image]][github-actions-ci-url] [![Test coverage][coveralls-image]][coveralls-url] Gets the entire buffer of a stream either as a `Buffer` or a string. Validates the stream's length against an expected length and maximum limit. Ideal for parsing request bodies. ## Install This is a [Node.js](https://nodejs.org/en/) module available through the [npm registry](https://www.npmjs.com/). Installation is done using the [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): ```sh $ npm install raw-body ``` ### TypeScript This module includes a [TypeScript](https://www.typescriptlang.org/) declaration file to enable auto complete in compatible editors and type information for TypeScript projects. This module depends on the Node.js types, so install `@types/node`: ```sh $ npm install @types/node ``` ## API ```js var getRawBody = require('raw-body') ``` ### getRawBody(stream, [options], [callback]) **Returns a promise if no callback specified and global `Promise` exists.** Options: - `length` - The length of the stream. If the contents of the stream do not add up to this length, an `400` error code is returned. - `limit` - The byte limit of the body. This is the number of bytes or any string format supported by [bytes](https://www.npmjs.com/package/bytes), for example `1000`, `'500kb'` or `'3mb'`. If the body ends up being larger than this limit, a `413` error code is returned. - `encoding` - The encoding to use to decode the body into a string. By default, a `Buffer` instance will be returned when no encoding is specified. Most likely, you want `utf-8`, so setting `encoding` to `true` will decode as `utf-8`. You can use any type of encoding supported by [iconv-lite](https://www.npmjs.org/package/iconv-lite#readme). You can also pass a string in place of options to just specify the encoding. If an error occurs, the stream will be paused, everything unpiped, and you are responsible for correctly disposing the stream. For HTTP requests, you may need to finish consuming the stream if you want to keep the socket open for future requests. For streams that use file descriptors, you should `stream.destroy()` or `stream.close()` to prevent leaks. ## Errors This module creates errors depending on the error condition during reading. The error may be an error from the underlying Node.js implementation, but is otherwise an error created by this module, which has the following attributes: * `limit` - the limit in bytes * `length` and `expected` - the expected length of the stream * `received` - the received bytes * `encoding` - the invalid encoding * `status` and `statusCode` - the corresponding status code for the error * `type` - the error type ### Types The errors from this module have a `type` property which allows for the programmatic determination of the type of error returned. #### encoding.unsupported This error will occur when the `encoding` option is specified, but the value does not map to an encoding supported by the [iconv-lite](https://www.npmjs.org/package/iconv-lite#readme) module. #### entity.too.large This error will occur when the `limit` option is specified, but the stream has an entity that is larger. #### request.aborted This error will occur when the request stream is aborted by the client before reading the body has finished. #### request.size.invalid This error will occur when the `length` option is specified, but the stream has emitted more bytes. #### stream.encoding.set This error will occur when the given stream has an encoding set on it, making it a decoded stream. The stream should not have an encoding set and is expected to emit `Buffer` objects. ## Examples ### Simple Express example ```js var contentType = require('content-type') var express = require('express') var getRawBody = require('raw-body') var app = express() app.use(function (req, res, next) { getRawBody(req, { length: req.headers['content-length'], limit: '1mb', encoding: contentType.parse(req).parameters.charset }, function (err, string) { if (err) return next(err) req.text = string next() }) }) // now access req.text ``` ### Simple Koa example ```js var contentType = require('content-type') var getRawBody = require('raw-body') var koa = require('koa') var app = koa() app.use(function * (next) { this.text = yield getRawBody(this.req, { length: this.req.headers['content-length'], limit: '1mb', encoding: contentType.parse(this.req).parameters.charset }) yield next }) // now access this.text ``` ### Using as a promise To use this library as a promise, simply omit the `callback` and a promise is returned, provided that a global `Promise` is defined. ```js var getRawBody = require('raw-body') var http = require('http') var server = http.createServer(function (req, res) { getRawBody(req) .then(function (buf) { res.statusCode = 200 res.end(buf.length + ' bytes submitted') }) .catch(function (err) { res.statusCode = 500 res.end(err.message) }) }) server.listen(3000) ``` ### Using with TypeScript ```ts import * as getRawBody from 'raw-body'; import * as http from 'http'; const server = http.createServer((req, res) => { getRawBody(req) .then((buf) => { res.statusCode = 200; res.end(buf.length + ' bytes submitted'); }) .catch((err) => { res.statusCode = err.statusCode; res.end(err.message); }); }); server.listen(3000); ``` ## License [MIT](LICENSE) [npm-image]: https://img.shields.io/npm/v/raw-body.svg [npm-url]: https://npmjs.org/package/raw-body [node-version-image]: https://img.shields.io/node/v/raw-body.svg [node-version-url]: https://nodejs.org/en/download/ [coveralls-image]: https://img.shields.io/coveralls/stream-utils/raw-body/master.svg [coveralls-url]: https://coveralls.io/r/stream-utils/raw-body?branch=master [downloads-image]: https://img.shields.io/npm/dm/raw-body.svg [downloads-url]: https://npmjs.org/package/raw-body [github-actions-ci-image]: https://img.shields.io/github/workflow/status/stream-utils/raw-body/ci/master?label=ci [github-actions-ci-url]: https://github.com/jshttp/stream-utils/raw-body?query=workflow%3Aci raw-body-2.4.3/index.d.ts000066400000000000000000000043561420261545600151620ustar00rootroot00000000000000import { Readable } from 'stream'; declare namespace getRawBody { export type Encoding = string | true; export interface Options { /** * The expected length of the stream. */ length?: number | string | null; /** * The byte limit of the body. This is the number of bytes or any string * format supported by `bytes`, for example `1000`, `'500kb'` or `'3mb'`. */ limit?: number | string | null; /** * The encoding to use to decode the body into a string. By default, a * `Buffer` instance will be returned when no encoding is specified. Most * likely, you want `utf-8`, so setting encoding to `true` will decode as * `utf-8`. You can use any type of encoding supported by `iconv-lite`. */ encoding?: Encoding | null; } export interface RawBodyError extends Error { /** * The limit in bytes. */ limit?: number; /** * The expected length of the stream. */ length?: number; expected?: number; /** * The received bytes. */ received?: number; /** * The encoding. */ encoding?: string; /** * The corresponding status code for the error. */ status: number; statusCode: number; /** * The error type. */ type: string; } } /** * Gets the entire buffer of a stream either as a `Buffer` or a string. * Validates the stream's length against an expected length and maximum * limit. Ideal for parsing request bodies. */ declare function getRawBody( stream: Readable, callback: (err: getRawBody.RawBodyError, body: Buffer) => void ): void; declare function getRawBody( stream: Readable, options: (getRawBody.Options & { encoding: getRawBody.Encoding }) | getRawBody.Encoding, callback: (err: getRawBody.RawBodyError, body: string) => void ): void; declare function getRawBody( stream: Readable, options: getRawBody.Options, callback: (err: getRawBody.RawBodyError, body: Buffer) => void ): void; declare function getRawBody( stream: Readable, options: (getRawBody.Options & { encoding: getRawBody.Encoding }) | getRawBody.Encoding ): Promise; declare function getRawBody( stream: Readable, options?: getRawBody.Options ): Promise; export = getRawBody; raw-body-2.4.3/index.js000066400000000000000000000136531420261545600147260ustar00rootroot00000000000000/*! * raw-body * Copyright(c) 2013-2014 Jonathan Ong * Copyright(c) 2014-2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. * @private */ var bytes = require('bytes') var createError = require('http-errors') var iconv = require('iconv-lite') var unpipe = require('unpipe') /** * Module exports. * @public */ module.exports = getRawBody /** * Module variables. * @private */ var ICONV_ENCODING_MESSAGE_REGEXP = /^Encoding not recognized: / /** * Get the decoder for a given encoding. * * @param {string} encoding * @private */ function getDecoder (encoding) { if (!encoding) return null try { return iconv.getDecoder(encoding) } catch (e) { // error getting decoder if (!ICONV_ENCODING_MESSAGE_REGEXP.test(e.message)) throw e // the encoding was not found throw createError(415, 'specified encoding unsupported', { encoding: encoding, type: 'encoding.unsupported' }) } } /** * Get the raw body of a stream (typically HTTP). * * @param {object} stream * @param {object|string|function} [options] * @param {function} [callback] * @public */ function getRawBody (stream, options, callback) { var done = callback var opts = options || {} if (options === true || typeof options === 'string') { // short cut for encoding opts = { encoding: options } } if (typeof options === 'function') { done = options opts = {} } // validate callback is a function, if provided if (done !== undefined && typeof done !== 'function') { throw new TypeError('argument callback must be a function') } // require the callback without promises if (!done && !global.Promise) { throw new TypeError('argument callback is required') } // get encoding var encoding = opts.encoding !== true ? opts.encoding : 'utf-8' // convert the limit to an integer var limit = bytes.parse(opts.limit) // convert the expected length to an integer var length = opts.length != null && !isNaN(opts.length) ? parseInt(opts.length, 10) : null if (done) { // classic callback style return readStream(stream, encoding, length, limit, done) } return new Promise(function executor (resolve, reject) { readStream(stream, encoding, length, limit, function onRead (err, buf) { if (err) return reject(err) resolve(buf) }) }) } /** * Halt a stream. * * @param {Object} stream * @private */ function halt (stream) { // unpipe everything from the stream unpipe(stream) // pause stream if (typeof stream.pause === 'function') { stream.pause() } } /** * Read the data from the stream. * * @param {object} stream * @param {string} encoding * @param {number} length * @param {number} limit * @param {function} callback * @public */ function readStream (stream, encoding, length, limit, callback) { var complete = false var sync = true // check the length and limit options. // note: we intentionally leave the stream paused, // so users should handle the stream themselves. if (limit !== null && length !== null && length > limit) { return done(createError(413, 'request entity too large', { expected: length, length: length, limit: limit, type: 'entity.too.large' })) } // streams1: assert request encoding is buffer. // streams2+: assert the stream encoding is buffer. // stream._decoder: streams1 // state.encoding: streams2 // state.decoder: streams2, specifically < 0.10.6 var state = stream._readableState if (stream._decoder || (state && (state.encoding || state.decoder))) { // developer error return done(createError(500, 'stream encoding should not be set', { type: 'stream.encoding.set' })) } var received = 0 var decoder try { decoder = getDecoder(encoding) } catch (err) { return done(err) } var buffer = decoder ? '' : [] // attach listeners stream.on('aborted', onAborted) stream.on('close', cleanup) stream.on('data', onData) stream.on('end', onEnd) stream.on('error', onEnd) // mark sync section complete sync = false function done () { var args = new Array(arguments.length) // copy arguments for (var i = 0; i < args.length; i++) { args[i] = arguments[i] } // mark complete complete = true if (sync) { process.nextTick(invokeCallback) } else { invokeCallback() } function invokeCallback () { cleanup() if (args[0]) { // halt the stream on error halt(stream) } callback.apply(null, args) } } function onAborted () { if (complete) return done(createError(400, 'request aborted', { code: 'ECONNABORTED', expected: length, length: length, received: received, type: 'request.aborted' })) } function onData (chunk) { if (complete) return received += chunk.length if (limit !== null && received > limit) { done(createError(413, 'request entity too large', { limit: limit, received: received, type: 'entity.too.large' })) } else if (decoder) { buffer += decoder.write(chunk) } else { buffer.push(chunk) } } function onEnd (err) { if (complete) return if (err) return done(err) if (length !== null && received !== length) { done(createError(400, 'request size did not match content length', { expected: length, length: length, received: received, type: 'request.size.invalid' })) } else { var string = decoder ? buffer + (decoder.end() || '') : Buffer.concat(buffer) done(null, string) } } function cleanup () { buffer = null stream.removeListener('aborted', onAborted) stream.removeListener('data', onData) stream.removeListener('end', onEnd) stream.removeListener('error', onEnd) stream.removeListener('close', cleanup) } } raw-body-2.4.3/package.json000066400000000000000000000024251420261545600155420ustar00rootroot00000000000000{ "name": "raw-body", "description": "Get and validate the raw body of a readable stream.", "version": "2.4.3", "author": "Jonathan Ong (http://jongleberry.com)", "contributors": [ "Douglas Christopher Wilson ", "Raynos " ], "license": "MIT", "repository": "stream-utils/raw-body", "dependencies": { "bytes": "3.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "devDependencies": { "bluebird": "3.7.2", "eslint": "7.32.0", "eslint-config-standard": "14.1.1", "eslint-plugin-import": "2.25.4", "eslint-plugin-markdown": "2.2.1", "eslint-plugin-node": "11.1.0", "eslint-plugin-promise": "5.2.0", "eslint-plugin-standard": "4.1.0", "mocha": "9.2.0", "nyc": "15.1.0", "readable-stream": "2.3.7", "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.8" }, "files": [ "HISTORY.md", "LICENSE", "README.md", "index.d.ts", "index.js" ], "scripts": { "lint": "eslint .", "test": "mocha --trace-deprecation --reporter spec --bail --check-leaks test/", "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test" } } raw-body-2.4.3/test/000077500000000000000000000000001420261545600142305ustar00rootroot00000000000000raw-body-2.4.3/test/.eslintrc.yml000066400000000000000000000000231420261545600166470ustar00rootroot00000000000000env: mocha: true raw-body-2.4.3/test/flowing.js000066400000000000000000000104741420261545600162410ustar00rootroot00000000000000var assert = require('assert') var Readable = require('readable-stream').Readable var Writable = require('readable-stream').Writable var getRawBody = require('../') var defaultLimit = 1024 * 1024 // Add Promise to mocha's global list // eslint-disable-next-line no-self-assign global.Promise = global.Promise describe('stream flowing', function () { describe('when limit lower then length', function (done) { it('should stop the steam flow', function (done) { var stream = createInfiniteStream() getRawBody(stream, { limit: defaultLimit, length: defaultLimit * 2 }, function (err, body) { assert.ok(err) assert.strictEqual(err.type, 'entity.too.large') assert.strictEqual(err.message, 'request entity too large') assert.strictEqual(err.statusCode, 413) assert.strictEqual(err.length, defaultLimit * 2) assert.strictEqual(err.limit, defaultLimit) assert.strictEqual(body, undefined) assert.ok(stream.isPaused) done() }) }) it('should halt flowing stream', function (done) { var stream = createInfiniteStream(true) var dest = createBlackholeStream() // pipe the stream stream.pipe(dest) getRawBody(stream, { limit: defaultLimit * 2, length: defaultLimit }, function (err, body) { assert.ok(err) assert.strictEqual(err.type, 'entity.too.large') assert.strictEqual(err.message, 'request entity too large') assert.strictEqual(err.statusCode, 413) assert.strictEqual(body, undefined) assert.ok(stream.isPaused) done() }) }) }) describe('when stream has encoding set', function (done) { it('should stop the steam flow', function (done) { var stream = createInfiniteStream() stream.setEncoding('utf8') getRawBody(stream, { limit: defaultLimit }, function (err, body) { assert.ok(err) assert.strictEqual(err.type, 'stream.encoding.set') assert.strictEqual(err.message, 'stream encoding should not be set') assert.strictEqual(err.statusCode, 500) assert.ok(stream.isPaused) done() }) }) }) describe('when stream has limit', function (done) { it('should stop the steam flow', function (done) { var stream = createInfiniteStream() getRawBody(stream, { limit: defaultLimit }, function (err, body) { assert.ok(err) assert.strictEqual(err.type, 'entity.too.large') assert.strictEqual(err.statusCode, 413) assert.ok(err.received > defaultLimit) assert.strictEqual(err.limit, defaultLimit) assert.ok(stream.isPaused) done() }) }) }) describe('when stream has limit', function (done) { it('should stop the steam flow', function (done) { var stream = createInfiniteStream() getRawBody(stream, function (err, body) { assert.ok(err) assert.strictEqual(err.message, 'BOOM') assert.ok(stream.isPaused) done() }) setTimeout(function () { stream.emit('error', new Error('BOOM')) }, 500) }) }) }) function createChunk () { var base = Math.random().toString(32) var KB_4 = 32 * 4 var KB_8 = KB_4 * 2 var KB_16 = KB_8 * 2 var KB_64 = KB_16 * 4 var rand = Math.random() if (rand < 0.25) { return repeat(base, KB_4) } else if (rand < 0.5) { return repeat(base, KB_8) } else if (rand < 0.75) { return repeat(base, KB_16) } else { return repeat(base, KB_64) } function repeat (str, num) { return new Array(num + 1).join(str) } } function createBlackholeStream () { var stream = new Writable() stream._write = function (chunk, encoding, cb) { cb() } return stream } function createInfiniteStream (paused) { var stream = new Readable() stream._read = function () { var rand = 2 + Math.floor(Math.random() * 10) setTimeout(function () { for (var i = 0; i < rand; i++) { stream.push(createChunk()) } }, 100) } // track paused state for tests stream.isPaused = false stream.on('pause', function () { this.isPaused = true }) stream.on('resume', function () { this.isPaused = false }) // immediately put the stream in flowing mode if (!paused) { stream.resume() } return stream } raw-body-2.4.3/test/http.js000066400000000000000000000056021420261545600155500ustar00rootroot00000000000000var assert = require('assert') var getRawBody = require('..') var http = require('http') var net = require('net') describe('using http streams', function () { it('should read body streams', function (done) { var server = http.createServer(function onRequest (req, res) { getRawBody(req, { length: req.headers['content-length'] }, function (err, body) { if (err) { req.resume() res.statusCode = 500 return res.end(err.message) } res.end(body) }) }) server.listen(function onListen () { var addr = server.address() var client = http.request({ method: 'POST', port: addr.port }) client.end('hello, world!') client.on('response', function onResponse (res) { getRawBody(res, { encoding: true }, function (err, str) { server.close(function onClose () { assert.ifError(err) assert.strictEqual(str, 'hello, world!') done() }) }) }) }) }) it('should throw if stream encoding is set', function (done) { var server = http.createServer(function onRequest (req, res) { req.setEncoding('utf8') getRawBody(req, { length: req.headers['content-length'] }, function (err, body) { if (err) { req.resume() res.statusCode = 500 return res.end(err.message) } res.end(body) }) }) server.listen(function onListen () { var addr = server.address() var client = http.request({ method: 'POST', port: addr.port }) client.end('hello, world!') client.on('response', function onResponse (res) { getRawBody(res, { encoding: true }, function (err, str) { server.close(function onClose () { assert.ifError(err) assert.strictEqual(str, 'stream encoding should not be set') done() }) }) }) }) }) it('should throw if connection ends', function (done) { var socket var server = http.createServer(function onRequest (req, res) { getRawBody(req, { length: req.headers['content-length'] }, function (err, body) { server.close() assert.ok(err) assert.strictEqual(err.code, 'ECONNABORTED') assert.strictEqual(err.expected, 50) assert.strictEqual(err.message, 'request aborted') assert.strictEqual(err.received, 10) assert.strictEqual(err.status, 400) assert.strictEqual(err.type, 'request.aborted') done() }) setTimeout(socket.destroy.bind(socket), 10) }) server.listen(function onListen () { socket = net.connect(server.address().port, function () { socket.write('POST / HTTP/1.0\r\n') socket.write('Connection: keep-alive\r\n') socket.write('Content-Length: 50\r\n') socket.write('\r\n') socket.write('testing...') }) }) }) }) raw-body-2.4.3/test/http2.js000066400000000000000000000072461420261545600156400ustar00rootroot00000000000000var assert = require('assert') var getRawBody = require('..') var http2 = tryRequire('http2') var net = require('net') var describeHttp2 = !http2 ? describe.skip : describe describeHttp2('using http2 streams', function () { it('should read body streams', function (done) { var server = http2.createServer(function onRequest (req, res) { getRawBody(req, { length: req.headers['content-length'] }, function (err, body) { if (err) { req.resume() res.statusCode = 500 return res.end(err.message) } res.end(body) }) }) server.listen(function onListen () { var addr = server.address() var session = http2.connect('http://localhost:' + addr.port) var request = session.request({ ':method': 'POST', ':path': '/' }) request.end('hello, world!') request.on('response', function onResponse (headers) { getRawBody(request, { encoding: true }, function (err, str) { http2close(server, session, function onClose () { assert.ifError(err) assert.strictEqual(str, 'hello, world!') done() }) }) }) }) }) it('should throw if stream encoding is set', function (done) { var server = http2.createServer(function onRequest (req, res) { req.setEncoding('utf8') getRawBody(req, { length: req.headers['content-length'] }, function (err, body) { if (err) { req.resume() res.statusCode = 500 return res.end(err.message) } res.end(body) }) }) server.listen(function onListen () { var addr = server.address() var session = http2.connect('http://localhost:' + addr.port) var request = session.request({ ':method': 'POST', ':path': '/' }) request.end('hello, world!') request.on('response', function onResponse (res) { getRawBody(request, { encoding: true }, function (err, str) { http2close(server, session, function onClose () { assert.ifError(err) assert.strictEqual(str, 'stream encoding should not be set') done() }) }) }) }) }) it('should throw if connection ends', function (done) { var socket var server = http2.createServer(function onRequest (req, res) { getRawBody(req, { length: req.headers['content-length'] }, function (err, body) { server.close() assert.ok(err) assert.strictEqual(err.code, 'ECONNABORTED') assert.strictEqual(err.expected, 50) assert.strictEqual(err.message, 'request aborted') assert.strictEqual(err.received, 10) assert.strictEqual(err.status, 400) assert.strictEqual(err.type, 'request.aborted') done() }) setTimeout(socket.destroy.bind(socket), 10) }) server.listen(function onListen () { var addr = server.address() var session = http2.connect('http://localhost:' + addr.port, { createConnection: function (authority) { return (socket = net.connect(authority.port, authority.hostname)) } }) var request = session.request({ ':method': 'POST', ':path': '/', 'content-length': '50' }) request.write('testing...') }) }) }) function http2close (server, session, callback) { if (typeof session.close === 'function') { session.close(onSessionClose) } else { session.shutdown(onSessionClose) } function onServerClose () { callback() } function onSessionClose () { server.close(onServerClose) } } function tryRequire (module) { try { return require(module) } catch (e) { return undefined } } raw-body-2.4.3/test/index.js000066400000000000000000000246371420261545600157110ustar00rootroot00000000000000var assert = require('assert') var fs = require('fs') var getRawBody = require('..') var path = require('path') var Buffer = require('safe-buffer').Buffer var EventEmitter = require('events').EventEmitter var Promise = global.Promise || require('bluebird') var Readable = require('readable-stream').Readable var file = path.join(__dirname, 'index.js') var length = fs.statSync(file).size var string = fs.readFileSync(file, 'utf8') // Add Promise to mocha's global list // eslint-disable-next-line no-self-assign global.Promise = global.Promise describe('Raw Body', function () { it('should work without any options', function (done) { getRawBody(createStream(), function (err, buf) { assert.ifError(err) checkBuffer(buf) done() }) }) it('should work with `true` as an option', function (done) { getRawBody(createStream(), true, function (err, buf) { assert.ifError(err) assert.strictEqual(typeof buf, 'string') done() }) }) it('should error for bad callback', function () { assert.throws(function () { getRawBody(createStream(), true, 'silly') }, /argument callback.*function/) }) it('should work with length', function (done) { getRawBody(createStream(), { length: length }, function (err, buf) { assert.ifError(err) checkBuffer(buf) done() }) }) it('should work when length=0', function (done) { var stream = new EventEmitter() getRawBody(stream, { length: 0, encoding: true }, function (err, str) { assert.ifError(err) assert.strictEqual(str, '') done() }) process.nextTick(function () { stream.emit('end') }) }) it('should work with limit', function (done) { getRawBody(createStream(), { limit: length + 1 }, function (err, buf) { assert.ifError(err) checkBuffer(buf) done() }) }) it('should work with limit as a string', function (done) { getRawBody(createStream(), { limit: '1gb' }, function (err, buf) { assert.ifError(err) checkBuffer(buf) done() }) }) it('should work with limit and length', function (done) { getRawBody(createStream(), { length: length, limit: length + 1 }, function (err, buf) { assert.ifError(err) checkBuffer(buf) done() }) }) it('should check options for limit and length', function (done) { getRawBody(createStream(), { length: length, limit: length - 1 }, function (err, buf) { assert.strictEqual(err.status, 413) assert.strictEqual(err.statusCode, 413) assert.strictEqual(err.expected, length) assert.strictEqual(err.length, length) assert.strictEqual(err.limit, length - 1) assert.strictEqual(err.type, 'entity.too.large') assert.strictEqual(err.message, 'request entity too large') done() }) }) it('should work with an empty stream', function (done) { var stream = new Readable() stream.push(null) getRawBody(stream, { length: 0, limit: 1 }, function (err, buf) { assert.ifError(err) assert.strictEqual(buf.length, 0) done() }) stream.emit('end') }) it('should throw on empty string and incorrect length', function (done) { var stream = new Readable() stream.push(null) getRawBody(stream, { length: 1, limit: 2 }, function (err, buf) { assert.strictEqual(err.status, 400) done() }) stream.emit('end') }) it('should throw if length > limit', function (done) { getRawBody(createStream(), { limit: length - 1 }, function (err, buf) { assert.strictEqual(err.status, 413) done() }) }) it('should throw if incorrect length supplied', function (done) { getRawBody(createStream(), { length: length - 1 }, function (err, buf) { assert.strictEqual(err.status, 400) done() }) }) it('should work with if length is null', function (done) { getRawBody(createStream(), { length: null, limit: length + 1 }, function (err, buf) { assert.ifError(err) checkBuffer(buf) done() }) }) it('should work with {"test":"å"}', function (done) { // https://github.com/visionmedia/express/issues/1816 var stream = new Readable() stream.push('{"test":"å"}') stream.push(null) getRawBody(stream, { length: 13 }, function (err, buf) { if (err) return done(err) assert.ok(buf) assert.strictEqual(buf.length, 13) done() }) }) it('should throw if stream encoding is set', function (done) { var stream = new Readable() stream.push('akl;sdjfklajsdfkljasdf') stream.push(null) stream.setEncoding('utf8') getRawBody(stream, function (err, buf) { assert.strictEqual(err.status, 500) done() }) }) it('should throw when given an invalid encoding', function (done) { var stream = new Readable() stream.push('akl;sdjfklajsdfkljasdf') stream.push(null) getRawBody(stream, 'akljsdflkajsdf', function (err) { assert.ok(err) assert.strictEqual(err.message, 'specified encoding unsupported') assert.strictEqual(err.status, 415) assert.strictEqual(err.type, 'encoding.unsupported') done() }) }) describe('with global Promise', function () { before(function () { global.Promise = Promise }) after(function () { global.Promise = undefined }) it('should work as a promise', function () { return getRawBody(createStream()) .then(checkBuffer) }) it('should work as a promise when length > limit', function () { return getRawBody(createStream(), { length: length, limit: length - 1 }).then(throwExpectedError, function (err) { assert.strictEqual(err.status, 413) }) }) }) describe('without global Promise', function () { before(function () { global.Promise = undefined }) after(function () { global.Promise = Promise }) it('should error without callback', function () { assert.throws(function () { getRawBody(createStream()) }, /argument callback.*required/) }) it('should work with callback as second argument', function (done) { getRawBody(createStream(), function (err, buf) { assert.ifError(err) checkBuffer(buf) done() }) }) it('should work with callback as third argument', function (done) { getRawBody(createStream(), true, function (err, str) { assert.ifError(err) checkString(str) done() }) }) }) describe('when an encoding is set', function () { it('should return a string', function (done) { getRawBody(createStream(), { encoding: 'utf-8' }, function (err, str) { assert.ifError(err) assert.strictEqual(str, string) done() }) }) it('should handle encoding true as utf-8', function (done) { getRawBody(createStream(), { encoding: true }, function (err, str) { assert.ifError(err) assert.strictEqual(str, string) done() }) }) it('should handle encoding as options string', function (done) { getRawBody(createStream(), 'utf-8', function (err, str) { assert.ifError(err) assert.strictEqual(str, string) done() }) }) it('should decode codepage string', function (done) { var stream = createStream(Buffer.from('bf43f36d6f20657374e1733f', 'hex')) var string = '¿Cómo estás?' getRawBody(stream, 'iso-8859-1', function (err, str) { assert.ifError(err) assert.strictEqual(str, string) done() }) }) it('should decode UTF-8 string', function (done) { var stream = createStream(Buffer.from('c2bf43c3b36d6f20657374c3a1733f', 'hex')) var string = '¿Cómo estás?' getRawBody(stream, 'utf-8', function (err, str) { assert.ifError(err) assert.strictEqual(str, string) done() }) }) it('should decode UTF-16 string (LE BOM)', function (done) { // BOM makes this LE var stream = createStream(Buffer.from('fffebf004300f3006d006f002000650073007400e10073003f00', 'hex')) var string = '¿Cómo estás?' getRawBody(stream, 'utf-16', function (err, str) { assert.ifError(err) assert.strictEqual(str, string) done() }) }) it('should decode UTF-16 string (BE BOM)', function (done) { // BOM makes this BE var stream = createStream(Buffer.from('feff00bf004300f3006d006f002000650073007400e10073003f', 'hex')) var string = '¿Cómo estás?' getRawBody(stream, 'utf-16', function (err, str) { assert.ifError(err) assert.strictEqual(str, string) done() }) }) it('should decode UTF-16LE string', function (done) { // UTF-16LE is different from UTF-16 due to BOM behavior var stream = createStream(Buffer.from('bf004300f3006d006f002000650073007400e10073003f00', 'hex')) var string = '¿Cómo estás?' getRawBody(stream, 'utf-16le', function (err, str) { assert.ifError(err) assert.strictEqual(str, string) done() }) }) it('should correctly calculate the expected length', function (done) { var stream = createStream(Buffer.from('{"test":"å"}')) getRawBody(stream, { encoding: 'utf-8', length: 13 }, done) }) }) it('should work on streams1 stream', function (done) { var stream = new EventEmitter() getRawBody(stream, { encoding: true, length: 19 }, function (err, value) { assert.ifError(err) assert.strictEqual(value, 'foobar,foobaz,yay!!') done() }) process.nextTick(function () { stream.emit('data', 'foobar,') stream.emit('data', 'foobaz,') stream.emit('data', 'yay!!') stream.emit('end') }) }) }) function checkBuffer (buf) { assert.ok(Buffer.isBuffer(buf)) assert.strictEqual(buf.length, length) assert.strictEqual(buf.toString('utf8'), string) } function checkString (str) { assert.ok(typeof str === 'string') assert.strictEqual(str, string) } function createStream (buf) { if (!buf) return fs.createReadStream(file) var stream = new Readable() stream._read = function () { stream.push(buf) stream.push(null) } return stream } function throwExpectedError () { throw new Error('expected error') } raw-body-2.4.3/test/native.js000066400000000000000000000024171420261545600160600ustar00rootroot00000000000000var assert = require('assert') var Buffer = require('safe-buffer').Buffer var getRawBody = require('..') var Readable = require('stream').Readable var run = Readable ? describe : describe.skip run('using native streams', function () { it('should read contents', function (done) { var stream = createStream(Buffer.from('hello, streams!')) getRawBody(stream, function (err, buf) { assert.ifError(err) assert.strictEqual(buf.toString(), 'hello, streams!') done() }) }) it('should read pre-buffered contents', function (done) { var stream = createStream(Buffer.from('hello, streams!')) stream.push('oh, ') getRawBody(stream, function (err, buf) { assert.ifError(err) assert.strictEqual(buf.toString(), 'oh, hello, streams!') done() }) }) it('should stop the stream on limit', function (done) { var stream = createStream(Buffer.from('hello, streams!')) getRawBody(stream, { limit: 2 }, function (err, buf) { assert.ok(err) assert.strictEqual(err.status, 413) assert.strictEqual(err.limit, 2) process.nextTick(done) }) }) }) function createStream (buf) { var stream = new Readable() stream._read = function () { stream.push(buf) stream.push(null) } return stream }