pax_global_header00006660000000000000000000000064146676775030014537gustar00rootroot0000000000000052 comment=17529513673e39ba79886a7ce3363320cf1c0c50 body-parser-1.20.3/000077500000000000000000000000001466767750300140515ustar00rootroot00000000000000body-parser-1.20.3/.eslintignore000066400000000000000000000000261466767750300165520ustar00rootroot00000000000000coverage node_modules body-parser-1.20.3/.eslintrc.yml000066400000000000000000000002741466767750300165000ustar00rootroot00000000000000root: true extends: - standard - plugin:markdown/recommended plugins: - markdown overrides: - files: '**/*.md' processor: 'markdown/markdown' rules: no-param-reassign: error body-parser-1.20.3/.github/000077500000000000000000000000001466767750300154115ustar00rootroot00000000000000body-parser-1.20.3/.github/workflows/000077500000000000000000000000001466767750300174465ustar00rootroot00000000000000body-parser-1.20.3/.github/workflows/ci.yml000066400000000000000000000145041466767750300205700ustar00rootroot00000000000000name: ci on: push: branches: - master pull_request: 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 - Node.js 18.x - Node.js 19.x - Node.js 20.x - Node.js 21.x - Node.js 22.x include: - name: Node.js 0.8 node-version: "0.8" npm-i: mocha@2.5.3 supertest@1.1.0 npm-rm: nyc - name: Node.js 0.10 node-version: "0.10" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: Node.js 0.12 node-version: "0.12" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: io.js 1.x node-version: "1" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: io.js 2.x node-version: "2" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: io.js 3.x node-version: "3" npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0 - name: Node.js 4.x node-version: "4" npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - name: Node.js 5.x node-version: "5" npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - name: Node.js 6.x node-version: "6" npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - name: Node.js 7.x node-version: "7" npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - name: Node.js 8.x node-version: "8" npm-i: mocha@7.2.0 nyc@14.1.1 - name: Node.js 9.x node-version: "9" npm-i: mocha@7.2.0 nyc@14.1.1 - name: Node.js 10.x node-version: "10" npm-i: mocha@8.4.0 - name: Node.js 11.x node-version: "11" npm-i: mocha@8.4.0 - name: Node.js 12.x node-version: "12" npm-i: mocha@9.2.2 - name: Node.js 13.x node-version: "13" npm-i: mocha@9.2.2 - name: Node.js 14.x node-version: "14" - name: Node.js 15.x node-version: "15" - name: Node.js 16.x node-version: "16" - name: Node.js 17.x node-version: "17" - name: Node.js 18.x node-version: "18" - name: Node.js 19.x node-version: "19" - name: Node.js 20.x node-version: "20" - name: Node.js 21.x node-version: "21" - name: Node.js 22.x node-version: "22.4.1" steps: - uses: actions/checkout@v4 - 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 npm install -g --prefix "$(which node)/../.." npm@1.2.8000 sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")" fi dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" - name: Configure npm run: | if [[ "$(npm config get package-lock)" == "true" ]]; then npm config set package-lock false else npm config set shrinkwrap false fi - 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 < 12 if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; 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 $2 "=" $3 }' >> "$GITHUB_OUTPUT" - 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@v3 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@v4 - name: Install lcov shell: bash run: sudo apt-get -y install lcov - name: Collect coverage reports uses: actions/download-artifact@v3 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 }} body-parser-1.20.3/.github/workflows/scorecard.yml000066400000000000000000000054511466767750300221430ustar00rootroot00000000000000# This workflow uses actions that are not certified by GitHub. They are provided # by a third-party and are governed by separate terms of service, privacy # policy, and support documentation. name: Scorecard supply-chain security on: # For Branch-Protection check. Only the default branch is supported. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection branch_protection_rule: # To guarantee Maintained check is occasionally updated. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained schedule: - cron: '16 21 * * 1' push: branches: [ "master" ] # Declare default permissions as read only. permissions: read-all jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write steps: - name: "Checkout code" uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.2 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 with: results_file: results.sarif results_format: sarif # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: # - you want to enable the Branch-Protection check on a *public* repository, or # - you are installing Scorecard on a *private* repository # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. # repo_token: ${{ secrets.SCORECARD_TOKEN }} # Public repositories: # - Publish results to OpenSSF REST API for easy access by consumers # - Allows the repository to include the Scorecard badge. # - See https://github.com/ossf/scorecard-action#publishing-results. # For private repositories: # - `publish_results` will always be set to `false`, regardless # of the value entered here. publish_results: true # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: name: SARIF file path: results.sarif retention-days: 5 # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@2f93e4319b2f04a2efc38fa7f78bd681bc3f7b2f # v2.23.2 with: sarif_file: results.sarif body-parser-1.20.3/.gitignore000066400000000000000000000001051466767750300160350ustar00rootroot00000000000000.nyc_output/ coverage/ node_modules/ npm-debug.log package-lock.json body-parser-1.20.3/HISTORY.md000066400000000000000000000405311466767750300155370ustar00rootroot000000000000001.20.3 / 2024-09-10 =================== * deps: qs@6.13.0 * add `depth` option to customize the depth level in the parser * IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`) 1.20.2 / 2023-02-21 =================== * Fix strict json error message on Node.js 19+ * deps: content-type@~1.0.5 - perf: skip value escaping when unnecessary * deps: raw-body@2.5.2 1.20.1 / 2022-10-06 =================== * deps: qs@6.11.0 * perf: remove unnecessary object clone 1.20.0 / 2022-04-02 =================== * Fix error message for json parse whitespace in `strict` * Fix internal error when inflated body exceeds limit * Prevent loss of async hooks context * Prevent hanging when request already read * deps: depd@2.0.0 - Replace internal `eval` usage with `Function` constructor - Use instance methods on `process` to check for listeners * deps: http-errors@2.0.0 - deps: depd@2.0.0 - deps: statuses@2.0.1 * deps: on-finished@2.4.1 * deps: qs@6.10.3 * deps: raw-body@2.5.1 - deps: http-errors@2.0.0 1.19.2 / 2022-02-15 =================== * deps: bytes@3.1.2 * deps: qs@6.9.7 * Fix handling of `__proto__` keys * deps: raw-body@2.4.3 - deps: bytes@3.1.2 1.19.1 / 2021-12-10 =================== * deps: bytes@3.1.1 * deps: http-errors@1.8.1 - deps: inherits@2.0.4 - deps: toidentifier@1.0.1 - deps: setprototypeof@1.2.0 * deps: qs@6.9.6 * deps: raw-body@2.4.2 - deps: bytes@3.1.1 - deps: http-errors@1.8.1 * deps: safe-buffer@5.2.1 * deps: type-is@~1.6.18 1.19.0 / 2019-04-25 =================== * 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 * deps: qs@6.7.0 - Fix parsing array brackets after index * deps: raw-body@2.4.0 - deps: bytes@3.1.0 - deps: http-errors@1.7.2 - deps: iconv-lite@0.4.24 * deps: type-is@~1.6.17 - deps: mime-types@~2.1.24 - perf: prevent internal `throw` on invalid type 1.18.3 / 2018-05-14 =================== * Fix stack trace for strict json parse error * deps: depd@~1.1.2 - perf: remove argument reassignment * 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+ * deps: qs@6.5.2 * deps: raw-body@2.3.3 - deps: http-errors@1.6.3 - deps: iconv-lite@0.4.23 * deps: type-is@~1.6.16 - deps: mime-types@~2.1.18 1.18.2 / 2017-09-22 =================== * deps: debug@2.6.9 * perf: remove argument reassignment 1.18.1 / 2017-09-12 =================== * deps: content-type@~1.0.4 - perf: remove argument reassignment - perf: skip parameter parsing when no parameters * deps: iconv-lite@0.4.19 - Fix ISO-8859-1 regression - Update Windows-1255 * deps: qs@6.5.1 - Fix parsing & compacting very deep objects * deps: raw-body@2.3.2 - deps: iconv-lite@0.4.19 1.18.0 / 2017-09-08 =================== * Fix JSON strict violation error to match native parse error * Include the `body` property on verify errors * Include the `type` property on all generated errors * Use `http-errors` to set status code on errors * deps: bytes@3.0.0 * deps: debug@2.6.8 * deps: depd@~1.1.1 - Remove unnecessary `Buffer` loading * deps: http-errors@~1.6.2 - deps: depd@1.1.1 * 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 * deps: qs@6.5.0 * deps: raw-body@2.3.1 - Use `http-errors` for standard emitted errors - deps: bytes@3.0.0 - deps: iconv-lite@0.4.18 - perf: skip buffer decoding on overage chunk * perf: prevent internal `throw` when missing charset 1.17.2 / 2017-05-17 =================== * deps: debug@2.6.7 - Fix `DEBUG_MAX_ARRAY_LENGTH` - deps: ms@2.0.0 * deps: type-is@~1.6.15 - deps: mime-types@~2.1.15 1.17.1 / 2017-03-06 =================== * deps: qs@6.4.0 - Fix regression parsing keys starting with `[` 1.17.0 / 2017-03-01 =================== * deps: http-errors@~1.6.1 - Make `message` property enumerable for `HttpError`s - deps: setprototypeof@1.0.3 * deps: qs@6.3.1 - Fix compacting nested arrays 1.16.1 / 2017-02-10 =================== * deps: debug@2.6.1 - Fix deprecation messages in WebStorm and other editors - Undeprecate `DEBUG_FD` set to `1` or `2` 1.16.0 / 2017-01-17 =================== * deps: debug@2.6.0 - Allow colors in workers - Deprecated `DEBUG_FD` environment variable - Fix error when running under React Native - Use same color for same namespace - deps: ms@0.7.2 * deps: http-errors@~1.5.1 - deps: inherits@2.0.3 - deps: setprototypeof@1.0.2 - deps: statuses@'>= 1.3.1 < 2' * 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 * deps: qs@6.2.1 - Fix array parsing from skipping empty values * deps: raw-body@~2.2.0 - deps: iconv-lite@0.4.15 * deps: type-is@~1.6.14 - deps: mime-types@~2.1.13 1.15.2 / 2016-06-19 =================== * deps: bytes@2.4.0 * deps: content-type@~1.0.2 - perf: enable strict mode * deps: http-errors@~1.5.0 - Use `setprototypeof` module to replace `__proto__` setting - deps: statuses@'>= 1.3.0 < 2' - perf: enable strict mode * deps: qs@6.2.0 * deps: raw-body@~2.1.7 - deps: bytes@2.4.0 - perf: remove double-cleanup on happy path * deps: type-is@~1.6.13 - deps: mime-types@~2.1.11 1.15.1 / 2016-05-05 =================== * deps: bytes@2.3.0 - Drop partial bytes on all parsed units - Fix parsing byte string that looks like hex * deps: raw-body@~2.1.6 - deps: bytes@2.3.0 * deps: type-is@~1.6.12 - deps: mime-types@~2.1.10 1.15.0 / 2016-02-10 =================== * deps: http-errors@~1.4.0 - Add `HttpError` export, for `err instanceof createError.HttpError` - deps: inherits@2.0.1 - deps: statuses@'>= 1.2.1 < 2' * deps: qs@6.1.0 * deps: type-is@~1.6.11 - deps: mime-types@~2.1.9 1.14.2 / 2015-12-16 =================== * deps: bytes@2.2.0 * deps: iconv-lite@0.4.13 * deps: qs@5.2.0 * deps: raw-body@~2.1.5 - deps: bytes@2.2.0 - deps: iconv-lite@0.4.13 * deps: type-is@~1.6.10 - deps: mime-types@~2.1.8 1.14.1 / 2015-09-27 =================== * Fix issue where invalid charset results in 400 when `verify` used * deps: iconv-lite@0.4.12 - Fix CESU-8 decoding in Node.js 4.x * deps: raw-body@~2.1.4 - Fix masking critical errors from `iconv-lite` - deps: iconv-lite@0.4.12 * deps: type-is@~1.6.9 - deps: mime-types@~2.1.7 1.14.0 / 2015-09-16 =================== * Fix JSON strict parse error to match syntax errors * Provide static `require` analysis in `urlencoded` parser * deps: depd@~1.1.0 - Support web browser loading * deps: qs@5.1.0 * deps: raw-body@~2.1.3 - Fix sync callback when attaching data listener causes sync read * deps: type-is@~1.6.8 - Fix type error when given invalid type to match against - deps: mime-types@~2.1.6 1.13.3 / 2015-07-31 =================== * deps: type-is@~1.6.6 - deps: mime-types@~2.1.4 1.13.2 / 2015-07-05 =================== * deps: iconv-lite@0.4.11 * deps: qs@4.0.0 - Fix dropping parameters like `hasOwnProperty` - Fix user-visible incompatibilities from 3.1.0 - Fix various parsing edge cases * deps: raw-body@~2.1.2 - Fix error stack traces to skip `makeError` - deps: iconv-lite@0.4.11 * deps: type-is@~1.6.4 - deps: mime-types@~2.1.2 - perf: enable strict mode - perf: remove argument reassignment 1.13.1 / 2015-06-16 =================== * deps: qs@2.4.2 - Downgraded from 3.1.0 because of user-visible incompatibilities 1.13.0 / 2015-06-14 =================== * Add `statusCode` property on `Error`s, in addition to `status` * Change `type` default to `application/json` for JSON parser * Change `type` default to `application/x-www-form-urlencoded` for urlencoded parser * Provide static `require` analysis * Use the `http-errors` module to generate errors * deps: bytes@2.1.0 - Slight optimizations * deps: iconv-lite@0.4.10 - The encoding UTF-16 without BOM now defaults to UTF-16LE when detection fails - Leading BOM is now removed when decoding * deps: on-finished@~2.3.0 - Add defined behavior for HTTP `CONNECT` requests - Add defined behavior for HTTP `Upgrade` requests - deps: ee-first@1.1.1 * deps: qs@3.1.0 - Fix dropping parameters like `hasOwnProperty` - Fix various parsing edge cases - Parsed object now has `null` prototype * deps: raw-body@~2.1.1 - Use `unpipe` module for unpiping requests - deps: iconv-lite@0.4.10 * deps: type-is@~1.6.3 - deps: mime-types@~2.1.1 - perf: reduce try block size - perf: remove bitwise operations * perf: enable strict mode * perf: remove argument reassignment * perf: remove delete call 1.12.4 / 2015-05-10 =================== * deps: debug@~2.2.0 * deps: qs@2.4.2 - Fix allowing parameters like `constructor` * deps: on-finished@~2.2.1 * deps: raw-body@~2.0.1 - Fix a false-positive when unpiping in Node.js 0.8 - deps: bytes@2.0.1 * deps: type-is@~1.6.2 - deps: mime-types@~2.0.11 1.12.3 / 2015-04-15 =================== * Slight efficiency improvement when not debugging * deps: depd@~1.0.1 * deps: iconv-lite@0.4.8 - Add encoding alias UNICODE-1-1-UTF-7 * deps: raw-body@1.3.4 - Fix hanging callback if request aborts during read - deps: iconv-lite@0.4.8 1.12.2 / 2015-03-16 =================== * deps: qs@2.4.1 - Fix error when parameter `hasOwnProperty` is present 1.12.1 / 2015-03-15 =================== * deps: debug@~2.1.3 - Fix high intensity foreground color for bold - deps: ms@0.7.0 * deps: type-is@~1.6.1 - deps: mime-types@~2.0.10 1.12.0 / 2015-02-13 =================== * add `debug` messages * accept a function for the `type` option * use `content-type` to parse `Content-Type` headers * deps: iconv-lite@0.4.7 - Gracefully support enumerables on `Object.prototype` * deps: raw-body@1.3.3 - deps: iconv-lite@0.4.7 * deps: type-is@~1.6.0 - fix argument reassignment - fix false-positives in `hasBody` `Transfer-Encoding` check - support wildcard for both type and subtype (`*/*`) - deps: mime-types@~2.0.9 1.11.0 / 2015-01-30 =================== * make internal `extended: true` depth limit infinity * deps: type-is@~1.5.6 - deps: mime-types@~2.0.8 1.10.2 / 2015-01-20 =================== * deps: iconv-lite@0.4.6 - Fix rare aliases of single-byte encodings * deps: raw-body@1.3.2 - deps: iconv-lite@0.4.6 1.10.1 / 2015-01-01 =================== * deps: on-finished@~2.2.0 * deps: type-is@~1.5.5 - deps: mime-types@~2.0.7 1.10.0 / 2014-12-02 =================== * make internal `extended: true` array limit dynamic 1.9.3 / 2014-11-21 ================== * deps: iconv-lite@0.4.5 - Fix Windows-31J and X-SJIS encoding support * deps: qs@2.3.3 - Fix `arrayLimit` behavior * deps: raw-body@1.3.1 - deps: iconv-lite@0.4.5 * deps: type-is@~1.5.3 - deps: mime-types@~2.0.3 1.9.2 / 2014-10-27 ================== * deps: qs@2.3.2 - Fix parsing of mixed objects and values 1.9.1 / 2014-10-22 ================== * deps: on-finished@~2.1.1 - Fix handling of pipelined requests * deps: qs@2.3.0 - Fix parsing of mixed implicit and explicit arrays * deps: type-is@~1.5.2 - deps: mime-types@~2.0.2 1.9.0 / 2014-09-24 ================== * include the charset in "unsupported charset" error message * include the encoding in "unsupported content encoding" error message * deps: depd@~1.0.0 1.8.4 / 2014-09-23 ================== * fix content encoding to be case-insensitive 1.8.3 / 2014-09-19 ================== * deps: qs@2.2.4 - Fix issue with object keys starting with numbers truncated 1.8.2 / 2014-09-15 ================== * deps: depd@0.4.5 1.8.1 / 2014-09-07 ================== * deps: media-typer@0.3.0 * deps: type-is@~1.5.1 1.8.0 / 2014-09-05 ================== * make empty-body-handling consistent between chunked requests - empty `json` produces `{}` - empty `raw` produces `new Buffer(0)` - empty `text` produces `''` - empty `urlencoded` produces `{}` * deps: qs@2.2.3 - Fix issue where first empty value in array is discarded * deps: type-is@~1.5.0 - fix `hasbody` to be true for `content-length: 0` 1.7.0 / 2014-09-01 ================== * add `parameterLimit` option to `urlencoded` parser * change `urlencoded` extended array limit to 100 * respond with 413 when over `parameterLimit` in `urlencoded` 1.6.7 / 2014-08-29 ================== * deps: qs@2.2.2 - Remove unnecessary cloning 1.6.6 / 2014-08-27 ================== * deps: qs@2.2.0 - Array parsing fix - Performance improvements 1.6.5 / 2014-08-16 ================== * deps: on-finished@2.1.0 1.6.4 / 2014-08-14 ================== * deps: qs@1.2.2 1.6.3 / 2014-08-10 ================== * deps: qs@1.2.1 1.6.2 / 2014-08-07 ================== * deps: qs@1.2.0 - Fix parsing array of objects 1.6.1 / 2014-08-06 ================== * deps: qs@1.1.0 - Accept urlencoded square brackets - Accept empty values in implicit array notation 1.6.0 / 2014-08-05 ================== * deps: qs@1.0.2 - Complete rewrite - Limits array length to 20 - Limits object depth to 5 - Limits parameters to 1,000 1.5.2 / 2014-07-27 ================== * deps: depd@0.4.4 - Work-around v8 generating empty stack traces 1.5.1 / 2014-07-26 ================== * deps: depd@0.4.3 - Fix exception when global `Error.stackTraceLimit` is too low 1.5.0 / 2014-07-20 ================== * deps: depd@0.4.2 - Add `TRACE_DEPRECATION` environment variable - Remove non-standard grey color from color output - Support `--no-deprecation` argument - Support `--trace-deprecation` argument * deps: iconv-lite@0.4.4 - Added encoding UTF-7 * deps: raw-body@1.3.0 - deps: iconv-lite@0.4.4 - Added encoding UTF-7 - Fix `Cannot switch to old mode now` error on Node.js 0.10+ * deps: type-is@~1.3.2 1.4.3 / 2014-06-19 ================== * deps: type-is@1.3.1 - fix global variable leak 1.4.2 / 2014-06-19 ================== * deps: type-is@1.3.0 - improve type parsing 1.4.1 / 2014-06-19 ================== * fix urlencoded extended deprecation message 1.4.0 / 2014-06-19 ================== * add `text` parser * add `raw` parser * check accepted charset in content-type (accepts utf-8) * check accepted encoding in content-encoding (accepts identity) * deprecate `bodyParser()` middleware; use `.json()` and `.urlencoded()` as needed * deprecate `urlencoded()` without provided `extended` option * lazy-load urlencoded parsers * parsers split into files for reduced mem usage * support gzip and deflate bodies - set `inflate: false` to turn off * deps: raw-body@1.2.2 - Support all encodings from `iconv-lite` 1.3.1 / 2014-06-11 ================== * deps: type-is@1.2.1 - Switch dependency from mime to mime-types@1.0.0 1.3.0 / 2014-05-31 ================== * add `extended` option to urlencoded parser 1.2.2 / 2014-05-27 ================== * deps: raw-body@1.1.6 - assert stream encoding on node.js 0.8 - assert stream encoding on node.js < 0.10.6 - deps: bytes@1 1.2.1 / 2014-05-26 ================== * invoke `next(err)` after request fully read - prevents hung responses and socket hang ups 1.2.0 / 2014-05-11 ================== * add `verify` option * deps: type-is@1.2.0 - support suffix matching 1.1.2 / 2014-05-11 ================== * improve json parser speed 1.1.1 / 2014-05-11 ================== * fix repeated limit parsing with every request 1.1.0 / 2014-05-10 ================== * add `type` option * deps: pin for safety and consistency 1.0.2 / 2014-04-14 ================== * use `type-is` module 1.0.1 / 2014-03-20 ================== * lower default limits to 100kb body-parser-1.20.3/LICENSE000066400000000000000000000022241466767750300150560ustar00rootroot00000000000000(The MIT License) Copyright (c) 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. body-parser-1.20.3/README.md000066400000000000000000000453541466767750300153430ustar00rootroot00000000000000# body-parser [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][coveralls-image]][coveralls-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer] Node.js body parsing middleware. Parse incoming request bodies in a middleware before your handlers, available under the `req.body` property. **Note** As `req.body`'s shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting. For example, `req.body.foo.toString()` may fail in multiple ways, for example the `foo` property may not be there or may not be a string, and `toString` may not be a function and instead a string or other user input. [Learn about the anatomy of an HTTP transaction in Node.js](https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/). _This does not handle multipart bodies_, due to their complex and typically large nature. For multipart bodies, you may be interested in the following modules: * [busboy](https://www.npmjs.org/package/busboy#readme) and [connect-busboy](https://www.npmjs.org/package/connect-busboy#readme) * [multiparty](https://www.npmjs.org/package/multiparty#readme) and [connect-multiparty](https://www.npmjs.org/package/connect-multiparty#readme) * [formidable](https://www.npmjs.org/package/formidable#readme) * [multer](https://www.npmjs.org/package/multer#readme) This module provides the following parsers: * [JSON body parser](#bodyparserjsonoptions) * [Raw body parser](#bodyparserrawoptions) * [Text body parser](#bodyparsertextoptions) * [URL-encoded form body parser](#bodyparserurlencodedoptions) Other body parsers you might be interested in: - [body](https://www.npmjs.org/package/body#readme) - [co-body](https://www.npmjs.org/package/co-body#readme) ## Installation ```sh $ npm install body-parser ``` ## API ```js var bodyParser = require('body-parser') ``` The `bodyParser` object exposes various factories to create middlewares. All middlewares will populate the `req.body` property with the parsed body when the `Content-Type` request header matches the `type` option, or an empty object (`{}`) if there was no body to parse, the `Content-Type` was not matched, or an error occurred. The various errors returned by this module are described in the [errors section](#errors). ### bodyParser.json([options]) Returns middleware that only parses `json` and only looks at requests where the `Content-Type` header matches the `type` option. This parser accepts any Unicode encoding of the body and supports automatic inflation of `gzip` and `deflate` encodings. A new `body` object containing the parsed data is populated on the `request` object after the middleware (i.e. `req.body`). #### Options The `json` function takes an optional `options` object that may contain any of the following keys: ##### inflate When set to `true`, then deflated (compressed) bodies will be inflated; when `false`, deflated bodies are rejected. Defaults to `true`. ##### limit Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults to `'100kb'`. ##### reviver The `reviver` option is passed directly to `JSON.parse` as the second argument. You can find more information on this argument [in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter). ##### strict When set to `true`, will only accept arrays and objects; when `false` will accept anything `JSON.parse` accepts. Defaults to `true`. ##### type The `type` option is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `json`), a mime type (like `application/json`), or a mime type with a wildcard (like `*/*` or `*/json`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. Defaults to `application/json`. ##### verify The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. ### bodyParser.raw([options]) Returns middleware that parses all bodies as a `Buffer` and only looks at requests where the `Content-Type` header matches the `type` option. This parser supports automatic inflation of `gzip` and `deflate` encodings. A new `body` object containing the parsed data is populated on the `request` object after the middleware (i.e. `req.body`). This will be a `Buffer` object of the body. #### Options The `raw` function takes an optional `options` object that may contain any of the following keys: ##### inflate When set to `true`, then deflated (compressed) bodies will be inflated; when `false`, deflated bodies are rejected. Defaults to `true`. ##### limit Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults to `'100kb'`. ##### type The `type` option is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `bin`), a mime type (like `application/octet-stream`), or a mime type with a wildcard (like `*/*` or `application/*`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. Defaults to `application/octet-stream`. ##### verify The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. ### bodyParser.text([options]) Returns middleware that parses all bodies as a string and only looks at requests where the `Content-Type` header matches the `type` option. This parser supports automatic inflation of `gzip` and `deflate` encodings. A new `body` string containing the parsed data is populated on the `request` object after the middleware (i.e. `req.body`). This will be a string of the body. #### Options The `text` function takes an optional `options` object that may contain any of the following keys: ##### defaultCharset Specify the default character set for the text content if the charset is not specified in the `Content-Type` header of the request. Defaults to `utf-8`. ##### inflate When set to `true`, then deflated (compressed) bodies will be inflated; when `false`, deflated bodies are rejected. Defaults to `true`. ##### limit Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults to `'100kb'`. ##### type The `type` option is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `txt`), a mime type (like `text/plain`), or a mime type with a wildcard (like `*/*` or `text/*`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. Defaults to `text/plain`. ##### verify The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. ### bodyParser.urlencoded([options]) Returns middleware that only parses `urlencoded` bodies and only looks at requests where the `Content-Type` header matches the `type` option. This parser accepts only UTF-8 encoding of the body and supports automatic inflation of `gzip` and `deflate` encodings. A new `body` object containing the parsed data is populated on the `request` object after the middleware (i.e. `req.body`). This object will contain key-value pairs, where the value can be a string or array (when `extended` is `false`), or any type (when `extended` is `true`). #### Options The `urlencoded` function takes an optional `options` object that may contain any of the following keys: ##### extended The `extended` option allows to choose between parsing the URL-encoded data with the `querystring` library (when `false`) or the `qs` library (when `true`). The "extended" syntax allows for rich objects and arrays to be encoded into the URL-encoded format, allowing for a JSON-like experience with URL-encoded. For more information, please [see the qs library](https://www.npmjs.org/package/qs#readme). Defaults to `true`, but using the default has been deprecated. Please research into the difference between `qs` and `querystring` and choose the appropriate setting. ##### inflate When set to `true`, then deflated (compressed) bodies will be inflated; when `false`, deflated bodies are rejected. Defaults to `true`. ##### limit Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults to `'100kb'`. ##### parameterLimit The `parameterLimit` option controls the maximum number of parameters that are allowed in the URL-encoded data. If a request contains more parameters than this value, a 413 will be returned to the client. Defaults to `1000`. ##### type The `type` option is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `urlencoded`), a mime type (like `application/x-www-form-urlencoded`), or a mime type with a wildcard (like `*/x-www-form-urlencoded`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. Defaults to `application/x-www-form-urlencoded`. ##### verify The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. #### depth The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible. ## Errors The middlewares provided by this module create errors using the [`http-errors` module](https://www.npmjs.com/package/http-errors). The errors will typically have a `status`/`statusCode` property that contains the suggested HTTP response code, an `expose` property to determine if the `message` property should be displayed to the client, a `type` property to determine the type of error without matching against the `message`, and a `body` property containing the read body, if available. The following are the common errors created, though any error can come through for various reasons. ### content encoding unsupported This error will occur when the request had a `Content-Encoding` header that contained an encoding but the "inflation" option was set to `false`. The `status` property is set to `415`, the `type` property is set to `'encoding.unsupported'`, and the `charset` property will be set to the encoding that is unsupported. ### entity parse failed This error will occur when the request contained an entity that could not be parsed by the middleware. The `status` property is set to `400`, the `type` property is set to `'entity.parse.failed'`, and the `body` property is set to the entity value that failed parsing. ### entity verify failed This error will occur when the request contained an entity that could not be failed verification by the defined `verify` option. The `status` property is set to `403`, the `type` property is set to `'entity.verify.failed'`, and the `body` property is set to the entity value that failed verification. ### request aborted This error will occur when the request is aborted by the client before reading the body has finished. The `received` property will be set to the number of bytes received before the request was aborted and the `expected` property is set to the number of expected bytes. The `status` property is set to `400` and `type` property is set to `'request.aborted'`. ### request entity too large This error will occur when the request body's size is larger than the "limit" option. The `limit` property will be set to the byte limit and the `length` property will be set to the request body's length. The `status` property is set to `413` and the `type` property is set to `'entity.too.large'`. ### request size did not match content length This error will occur when the request's length did not match the length from the `Content-Length` header. This typically occurs when the request is malformed, typically when the `Content-Length` header was calculated based on characters instead of bytes. The `status` property is set to `400` and the `type` property is set to `'request.size.invalid'`. ### stream encoding should not be set This error will occur when something called the `req.setEncoding` method prior to this middleware. This module operates directly on bytes only and you cannot call `req.setEncoding` when using this module. The `status` property is set to `500` and the `type` property is set to `'stream.encoding.set'`. ### stream is not readable This error will occur when the request is no longer readable when this middleware attempts to read it. This typically means something other than a middleware from this module read the request body already and the middleware was also configured to read the same request. The `status` property is set to `500` and the `type` property is set to `'stream.not.readable'`. ### too many parameters This error will occur when the content of the request exceeds the configured `parameterLimit` for the `urlencoded` parser. The `status` property is set to `413` and the `type` property is set to `'parameters.too.many'`. ### unsupported charset "BOGUS" This error will occur when the request had a charset parameter in the `Content-Type` header, but the `iconv-lite` module does not support it OR the parser does not support it. The charset is contained in the message as well as in the `charset` property. The `status` property is set to `415`, the `type` property is set to `'charset.unsupported'`, and the `charset` property is set to the charset that is unsupported. ### unsupported content encoding "bogus" This error will occur when the request had a `Content-Encoding` header that contained an unsupported encoding. The encoding is contained in the message as well as in the `encoding` property. The `status` property is set to `415`, the `type` property is set to `'encoding.unsupported'`, and the `encoding` property is set to the encoding that is unsupported. ### The input exceeded the depth This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. ## Examples ### Express/Connect top-level generic This example demonstrates adding a generic JSON and URL-encoded parser as a top-level middleware, which will parse the bodies of all incoming requests. This is the simplest setup. ```js var express = require('express') var bodyParser = require('body-parser') var app = express() // parse application/x-www-form-urlencoded app.use(bodyParser.urlencoded({ extended: false })) // parse application/json app.use(bodyParser.json()) app.use(function (req, res) { res.setHeader('Content-Type', 'text/plain') res.write('you posted:\n') res.end(JSON.stringify(req.body, null, 2)) }) ``` ### Express route-specific This example demonstrates adding body parsers specifically to the routes that need them. In general, this is the most recommended way to use body-parser with Express. ```js var express = require('express') var bodyParser = require('body-parser') var app = express() // create application/json parser var jsonParser = bodyParser.json() // create application/x-www-form-urlencoded parser var urlencodedParser = bodyParser.urlencoded({ extended: false }) // POST /login gets urlencoded bodies app.post('/login', urlencodedParser, function (req, res) { res.send('welcome, ' + req.body.username) }) // POST /api/users gets JSON bodies app.post('/api/users', jsonParser, function (req, res) { // create user in req.body }) ``` ### Change accepted type for parsers All the parsers accept a `type` option which allows you to change the `Content-Type` that the middleware will parse. ```js var express = require('express') var bodyParser = require('body-parser') var app = express() // parse various different custom JSON types as JSON app.use(bodyParser.json({ type: 'application/*+json' })) // parse some custom thing into a Buffer app.use(bodyParser.raw({ type: 'application/vnd.custom-type' })) // parse an HTML body into a string app.use(bodyParser.text({ type: 'text/html' })) ``` ## License [MIT](LICENSE) [ci-image]: https://badgen.net/github/checks/expressjs/body-parser/master?label=ci [ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml [coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/body-parser/master [coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master [node-version-image]: https://badgen.net/npm/node/body-parser [node-version-url]: https://nodejs.org/en/download [npm-downloads-image]: https://badgen.net/npm/dm/body-parser [npm-url]: https://npmjs.org/package/body-parser [npm-version-image]: https://badgen.net/npm/v/body-parser [ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/body-parser/badge [ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/body-parserbody-parser-1.20.3/SECURITY.md000066400000000000000000000022511466767750300156420ustar00rootroot00000000000000# Security Policies and Procedures ## Reporting a Bug The Express team and community take all security bugs seriously. Thank you for improving the security of Express. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. Report security bugs by emailing the current owner(s) of `body-parser`. This information can be found in the npm registry using the command `npm owner ls body-parser`. If unsure or unable to get the information from the above, open an issue in the [project issue tracker](https://github.com/expressjs/body-parser/issues) asking for the current contact information. To ensure the timely response to your report, please ensure that the entirety of the report is contained within the email body and not solely behind a web link or an attachment. At least one owner will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours indicating the next steps in handling your report. After the initial reply to your report, the owners will endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. body-parser-1.20.3/index.js000066400000000000000000000051711466767750300155220ustar00rootroot00000000000000/*! * body-parser * Copyright(c) 2014-2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. * @private */ var deprecate = require('depd')('body-parser') /** * Cache of loaded parsers. * @private */ var parsers = Object.create(null) /** * @typedef Parsers * @type {function} * @property {function} json * @property {function} raw * @property {function} text * @property {function} urlencoded */ /** * Module exports. * @type {Parsers} */ exports = module.exports = deprecate.function(bodyParser, 'bodyParser: use individual json/urlencoded middlewares') /** * JSON parser. * @public */ Object.defineProperty(exports, 'json', { configurable: true, enumerable: true, get: createParserGetter('json') }) /** * Raw parser. * @public */ Object.defineProperty(exports, 'raw', { configurable: true, enumerable: true, get: createParserGetter('raw') }) /** * Text parser. * @public */ Object.defineProperty(exports, 'text', { configurable: true, enumerable: true, get: createParserGetter('text') }) /** * URL-encoded parser. * @public */ Object.defineProperty(exports, 'urlencoded', { configurable: true, enumerable: true, get: createParserGetter('urlencoded') }) /** * Create a middleware to parse json and urlencoded bodies. * * @param {object} [options] * @return {function} * @deprecated * @public */ function bodyParser (options) { // use default type for parsers var opts = Object.create(options || null, { type: { configurable: true, enumerable: true, value: undefined, writable: true } }) var _urlencoded = exports.urlencoded(opts) var _json = exports.json(opts) return function bodyParser (req, res, next) { _json(req, res, function (err) { if (err) return next(err) _urlencoded(req, res, next) }) } } /** * Create a getter for loading a parser. * @private */ function createParserGetter (name) { return function get () { return loadParser(name) } } /** * Load a parser module. * @private */ function loadParser (parserName) { var parser = parsers[parserName] if (parser !== undefined) { return parser } // this uses a switch for static require analysis switch (parserName) { case 'json': parser = require('./lib/types/json') break case 'raw': parser = require('./lib/types/raw') break case 'text': parser = require('./lib/types/text') break case 'urlencoded': parser = require('./lib/types/urlencoded') break } // store to prevent invoking require() return (parsers[parserName] = parser) } body-parser-1.20.3/lib/000077500000000000000000000000001466767750300146175ustar00rootroot00000000000000body-parser-1.20.3/lib/read.js000066400000000000000000000103451466767750300160730ustar00rootroot00000000000000/*! * body-parser * Copyright(c) 2014-2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. * @private */ var createError = require('http-errors') var destroy = require('destroy') var getBody = require('raw-body') var iconv = require('iconv-lite') var onFinished = require('on-finished') var unpipe = require('unpipe') var zlib = require('zlib') /** * Module exports. */ module.exports = read /** * Read a request into a buffer and parse. * * @param {object} req * @param {object} res * @param {function} next * @param {function} parse * @param {function} debug * @param {object} options * @private */ function read (req, res, next, parse, debug, options) { var length var opts = options var stream // flag as parsed req._body = true // read options var encoding = opts.encoding !== null ? opts.encoding : null var verify = opts.verify try { // get the content stream stream = contentstream(req, debug, opts.inflate) length = stream.length stream.length = undefined } catch (err) { return next(err) } // set raw-body options opts.length = length opts.encoding = verify ? null : encoding // assert charset is supported if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) { return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', { charset: encoding.toLowerCase(), type: 'charset.unsupported' })) } // read body debug('read body') getBody(stream, opts, function (error, body) { if (error) { var _error if (error.type === 'encoding.unsupported') { // echo back charset _error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', { charset: encoding.toLowerCase(), type: 'charset.unsupported' }) } else { // set status code on error _error = createError(400, error) } // unpipe from stream and destroy if (stream !== req) { unpipe(req) destroy(stream, true) } // read off entire request dump(req, function onfinished () { next(createError(400, _error)) }) return } // verify if (verify) { try { debug('verify body') verify(req, res, body, encoding) } catch (err) { next(createError(403, err, { body: body, type: err.type || 'entity.verify.failed' })) return } } // parse var str = body try { debug('parse body') str = typeof body !== 'string' && encoding !== null ? iconv.decode(body, encoding) : body req.body = parse(str) } catch (err) { next(createError(400, err, { body: str, type: err.type || 'entity.parse.failed' })) return } next() }) } /** * Get the content stream of the request. * * @param {object} req * @param {function} debug * @param {boolean} [inflate=true] * @return {object} * @api private */ function contentstream (req, debug, inflate) { var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase() var length = req.headers['content-length'] var stream debug('content-encoding "%s"', encoding) if (inflate === false && encoding !== 'identity') { throw createError(415, 'content encoding unsupported', { encoding: encoding, type: 'encoding.unsupported' }) } switch (encoding) { case 'deflate': stream = zlib.createInflate() debug('inflate body') req.pipe(stream) break case 'gzip': stream = zlib.createGunzip() debug('gunzip body') req.pipe(stream) break case 'identity': stream = req stream.length = length break default: throw createError(415, 'unsupported content encoding "' + encoding + '"', { encoding: encoding, type: 'encoding.unsupported' }) } return stream } /** * Dump the contents of a request. * * @param {object} req * @param {function} callback * @api private */ function dump (req, callback) { if (onFinished.isFinished(req)) { callback(null) } else { onFinished(req, callback) req.resume() } } body-parser-1.20.3/lib/types/000077500000000000000000000000001466767750300157635ustar00rootroot00000000000000body-parser-1.20.3/lib/types/json.js000066400000000000000000000122631466767750300172760ustar00rootroot00000000000000/*! * body-parser * Copyright(c) 2014 Jonathan Ong * Copyright(c) 2014-2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. * @private */ var bytes = require('bytes') var contentType = require('content-type') var createError = require('http-errors') var debug = require('debug')('body-parser:json') var read = require('../read') var typeis = require('type-is') /** * Module exports. */ module.exports = json /** * RegExp to match the first non-space in a string. * * Allowed whitespace is defined in RFC 7159: * * ws = *( * %x20 / ; Space * %x09 / ; Horizontal tab * %x0A / ; Line feed or New line * %x0D ) ; Carriage return */ var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex var JSON_SYNTAX_CHAR = '#' var JSON_SYNTAX_REGEXP = /#+/g /** * Create a middleware to parse JSON bodies. * * @param {object} [options] * @return {function} * @public */ function json (options) { var opts = options || {} var limit = typeof opts.limit !== 'number' ? bytes.parse(opts.limit || '100kb') : opts.limit var inflate = opts.inflate !== false var reviver = opts.reviver var strict = opts.strict !== false var type = opts.type || 'application/json' var verify = opts.verify || false if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') } // create the appropriate type checking function var shouldParse = typeof type !== 'function' ? typeChecker(type) : type function parse (body) { if (body.length === 0) { // special-case empty json body, as it's a common client-side mistake // TODO: maybe make this configurable or part of "strict" option return {} } if (strict) { var first = firstchar(body) if (first !== '{' && first !== '[') { debug('strict violation') throw createStrictSyntaxError(body, first) } } try { debug('parse json') return JSON.parse(body, reviver) } catch (e) { throw normalizeJsonSyntaxError(e, { message: e.message, stack: e.stack }) } } return function jsonParser (req, res, next) { if (req._body) { debug('body already parsed') next() return } req.body = req.body || {} // skip requests without bodies if (!typeis.hasBody(req)) { debug('skip empty body') next() return } debug('content-type %j', req.headers['content-type']) // determine if request should be parsed if (!shouldParse(req)) { debug('skip parsing') next() return } // assert charset per RFC 7159 sec 8.1 var charset = getCharset(req) || 'utf-8' if (charset.slice(0, 4) !== 'utf-') { debug('invalid charset') next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', { charset: charset, type: 'charset.unsupported' })) return } // read read(req, res, next, parse, debug, { encoding: charset, inflate: inflate, limit: limit, verify: verify }) } } /** * Create strict violation syntax error matching native error. * * @param {string} str * @param {string} char * @return {Error} * @private */ function createStrictSyntaxError (str, char) { var index = str.indexOf(char) var partial = '' if (index !== -1) { partial = str.substring(0, index) + JSON_SYNTAX_CHAR for (var i = index + 1; i < str.length; i++) { partial += JSON_SYNTAX_CHAR } } try { JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation') } catch (e) { return normalizeJsonSyntaxError(e, { message: e.message.replace(JSON_SYNTAX_REGEXP, function (placeholder) { return str.substring(index, index + placeholder.length) }), stack: e.stack }) } } /** * Get the first non-whitespace character in a string. * * @param {string} str * @return {function} * @private */ function firstchar (str) { var match = FIRST_CHAR_REGEXP.exec(str) return match ? match[1] : undefined } /** * Get the charset of a request. * * @param {object} req * @api private */ function getCharset (req) { try { return (contentType.parse(req).parameters.charset || '').toLowerCase() } catch (e) { return undefined } } /** * Normalize a SyntaxError for JSON.parse. * * @param {SyntaxError} error * @param {object} obj * @return {SyntaxError} */ function normalizeJsonSyntaxError (error, obj) { var keys = Object.getOwnPropertyNames(error) for (var i = 0; i < keys.length; i++) { var key = keys[i] if (key !== 'stack' && key !== 'message') { delete error[key] } } // replace stack before message for Node.js 0.10 and below error.stack = obj.stack.replace(error.message, obj.message) error.message = obj.message return error } /** * Get the simple type checker. * * @param {string} type * @return {function} */ function typeChecker (type) { return function checkType (req) { return Boolean(typeis(req, type)) } } body-parser-1.20.3/lib/types/raw.js000066400000000000000000000035341466767750300171170ustar00rootroot00000000000000/*! * body-parser * Copyright(c) 2014-2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. */ var bytes = require('bytes') var debug = require('debug')('body-parser:raw') var read = require('../read') var typeis = require('type-is') /** * Module exports. */ module.exports = raw /** * Create a middleware to parse raw bodies. * * @param {object} [options] * @return {function} * @api public */ function raw (options) { var opts = options || {} var inflate = opts.inflate !== false var limit = typeof opts.limit !== 'number' ? bytes.parse(opts.limit || '100kb') : opts.limit var type = opts.type || 'application/octet-stream' var verify = opts.verify || false if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') } // create the appropriate type checking function var shouldParse = typeof type !== 'function' ? typeChecker(type) : type function parse (buf) { return buf } return function rawParser (req, res, next) { if (req._body) { debug('body already parsed') next() return } req.body = req.body || {} // skip requests without bodies if (!typeis.hasBody(req)) { debug('skip empty body') next() return } debug('content-type %j', req.headers['content-type']) // determine if request should be parsed if (!shouldParse(req)) { debug('skip parsing') next() return } // read read(req, res, next, parse, debug, { encoding: null, inflate: inflate, limit: limit, verify: verify }) } } /** * Get the simple type checker. * * @param {string} type * @return {function} */ function typeChecker (type) { return function checkType (req) { return Boolean(typeis(req, type)) } } body-parser-1.20.3/lib/types/text.js000066400000000000000000000043551466767750300173140ustar00rootroot00000000000000/*! * body-parser * Copyright(c) 2014-2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. */ var bytes = require('bytes') var contentType = require('content-type') var debug = require('debug')('body-parser:text') var read = require('../read') var typeis = require('type-is') /** * Module exports. */ module.exports = text /** * Create a middleware to parse text bodies. * * @param {object} [options] * @return {function} * @api public */ function text (options) { var opts = options || {} var defaultCharset = opts.defaultCharset || 'utf-8' var inflate = opts.inflate !== false var limit = typeof opts.limit !== 'number' ? bytes.parse(opts.limit || '100kb') : opts.limit var type = opts.type || 'text/plain' var verify = opts.verify || false if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') } // create the appropriate type checking function var shouldParse = typeof type !== 'function' ? typeChecker(type) : type function parse (buf) { return buf } return function textParser (req, res, next) { if (req._body) { debug('body already parsed') next() return } req.body = req.body || {} // skip requests without bodies if (!typeis.hasBody(req)) { debug('skip empty body') next() return } debug('content-type %j', req.headers['content-type']) // determine if request should be parsed if (!shouldParse(req)) { debug('skip parsing') next() return } // get charset var charset = getCharset(req) || defaultCharset // read read(req, res, next, parse, debug, { encoding: charset, inflate: inflate, limit: limit, verify: verify }) } } /** * Get the charset of a request. * * @param {object} req * @api private */ function getCharset (req) { try { return (contentType.parse(req).parameters.charset || '').toLowerCase() } catch (e) { return undefined } } /** * Get the simple type checker. * * @param {string} type * @return {function} */ function typeChecker (type) { return function checkType (req) { return Boolean(typeis(req, type)) } } body-parser-1.20.3/lib/types/urlencoded.js000066400000000000000000000144041466767750300204500ustar00rootroot00000000000000/*! * body-parser * Copyright(c) 2014 Jonathan Ong * Copyright(c) 2014-2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. * @private */ var bytes = require('bytes') var contentType = require('content-type') var createError = require('http-errors') var debug = require('debug')('body-parser:urlencoded') var deprecate = require('depd')('body-parser') var read = require('../read') var typeis = require('type-is') /** * Module exports. */ module.exports = urlencoded /** * Cache of parser modules. */ var parsers = Object.create(null) /** * Create a middleware to parse urlencoded bodies. * * @param {object} [options] * @return {function} * @public */ function urlencoded (options) { var opts = options || {} // notice because option default will flip in next major if (opts.extended === undefined) { deprecate('undefined extended: provide extended option') } var extended = opts.extended !== false var inflate = opts.inflate !== false var limit = typeof opts.limit !== 'number' ? bytes.parse(opts.limit || '100kb') : opts.limit var type = opts.type || 'application/x-www-form-urlencoded' var verify = opts.verify || false var depth = typeof opts.depth !== 'number' ? Number(opts.depth || 32) : opts.depth if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') } // create the appropriate query parser var queryparse = extended ? extendedparser(opts) : simpleparser(opts) // create the appropriate type checking function var shouldParse = typeof type !== 'function' ? typeChecker(type) : type function parse (body) { return body.length ? queryparse(body) : {} } return function urlencodedParser (req, res, next) { if (req._body) { debug('body already parsed') next() return } req.body = req.body || {} // skip requests without bodies if (!typeis.hasBody(req)) { debug('skip empty body') next() return } debug('content-type %j', req.headers['content-type']) // determine if request should be parsed if (!shouldParse(req)) { debug('skip parsing') next() return } // assert charset var charset = getCharset(req) || 'utf-8' if (charset !== 'utf-8') { debug('invalid charset') next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', { charset: charset, type: 'charset.unsupported' })) return } // read read(req, res, next, parse, debug, { debug: debug, encoding: charset, inflate: inflate, limit: limit, verify: verify, depth: depth }) } } /** * Get the extended query parser. * * @param {object} options */ function extendedparser (options) { var parameterLimit = options.parameterLimit !== undefined ? options.parameterLimit : 1000 var depth = typeof options.depth !== 'number' ? Number(options.depth || 32) : options.depth var parse = parser('qs') if (isNaN(parameterLimit) || parameterLimit < 1) { throw new TypeError('option parameterLimit must be a positive number') } if (isNaN(depth) || depth < 0) { throw new TypeError('option depth must be a zero or a positive number') } if (isFinite(parameterLimit)) { parameterLimit = parameterLimit | 0 } return function queryparse (body) { var paramCount = parameterCount(body, parameterLimit) if (paramCount === undefined) { debug('too many parameters') throw createError(413, 'too many parameters', { type: 'parameters.too.many' }) } var arrayLimit = Math.max(100, paramCount) debug('parse extended urlencoding') try { return parse(body, { allowPrototypes: true, arrayLimit: arrayLimit, depth: depth, strictDepth: true, parameterLimit: parameterLimit }) } catch (err) { if (err instanceof RangeError) { throw createError(400, 'The input exceeded the depth', { type: 'querystring.parse.rangeError' }) } else { throw err } } } } /** * Get the charset of a request. * * @param {object} req * @api private */ function getCharset (req) { try { return (contentType.parse(req).parameters.charset || '').toLowerCase() } catch (e) { return undefined } } /** * Count the number of parameters, stopping once limit reached * * @param {string} body * @param {number} limit * @api private */ function parameterCount (body, limit) { var count = 0 var index = 0 while ((index = body.indexOf('&', index)) !== -1) { count++ index++ if (count === limit) { return undefined } } return count } /** * Get parser for module name dynamically. * * @param {string} name * @return {function} * @api private */ function parser (name) { var mod = parsers[name] if (mod !== undefined) { return mod.parse } // this uses a switch for static require analysis switch (name) { case 'qs': mod = require('qs') break case 'querystring': mod = require('querystring') break } // store to prevent invoking require() parsers[name] = mod return mod.parse } /** * Get the simple query parser. * * @param {object} options */ function simpleparser (options) { var parameterLimit = options.parameterLimit !== undefined ? options.parameterLimit : 1000 var parse = parser('querystring') if (isNaN(parameterLimit) || parameterLimit < 1) { throw new TypeError('option parameterLimit must be a positive number') } if (isFinite(parameterLimit)) { parameterLimit = parameterLimit | 0 } return function queryparse (body) { var paramCount = parameterCount(body, parameterLimit) if (paramCount === undefined) { debug('too many parameters') throw createError(413, 'too many parameters', { type: 'parameters.too.many' }) } debug('parse urlencoding') return parse(body, undefined, undefined, { maxKeys: parameterLimit }) } } /** * Get the simple type checker. * * @param {string} type * @return {function} */ function typeChecker (type) { return function checkType (req) { return Boolean(typeis(req, type)) } } body-parser-1.20.3/package.json000066400000000000000000000027001466767750300163360ustar00rootroot00000000000000{ "name": "body-parser", "description": "Node.js body parsing middleware", "version": "1.20.3", "contributors": [ "Douglas Christopher Wilson ", "Jonathan Ong (http://jongleberry.com)" ], "license": "MIT", "repository": "expressjs/body-parser", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, "devDependencies": { "eslint": "8.34.0", "eslint-config-standard": "14.1.1", "eslint-plugin-import": "2.27.5", "eslint-plugin-markdown": "3.0.0", "eslint-plugin-node": "11.1.0", "eslint-plugin-promise": "6.1.1", "eslint-plugin-standard": "4.1.0", "methods": "1.1.2", "mocha": "10.2.0", "nyc": "15.1.0", "safe-buffer": "5.2.1", "supertest": "6.3.3" }, "files": [ "lib/", "LICENSE", "HISTORY.md", "SECURITY.md", "index.js" ], "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" }, "scripts": { "lint": "eslint .", "test": "mocha --require test/support/env --reporter spec --check-leaks --bail test/", "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test" } } body-parser-1.20.3/test/000077500000000000000000000000001466767750300150305ustar00rootroot00000000000000body-parser-1.20.3/test/.eslintrc.yml000066400000000000000000000000231466767750300174470ustar00rootroot00000000000000env: mocha: true body-parser-1.20.3/test/body-parser.js000066400000000000000000000115071466767750300176210ustar00rootroot00000000000000 var http = require('http') var methods = require('methods') var request = require('supertest') var bodyParser = require('..') describe('bodyParser()', function () { before(function () { this.server = createServer() }) it('should default to {}', function (done) { request(this.server) .post('/') .expect(200, '{}', done) }) it('should parse JSON', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) it('should parse x-www-form-urlencoded', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) it('should handle duplicated middleware', function (done) { var _bodyParser = bodyParser() var server = http.createServer(function (req, res) { _bodyParser(req, res, function (err0) { _bodyParser(req, res, function (err1) { var err = err0 || err1 res.statusCode = err ? (err.status || 500) : 200 res.end(err ? err.message : JSON.stringify(req.body)) }) }) }) request(server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) describe('http methods', function () { before(function () { var _bodyParser = bodyParser() this.server = http.createServer(function (req, res) { _bodyParser(req, res, function (err) { if (err) { res.statusCode = 500 res.end(err.message) return } res.statusCode = req.headers['x-expect-method'] === req.method ? req.body.user === 'tobi' ? 201 : 400 : 405 res.end() }) }) }) function getMajorVersion (versionString) { return versionString.split('.')[0] } function shouldSkipQuery (versionString) { // Skipping HTTP QUERY tests on Node 21, it is reported in http.METHODS on 21.7.2 but not supported // update this implementation to run on supported versions of 21 once they exist // upstream tracking https://github.com/nodejs/node/issues/51562 // express tracking issue: https://github.com/expressjs/express/issues/5615 return getMajorVersion(versionString) === '21' } methods.slice().sort().forEach(function (method) { if (method === 'connect') return it('should support ' + method.toUpperCase() + ' requests', function (done) { if (method === 'query' && shouldSkipQuery(process.versions.node)) { this.skip() } request(this.server)[method]('/') .set('Content-Type', 'application/json') .set('Content-Length', '15') .set('X-Expect-Method', method.toUpperCase()) .send('{"user":"tobi"}') .expect(201, done) }) }) }) describe('with type option', function () { before(function () { this.server = createServer({ limit: '1mb', type: 'application/octet-stream' }) }) it('should parse JSON', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) it('should parse x-www-form-urlencoded', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) }) describe('with verify option', function () { it('should apply to json', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x20) throw new Error('no leading space') } }) request(server) .post('/') .set('Content-Type', 'application/json') .send(' {"user":"tobi"}') .expect(403, '[entity.verify.failed] no leading space', done) }) it('should apply to urlencoded', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x20) throw new Error('no leading space') } }) request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(' user=tobi') .expect(403, '[entity.verify.failed] no leading space', done) }) }) }) function createServer (opts) { var _bodyParser = bodyParser(opts) return http.createServer(function (req, res) { _bodyParser(req, res, function (err) { res.statusCode = err ? (err.status || 500) : 200 res.end(err ? ('[' + err.type + '] ' + err.message) : JSON.stringify(req.body)) }) }) } body-parser-1.20.3/test/json.js000066400000000000000000000574741466767750300163600ustar00rootroot00000000000000 var assert = require('assert') var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var http = require('http') var request = require('supertest') var bodyParser = require('..') var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' ? describe : describe.skip describe('bodyParser.json()', function () { it('should parse JSON', function (done) { request(createServer()) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) it('should handle Content-Length: 0', function (done) { request(createServer()) .get('/') .set('Content-Type', 'application/json') .set('Content-Length', '0') .expect(200, '{}', done) }) it('should handle empty message-body', function (done) { request(createServer()) .get('/') .set('Content-Type', 'application/json') .set('Transfer-Encoding', 'chunked') .expect(200, '{}', done) }) it('should handle no message-body', function (done) { request(createServer()) .get('/') .set('Content-Type', 'application/json') .unset('Transfer-Encoding') .expect(200, '{}', done) }) it('should 400 when only whitespace', function (done) { request(createServer()) .post('/') .set('Content-Type', 'application/json') .send(' \n') .expect(400, '[entity.parse.failed] ' + parseError(' '), done) }) it('should 400 when invalid content-length', function (done) { var jsonParser = bodyParser.json() var server = createServer(function (req, res, next) { req.headers['content-length'] = '20' // bad length jsonParser(req, res, next) }) request(server) .post('/') .set('Content-Type', 'application/json') .send('{"str":') .expect(400, /content length/, done) }) it('should 500 if stream not readable', function (done) { var jsonParser = bodyParser.json() var server = createServer(function (req, res, next) { req.on('end', function () { jsonParser(req, res, next) }) req.resume() }) request(server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(500, '[stream.not.readable] stream is not readable', done) }) it('should handle duplicated middleware', function (done) { var jsonParser = bodyParser.json() var server = createServer(function (req, res, next) { jsonParser(req, res, function (err) { if (err) return next(err) jsonParser(req, res, next) }) }) request(server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) describe('when JSON is invalid', function () { before(function () { this.server = createServer() }) it('should 400 for bad token', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{:') .expect(400, '[entity.parse.failed] ' + parseError('{:'), done) }) it('should 400 for incomplete', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{"user"') .expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done) }) it('should include original body on error object', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .set('X-Error-Property', 'body') .send(' {"user"') .expect(400, ' {"user"', done) }) }) describe('with limit option', function () { it('should 413 when over limit with Content-Length', function (done) { var buf = Buffer.alloc(1024, '.') request(createServer({ limit: '1kb' })) .post('/') .set('Content-Type', 'application/json') .set('Content-Length', '1034') .send(JSON.stringify({ str: buf.toString() })) .expect(413, '[entity.too.large] request entity too large', done) }) it('should 413 when over limit with chunked encoding', function (done) { var buf = Buffer.alloc(1024, '.') var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Type', 'application/json') test.set('Transfer-Encoding', 'chunked') test.write('{"str":') test.write('"' + buf.toString() + '"}') test.expect(413, done) }) it('should 413 when inflated body over limit', function (done) { var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex')) test.expect(413, done) }) it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1024, '.') request(createServer({ limit: 1024 })) .post('/') .set('Content-Type', 'application/json') .send(JSON.stringify({ str: buf.toString() })) .expect(413, done) }) it('should not change when options altered', function (done) { var buf = Buffer.alloc(1024, '.') var options = { limit: '1kb' } var server = createServer(options) options.limit = '100kb' request(server) .post('/') .set('Content-Type', 'application/json') .send(JSON.stringify({ str: buf.toString() })) .expect(413, done) }) it('should not hang response', function (done) { var buf = Buffer.alloc(10240, '.') var server = createServer({ limit: '8kb' }) var test = request(server).post('/') test.set('Content-Type', 'application/json') test.write(buf) test.write(buf) test.write(buf) test.expect(413, done) }) it('should not error when inflating', function (done) { var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex')) test.expect(413, done) }) }) describe('with inflate option', function () { describe('when false', function () { before(function () { this.server = createServer({ inflate: false }) }) it('should not accept content-encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) describe('when true', function () { before(function () { this.server = createServer({ inflate: true }) }) it('should accept content-encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) test.expect(200, '{"name":"论"}', done) }) }) }) describe('with strict option', function () { describe('when undefined', function () { before(function () { this.server = createServer() }) it('should 400 on primitives', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('true') .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done) }) }) describe('when false', function () { before(function () { this.server = createServer({ strict: false }) }) it('should parse primitives', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('true') .expect(200, 'true', done) }) }) describe('when true', function () { before(function () { this.server = createServer({ strict: true }) }) it('should not parse primitives', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('true') .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done) }) it('should not parse primitives with leading whitespaces', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send(' true') .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done) }) it('should allow leading whitespaces in JSON', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send(' { "user": "tobi" }') .expect(200, '{"user":"tobi"}', done) }) it('should include correct message in stack trace', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .set('X-Error-Property', 'stack') .send('true') .expect(400) .expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't'))) .end(done) }) }) }) describe('with type option', function () { describe('when "application/vnd.api+json"', function () { before(function () { this.server = createServer({ type: 'application/vnd.api+json' }) }) it('should parse JSON for custom type', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/vnd.api+json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) it('should ignore standard type', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200, '{}', done) }) }) describe('when ["application/json", "application/vnd.api+json"]', function () { before(function () { this.server = createServer({ type: ['application/json', 'application/vnd.api+json'] }) }) it('should parse JSON for "application/json"', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) it('should parse JSON for "application/vnd.api+json"', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/vnd.api+json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) it('should ignore "application/x-json"', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-json') .send('{"user":"tobi"}') .expect(200, '{}', done) }) }) describe('when a function', function () { it('should parse when truthy value returned', function (done) { var server = createServer({ type: accept }) function accept (req) { return req.headers['content-type'] === 'application/vnd.api+json' } request(server) .post('/') .set('Content-Type', 'application/vnd.api+json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) it('should work without content-type', function (done) { var server = createServer({ type: accept }) function accept (req) { return true } var test = request(server).post('/') test.write('{"user":"tobi"}') test.expect(200, '{"user":"tobi"}', done) }) it('should not invoke without a body', function (done) { var server = createServer({ type: accept }) function accept (req) { throw new Error('oops!') } request(server) .get('/') .expect(200, done) }) }) }) describe('with verify option', function () { it('should assert value if function', function () { assert.throws(createServer.bind(null, { verify: 'lol' }), /TypeError: option verify must be function/) }) it('should error from verify', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x5b) throw new Error('no arrays') } }) request(server) .post('/') .set('Content-Type', 'application/json') .send('["tobi"]') .expect(403, '[entity.verify.failed] no arrays', done) }) it('should allow custom codes', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] !== 0x5b) return var err = new Error('no arrays') err.status = 400 throw err } }) request(server) .post('/') .set('Content-Type', 'application/json') .send('["tobi"]') .expect(400, '[entity.verify.failed] no arrays', done) }) it('should allow custom type', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] !== 0x5b) return var err = new Error('no arrays') err.type = 'foo.bar' throw err } }) request(server) .post('/') .set('Content-Type', 'application/json') .send('["tobi"]') .expect(403, '[foo.bar] no arrays', done) }) it('should include original body on error object', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x5b) throw new Error('no arrays') } }) request(server) .post('/') .set('Content-Type', 'application/json') .set('X-Error-Property', 'body') .send('["tobi"]') .expect(403, '["tobi"]', done) }) it('should allow pass-through', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x5b) throw new Error('no arrays') } }) request(server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200, '{"user":"tobi"}', done) }) it('should work with different charsets', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x5b) throw new Error('no arrays') } }) var test = request(server).post('/') test.set('Content-Type', 'application/json; charset=utf-16') test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should 415 on unknown charset prior to verify', function (done) { var server = createServer({ verify: function (req, res, buf) { throw new Error('unexpected verify call') } }) var test = request(server).post('/') test.set('Content-Type', 'application/json; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) }) }) describeAsyncHooks('async local storage', function () { before(function () { var jsonParser = bodyParser.json() var store = { foo: 'bar' } this.server = createServer(function (req, res, next) { var asyncLocalStorage = new asyncHooks.AsyncLocalStorage() asyncLocalStorage.run(store, function () { jsonParser(req, res, function (err) { var local = asyncLocalStorage.getStore() if (local) { res.setHeader('x-store-foo', String(local.foo)) } next(err) }) }) }) }) it('should presist store', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') .expect(200) .expect('x-store-foo', 'bar') .expect('{"user":"tobi"}') .end(done) }) it('should presist store when unmatched content-type', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/fizzbuzz') .send('buzz') .expect(200) .expect('x-store-foo', 'bar') .expect('{}') .end(done) }) it('should presist store when inflated', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) test.expect(200) test.expect('x-store-foo', 'bar') test.expect('{"name":"论"}') test.end(done) }) it('should presist store when inflate error', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) test.expect(400) test.expect('x-store-foo', 'bar') test.end(done) }) it('should presist store when parse error', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{"user":') .expect(400) .expect('x-store-foo', 'bar') .end(done) }) it('should presist store when limit exceeded', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/json') .send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}') .expect(413) .expect('x-store-foo', 'bar') .end(done) }) }) describe('charset', function () { before(function () { this.server = createServer() }) it('should parse utf-8', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/json; charset=utf-8') test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should parse utf-16', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/json; charset=utf-16') test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should parse when content-length != char length', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/json; charset=utf-8') test.set('Content-Length', '13') test.write(Buffer.from('7b2274657374223a22c3a5227d', 'hex')) test.expect(200, '{"test":"å"}', done) }) it('should default to utf-8', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/json') test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should fail on unknown charset', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/json; charset=koi8-r') test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex')) test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done) }) }) describe('encoding', function () { before(function () { this.server = createServer({ limit: '1kb' }) }) it('should parse without encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/json') test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should support identity encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'identity') test.set('Content-Type', 'application/json') test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should support gzip encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should support deflate encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'deflate') test.set('Content-Type', 'application/json') test.write(Buffer.from('789cab56ca4bcc4d55b2527ab16e97522d00274505ac', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should be case-insensitive', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'GZIP') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should 415 on unknown encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/json') test.write(Buffer.from('000000000000', 'hex')) test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) it('should 400 on malformed encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) test.expect(400, done) }) it('should 413 when inflated value exceeds limit', function (done) { // gzip'd data exceeds 1kb, but deflated below 1kb var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000', 'hex')) test.write(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')) test.write(Buffer.from('0000000000000000004f0625b3b71650c30000', 'hex')) test.expect(413, done) }) }) }) function createServer (opts) { var _bodyParser = typeof opts !== 'function' ? bodyParser.json(opts) : opts return http.createServer(function (req, res) { _bodyParser(req, res, function (err) { if (err) { res.statusCode = err.status || 500 res.end(req.headers['x-error-property'] ? err[req.headers['x-error-property']] : ('[' + err.type + '] ' + err.message)) } else { res.statusCode = 200 res.end(JSON.stringify(req.body)) } }) }) } function parseError (str) { try { JSON.parse(str); throw new SyntaxError('strict violation') } catch (e) { return e.message } } function shouldContainInBody (str) { return function (res) { assert.ok(res.text.indexOf(str) !== -1, 'expected \'' + res.text + '\' to contain \'' + str + '\'') } } function tryRequire (name) { try { return require(name) } catch (e) { return {} } } body-parser-1.20.3/test/raw.js000066400000000000000000000401151466767750300161600ustar00rootroot00000000000000 var assert = require('assert') var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var http = require('http') var request = require('supertest') var bodyParser = require('..') var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' ? describe : describe.skip describe('bodyParser.raw()', function () { before(function () { this.server = createServer() }) it('should parse application/octet-stream', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/octet-stream') .send('the user is tobi') .expect(200, 'buf:746865207573657220697320746f6269', done) }) it('should 400 when invalid content-length', function (done) { var rawParser = bodyParser.raw() var server = createServer(function (req, res, next) { req.headers['content-length'] = '20' // bad length rawParser(req, res, next) }) request(server) .post('/') .set('Content-Type', 'application/octet-stream') .send('stuff') .expect(400, /content length/, done) }) it('should handle Content-Length: 0', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/octet-stream') .set('Content-Length', '0') .expect(200, 'buf:', done) }) it('should handle empty message-body', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/octet-stream') .set('Transfer-Encoding', 'chunked') .send('') .expect(200, 'buf:', done) }) it('should 500 if stream not readable', function (done) { var rawParser = bodyParser.raw() var server = createServer(function (req, res, next) { req.on('end', function () { rawParser(req, res, next) }) req.resume() }) request(server) .post('/') .set('Content-Type', 'application/octet-stream') .send('the user is tobi') .expect(500, '[stream.not.readable] stream is not readable', done) }) it('should handle duplicated middleware', function (done) { var rawParser = bodyParser.raw() var server = createServer(function (req, res, next) { rawParser(req, res, function (err) { if (err) return next(err) rawParser(req, res, next) }) }) request(server) .post('/') .set('Content-Type', 'application/octet-stream') .send('the user is tobi') .expect(200, 'buf:746865207573657220697320746f6269', done) }) describe('with limit option', function () { it('should 413 when over limit with Content-Length', function (done) { var buf = Buffer.alloc(1028, '.') var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Type', 'application/octet-stream') test.set('Content-Length', '1028') test.write(buf) test.expect(413, done) }) it('should 413 when over limit with chunked encoding', function (done) { var buf = Buffer.alloc(1028, '.') var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Type', 'application/octet-stream') test.set('Transfer-Encoding', 'chunked') test.write(buf) test.expect(413, done) }) it('should 413 when inflated body over limit', function (done) { var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex')) test.expect(413, done) }) it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1028, '.') var server = createServer({ limit: 1024 }) var test = request(server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(buf) test.expect(413, done) }) it('should not change when options altered', function (done) { var buf = Buffer.alloc(1028, '.') var options = { limit: '1kb' } var server = createServer(options) options.limit = '100kb' var test = request(server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(buf) test.expect(413, done) }) it('should not hang response', function (done) { var buf = Buffer.alloc(10240, '.') var server = createServer({ limit: '8kb' }) var test = request(server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(buf) test.write(buf) test.write(buf) test.expect(413, done) }) it('should not error when inflating', function (done) { var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex')) test.expect(413, done) }) }) describe('with inflate option', function () { describe('when false', function () { before(function () { this.server = createServer({ inflate: false }) }) it('should not accept content-encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) describe('when true', function () { before(function () { this.server = createServer({ inflate: true }) }) it('should accept content-encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(200, 'buf:6e616d653de8aeba', done) }) }) }) describe('with type option', function () { describe('when "application/vnd+octets"', function () { before(function () { this.server = createServer({ type: 'application/vnd+octets' }) }) it('should parse for custom type', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/vnd+octets') test.write(Buffer.from('000102', 'hex')) test.expect(200, 'buf:000102', done) }) it('should ignore standard type', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) test.expect(200, '{}', done) }) }) describe('when ["application/octet-stream", "application/vnd+octets"]', function () { before(function () { this.server = createServer({ type: ['application/octet-stream', 'application/vnd+octets'] }) }) it('should parse "application/octet-stream"', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) test.expect(200, 'buf:000102', done) }) it('should parse "application/vnd+octets"', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/vnd+octets') test.write(Buffer.from('000102', 'hex')) test.expect(200, 'buf:000102', done) }) it('should ignore "application/x-foo"', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/x-foo') test.write(Buffer.from('000102', 'hex')) test.expect(200, '{}', done) }) }) describe('when a function', function () { it('should parse when truthy value returned', function (done) { var server = createServer({ type: accept }) function accept (req) { return req.headers['content-type'] === 'application/vnd.octet' } var test = request(server).post('/') test.set('Content-Type', 'application/vnd.octet') test.write(Buffer.from('000102', 'hex')) test.expect(200, 'buf:000102', done) }) it('should work without content-type', function (done) { var server = createServer({ type: accept }) function accept (req) { return true } var test = request(server).post('/') test.write(Buffer.from('000102', 'hex')) test.expect(200, 'buf:000102', done) }) it('should not invoke without a body', function (done) { var server = createServer({ type: accept }) function accept (req) { throw new Error('oops!') } request(server) .get('/') .expect(200, done) }) }) }) describe('with verify option', function () { it('should assert value is function', function () { assert.throws(createServer.bind(null, { verify: 'lol' }), /TypeError: option verify must be function/) }) it('should error from verify', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x00) throw new Error('no leading null') } }) var test = request(server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) test.expect(403, '[entity.verify.failed] no leading null', done) }) it('should allow custom codes', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] !== 0x00) return var err = new Error('no leading null') err.status = 400 throw err } }) var test = request(server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) test.expect(400, '[entity.verify.failed] no leading null', done) }) it('should allow pass-through', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x00) throw new Error('no leading null') } }) var test = request(server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('0102', 'hex')) test.expect(200, 'buf:0102', done) }) }) describeAsyncHooks('async local storage', function () { before(function () { var rawParser = bodyParser.raw() var store = { foo: 'bar' } this.server = createServer(function (req, res, next) { var asyncLocalStorage = new asyncHooks.AsyncLocalStorage() asyncLocalStorage.run(store, function () { rawParser(req, res, function (err) { var local = asyncLocalStorage.getStore() if (local) { res.setHeader('x-store-foo', String(local.foo)) } next(err) }) }) }) }) it('should presist store', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/octet-stream') .send('the user is tobi') .expect(200) .expect('x-store-foo', 'bar') .expect('buf:746865207573657220697320746f6269') .end(done) }) it('should presist store when unmatched content-type', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/fizzbuzz') .send('buzz') .expect(200) .expect('x-store-foo', 'bar') .expect('{}') .end(done) }) it('should presist store when inflated', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(200) test.expect('x-store-foo', 'bar') test.expect('buf:6e616d653de8aeba') test.end(done) }) it('should presist store when inflate error', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex')) test.expect(400) test.expect('x-store-foo', 'bar') test.end(done) }) it('should presist store when limit exceeded', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/octet-stream') .send('the user is ' + Buffer.alloc(1024 * 100, '.').toString()) .expect(413) .expect('x-store-foo', 'bar') .end(done) }) }) describe('charset', function () { before(function () { this.server = createServer() }) it('should ignore charset', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/octet-stream; charset=utf-8') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, 'buf:6e616d6520697320e8aeba', done) }) }) describe('encoding', function () { before(function () { this.server = createServer({ limit: '10kb' }) }) it('should parse without encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('6e616d653de8aeba', 'hex')) test.expect(200, 'buf:6e616d653de8aeba', done) }) it('should support identity encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'identity') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('6e616d653de8aeba', 'hex')) test.expect(200, 'buf:6e616d653de8aeba', done) }) it('should support gzip encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(200, 'buf:6e616d653de8aeba', done) }) it('should support deflate encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'deflate') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex')) test.expect(200, 'buf:6e616d653de8aeba', done) }) it('should be case-insensitive', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'GZIP') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(200, 'buf:6e616d653de8aeba', done) }) it('should 415 on unknown encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000000000000', 'hex')) test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) function createServer (opts) { var _bodyParser = typeof opts !== 'function' ? bodyParser.raw(opts) : opts return http.createServer(function (req, res) { _bodyParser(req, res, function (err) { if (err) { res.statusCode = err.status || 500 res.end('[' + err.type + '] ' + err.message) return } if (Buffer.isBuffer(req.body)) { res.end('buf:' + req.body.toString('hex')) return } res.end(JSON.stringify(req.body)) }) }) } function tryRequire (name) { try { return require(name) } catch (e) { return {} } } body-parser-1.20.3/test/support/000077500000000000000000000000001466767750300165445ustar00rootroot00000000000000body-parser-1.20.3/test/support/env.js000066400000000000000000000000541466767750300176710ustar00rootroot00000000000000 process.env.NO_DEPRECATION = 'body-parser' body-parser-1.20.3/test/text.js000066400000000000000000000427021466767750300163570ustar00rootroot00000000000000 var assert = require('assert') var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var http = require('http') var request = require('supertest') var bodyParser = require('..') var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' ? describe : describe.skip describe('bodyParser.text()', function () { before(function () { this.server = createServer() }) it('should parse text/plain', function (done) { request(this.server) .post('/') .set('Content-Type', 'text/plain') .send('user is tobi') .expect(200, '"user is tobi"', done) }) it('should 400 when invalid content-length', function (done) { var textParser = bodyParser.text() var server = createServer(function (req, res, next) { req.headers['content-length'] = '20' // bad length textParser(req, res, next) }) request(server) .post('/') .set('Content-Type', 'text/plain') .send('user') .expect(400, /content length/, done) }) it('should handle Content-Length: 0', function (done) { request(createServer({ limit: '1kb' })) .post('/') .set('Content-Type', 'text/plain') .set('Content-Length', '0') .expect(200, '""', done) }) it('should handle empty message-body', function (done) { request(createServer({ limit: '1kb' })) .post('/') .set('Content-Type', 'text/plain') .set('Transfer-Encoding', 'chunked') .send('') .expect(200, '""', done) }) it('should 500 if stream not readable', function (done) { var textParser = bodyParser.text() var server = createServer(function (req, res, next) { req.on('end', function () { textParser(req, res, next) }) req.resume() }) request(server) .post('/') .set('Content-Type', 'text/plain') .send('user is tobi') .expect(500, '[stream.not.readable] stream is not readable', done) }) it('should handle duplicated middleware', function (done) { var textParser = bodyParser.text() var server = createServer(function (req, res, next) { textParser(req, res, function (err) { if (err) return next(err) textParser(req, res, next) }) }) request(server) .post('/') .set('Content-Type', 'text/plain') .send('user is tobi') .expect(200, '"user is tobi"', done) }) describe('with defaultCharset option', function () { it('should change default charset', function (done) { var server = createServer({ defaultCharset: 'koi8-r' }) var test = request(server).post('/') test.set('Content-Type', 'text/plain') test.write(Buffer.from('6e616d6520697320cec5d4', 'hex')) test.expect(200, '"name is нет"', done) }) it('should honor content-type charset', function (done) { var server = createServer({ defaultCharset: 'koi8-r' }) var test = request(server).post('/') test.set('Content-Type', 'text/plain; charset=utf-8') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, '"name is 论"', done) }) }) describe('with limit option', function () { it('should 413 when over limit with Content-Length', function (done) { var buf = Buffer.alloc(1028, '.') request(createServer({ limit: '1kb' })) .post('/') .set('Content-Type', 'text/plain') .set('Content-Length', '1028') .send(buf.toString()) .expect(413, done) }) it('should 413 when over limit with chunked encoding', function (done) { var buf = Buffer.alloc(1028, '.') var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Type', 'text/plain') test.set('Transfer-Encoding', 'chunked') test.write(buf.toString()) test.expect(413, done) }) it('should 413 when inflated body over limit', function (done) { var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex')) test.expect(413, done) }) it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1028, '.') request(createServer({ limit: 1024 })) .post('/') .set('Content-Type', 'text/plain') .send(buf.toString()) .expect(413, done) }) it('should not change when options altered', function (done) { var buf = Buffer.alloc(1028, '.') var options = { limit: '1kb' } var server = createServer(options) options.limit = '100kb' request(server) .post('/') .set('Content-Type', 'text/plain') .send(buf.toString()) .expect(413, done) }) it('should not hang response', function (done) { var buf = Buffer.alloc(10240, '.') var server = createServer({ limit: '8kb' }) var test = request(server).post('/') test.set('Content-Type', 'text/plain') test.write(buf) test.write(buf) test.write(buf) test.expect(413, done) }) it('should not error when inflating', function (done) { var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a1470404', 'hex')) setTimeout(function () { test.expect(413, done) }, 100) }) }) describe('with inflate option', function () { describe('when false', function () { before(function () { this.server = createServer({ inflate: false }) }) it('should not accept content-encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) describe('when true', function () { before(function () { this.server = createServer({ inflate: true }) }) it('should accept content-encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) test.expect(200, '"name is 论"', done) }) }) }) describe('with type option', function () { describe('when "text/html"', function () { before(function () { this.server = createServer({ type: 'text/html' }) }) it('should parse for custom type', function (done) { request(this.server) .post('/') .set('Content-Type', 'text/html') .send('tobi') .expect(200, '"tobi"', done) }) it('should ignore standard type', function (done) { request(this.server) .post('/') .set('Content-Type', 'text/plain') .send('user is tobi') .expect(200, '{}', done) }) }) describe('when ["text/html", "text/plain"]', function () { before(function () { this.server = createServer({ type: ['text/html', 'text/plain'] }) }) it('should parse "text/html"', function (done) { request(this.server) .post('/') .set('Content-Type', 'text/html') .send('tobi') .expect(200, '"tobi"', done) }) it('should parse "text/plain"', function (done) { request(this.server) .post('/') .set('Content-Type', 'text/plain') .send('tobi') .expect(200, '"tobi"', done) }) it('should ignore "text/xml"', function (done) { request(this.server) .post('/') .set('Content-Type', 'text/xml') .send('tobi') .expect(200, '{}', done) }) }) describe('when a function', function () { it('should parse when truthy value returned', function (done) { var server = createServer({ type: accept }) function accept (req) { return req.headers['content-type'] === 'text/vnd.something' } request(server) .post('/') .set('Content-Type', 'text/vnd.something') .send('user is tobi') .expect(200, '"user is tobi"', done) }) it('should work without content-type', function (done) { var server = createServer({ type: accept }) function accept (req) { return true } var test = request(server).post('/') test.write('user is tobi') test.expect(200, '"user is tobi"', done) }) it('should not invoke without a body', function (done) { var server = createServer({ type: accept }) function accept (req) { throw new Error('oops!') } request(server) .get('/') .expect(200, done) }) }) }) describe('with verify option', function () { it('should assert value is function', function () { assert.throws(createServer.bind(null, { verify: 'lol' }), /TypeError: option verify must be function/) }) it('should error from verify', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x20) throw new Error('no leading space') } }) request(server) .post('/') .set('Content-Type', 'text/plain') .send(' user is tobi') .expect(403, '[entity.verify.failed] no leading space', done) }) it('should allow custom codes', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] !== 0x20) return var err = new Error('no leading space') err.status = 400 throw err } }) request(server) .post('/') .set('Content-Type', 'text/plain') .send(' user is tobi') .expect(400, '[entity.verify.failed] no leading space', done) }) it('should allow pass-through', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x20) throw new Error('no leading space') } }) request(server) .post('/') .set('Content-Type', 'text/plain') .send('user is tobi') .expect(200, '"user is tobi"', done) }) it('should 415 on unknown charset prior to verify', function (done) { var server = createServer({ verify: function (req, res, buf) { throw new Error('unexpected verify call') } }) var test = request(server).post('/') test.set('Content-Type', 'text/plain; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) }) }) describeAsyncHooks('async local storage', function () { before(function () { var textParser = bodyParser.text() var store = { foo: 'bar' } this.server = createServer(function (req, res, next) { var asyncLocalStorage = new asyncHooks.AsyncLocalStorage() asyncLocalStorage.run(store, function () { textParser(req, res, function (err) { var local = asyncLocalStorage.getStore() if (local) { res.setHeader('x-store-foo', String(local.foo)) } next(err) }) }) }) }) it('should presist store', function (done) { request(this.server) .post('/') .set('Content-Type', 'text/plain') .send('user is tobi') .expect(200) .expect('x-store-foo', 'bar') .expect('"user is tobi"') .end(done) }) it('should presist store when unmatched content-type', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/fizzbuzz') .send('buzz') .expect(200) .expect('x-store-foo', 'bar') .expect('{}') .end(done) }) it('should presist store when inflated', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) test.expect(200) test.expect('x-store-foo', 'bar') test.expect('"name is 论"') test.end(done) }) it('should presist store when inflate error', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000', 'hex')) test.expect(400) test.expect('x-store-foo', 'bar') test.end(done) }) it('should presist store when limit exceeded', function (done) { request(this.server) .post('/') .set('Content-Type', 'text/plain') .send('user is ' + Buffer.alloc(1024 * 100, '.').toString()) .expect(413) .expect('x-store-foo', 'bar') .end(done) }) }) describe('charset', function () { before(function () { this.server = createServer() }) it('should parse utf-8', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'text/plain; charset=utf-8') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, '"name is 论"', done) }) it('should parse codepage charsets', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'text/plain; charset=koi8-r') test.write(Buffer.from('6e616d6520697320cec5d4', 'hex')) test.expect(200, '"name is нет"', done) }) it('should parse when content-length != char length', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'text/plain; charset=utf-8') test.set('Content-Length', '11') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, '"name is 论"', done) }) it('should default to utf-8', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'text/plain') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, '"name is 论"', done) }) it('should 415 on unknown charset', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'text/plain; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) }) }) describe('encoding', function () { before(function () { this.server = createServer({ limit: '10kb' }) }) it('should parse without encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'text/plain') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, '"name is 论"', done) }) it('should support identity encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'identity') test.set('Content-Type', 'text/plain') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, '"name is 论"', done) }) it('should support gzip encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) test.expect(200, '"name is 论"', done) }) it('should support deflate encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'deflate') test.set('Content-Type', 'text/plain') test.write(Buffer.from('789ccb4bcc4d55c82c5678b16e17001a6f050e', 'hex')) test.expect(200, '"name is 论"', done) }) it('should be case-insensitive', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'GZIP') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) test.expect(200, '"name is 论"', done) }) it('should 415 on unknown encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'text/plain') test.write(Buffer.from('000000000000', 'hex')) test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) function createServer (opts) { var _bodyParser = typeof opts !== 'function' ? bodyParser.text(opts) : opts return http.createServer(function (req, res) { _bodyParser(req, res, function (err) { res.statusCode = err ? (err.status || 500) : 200 res.end(err ? ('[' + err.type + '] ' + err.message) : JSON.stringify(req.body)) }) }) } function tryRequire (name) { try { return require(name) } catch (e) { return {} } } body-parser-1.20.3/test/urlencoded.js000066400000000000000000000727311466767750300175240ustar00rootroot00000000000000 var assert = require('assert') var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var http = require('http') var request = require('supertest') var bodyParser = require('..') var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' ? describe : describe.skip describe('bodyParser.urlencoded()', function () { before(function () { this.server = createServer() }) it('should parse x-www-form-urlencoded', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) it('should 400 when invalid content-length', function (done) { var urlencodedParser = bodyParser.urlencoded() var server = createServer(function (req, res, next) { req.headers['content-length'] = '20' // bad length urlencodedParser(req, res, next) }) request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('str=') .expect(400, /content length/, done) }) it('should handle Content-Length: 0', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .set('Content-Length', '0') .send('') .expect(200, '{}', done) }) it('should handle empty message-body', function (done) { request(createServer({ limit: '1kb' })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .set('Transfer-Encoding', 'chunked') .send('') .expect(200, '{}', done) }) it('should 500 if stream not readable', function (done) { var urlencodedParser = bodyParser.urlencoded() var server = createServer(function (req, res, next) { req.on('end', function () { urlencodedParser(req, res, next) }) req.resume() }) request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(500, '[stream.not.readable] stream is not readable', done) }) it('should handle duplicated middleware', function (done) { var urlencodedParser = bodyParser.urlencoded() var server = createServer(function (req, res, next) { urlencodedParser(req, res, function (err) { if (err) return next(err) urlencodedParser(req, res, next) }) }) request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) it('should parse extended syntax', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user[name][first]=Tobi') .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) }) describe('with extended option', function () { describe('when false', function () { before(function () { this.server = createServer({ extended: false }) }) it('should not parse extended syntax', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user[name][first]=Tobi') .expect(200, '{"user[name][first]":"Tobi"}', done) }) it('should parse multiple key instances', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=Tobi&user=Loki') .expect(200, '{"user":["Tobi","Loki"]}', done) }) }) describe('when true', function () { before(function () { this.server = createServer({ extended: true }) }) it('should parse multiple key instances', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=Tobi&user=Loki') .expect(200, '{"user":["Tobi","Loki"]}', done) }) it('should parse extended syntax', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user[name][first]=Tobi') .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) }) it('should parse parameters with dots', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user.name=Tobi') .expect(200, '{"user.name":"Tobi"}', done) }) it('should parse fully-encoded extended syntax', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user%5Bname%5D%5Bfirst%5D=Tobi') .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) }) it('should parse array index notation', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('foo[0]=bar&foo[1]=baz') .expect(200, '{"foo":["bar","baz"]}', done) }) it('should parse array index notation with large array', function (done) { var str = 'f[0]=0' for (var i = 1; i < 500; i++) { str += '&f[' + i + ']=' + i.toString(16) } request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(str) .expect(function (res) { var obj = JSON.parse(res.text) assert.strictEqual(Object.keys(obj).length, 1) assert.strictEqual(Array.isArray(obj.f), true) assert.strictEqual(obj.f.length, 500) }) .expect(200, done) }) it('should parse array of objects syntax', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!') .expect(200, '{"foo":[{"bar":"baz","fizz":"buzz"},"done!"]}', done) }) it('should parse deep object', function (done) { var str = 'foo' for (var i = 0; i < 32; i++) { str += '[p]' } str += '=bar' request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(str) .expect(function (res) { var obj = JSON.parse(res.text) assert.strictEqual(Object.keys(obj).length, 1) assert.strictEqual(typeof obj.foo, 'object') var depth = 0 var ref = obj.foo while ((ref = ref.p)) { depth++ } assert.strictEqual(depth, 32) }) .expect(200, done) }) }) }) describe('with depth option', function () { describe('when custom value set', function () { it('should reject non possitive numbers', function () { assert.throws(createServer.bind(null, { extended: true, depth: -1 }), /TypeError: option depth must be a zero or a positive number/) assert.throws(createServer.bind(null, { extended: true, depth: NaN }), /TypeError: option depth must be a zero or a positive number/) assert.throws(createServer.bind(null, { extended: true, depth: 'beep' }), /TypeError: option depth must be a zero or a positive number/) }) it('should parse up to the specified depth', function (done) { this.server = createServer({ extended: true, depth: 10 }) request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('a[b][c][d]=value') .expect(200, '{"a":{"b":{"c":{"d":"value"}}}}', done) }) it('should not parse beyond the specified depth', function (done) { this.server = createServer({ extended: true, depth: 1 }) request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('a[b][c][d][e]=value') .expect(400, '[querystring.parse.rangeError] The input exceeded the depth', done) }) }) describe('when default value', function () { before(function () { this.server = createServer({ }) }) it('should parse deeply nested objects', function (done) { var deepObject = 'a' for (var i = 0; i < 32; i++) { deepObject += '[p]' } deepObject += '=value' request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(deepObject) .expect(function (res) { var obj = JSON.parse(res.text) var depth = 0 var ref = obj.a while ((ref = ref.p)) { depth++ } assert.strictEqual(depth, 32) }) .expect(200, done) }) it('should not parse beyond the specified depth', function (done) { var deepObject = 'a' for (var i = 0; i < 33; i++) { deepObject += '[p]' } deepObject += '=value' request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(deepObject) .expect(400, '[querystring.parse.rangeError] The input exceeded the depth', done) }) }) }) describe('with inflate option', function () { describe('when false', function () { before(function () { this.server = createServer({ inflate: false }) }) it('should not accept content-encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) describe('when true', function () { before(function () { this.server = createServer({ inflate: true }) }) it('should accept content-encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(200, '{"name":"论"}', done) }) }) }) describe('with limit option', function () { it('should 413 when over limit with Content-Length', function (done) { var buf = Buffer.alloc(1024, '.') request(createServer({ limit: '1kb' })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .set('Content-Length', '1028') .send('str=' + buf.toString()) .expect(413, done) }) it('should 413 when over limit with chunked encoding', function (done) { var buf = Buffer.alloc(1024, '.') var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded') test.set('Transfer-Encoding', 'chunked') test.write('str=') test.write(buf.toString()) test.expect(413, done) }) it('should 413 when inflated body over limit', function (done) { var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex')) test.expect(413, done) }) it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1024, '.') request(createServer({ limit: 1024 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('str=' + buf.toString()) .expect(413, done) }) it('should not change when options altered', function (done) { var buf = Buffer.alloc(1024, '.') var options = { limit: '1kb' } var server = createServer(options) options.limit = '100kb' request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('str=' + buf.toString()) .expect(413, done) }) it('should not hang response', function (done) { var buf = Buffer.alloc(10240, '.') var server = createServer({ limit: '8kb' }) var test = request(server).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(buf) test.write(buf) test.write(buf) test.expect(413, done) }) it('should not error when inflating', function (done) { var server = createServer({ limit: '1kb' }) var test = request(server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex')) test.expect(413, done) }) }) describe('with parameterLimit option', function () { describe('with extended: false', function () { it('should reject 0', function () { assert.throws(createServer.bind(null, { extended: false, parameterLimit: 0 }), /TypeError: option parameterLimit must be a positive number/) }) it('should reject string', function () { assert.throws(createServer.bind(null, { extended: false, parameterLimit: 'beep' }), /TypeError: option parameterLimit must be a positive number/) }) it('should 413 if over limit', function (done) { request(createServer({ extended: false, parameterLimit: 10 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(11)) .expect(413, '[parameters.too.many] too many parameters', done) }) it('should work when at the limit', function (done) { request(createServer({ extended: false, parameterLimit: 10 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(10)) .expect(expectKeyCount(10)) .expect(200, done) }) it('should work if number is floating point', function (done) { request(createServer({ extended: false, parameterLimit: 10.1 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(11)) .expect(413, /too many parameters/, done) }) it('should work with large limit', function (done) { request(createServer({ extended: false, parameterLimit: 5000 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(5000)) .expect(expectKeyCount(5000)) .expect(200, done) }) it('should work with Infinity limit', function (done) { request(createServer({ extended: false, parameterLimit: Infinity })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(10000)) .expect(expectKeyCount(10000)) .expect(200, done) }) }) describe('with extended: true', function () { it('should reject 0', function () { assert.throws(createServer.bind(null, { extended: true, parameterLimit: 0 }), /TypeError: option parameterLimit must be a positive number/) }) it('should reject string', function () { assert.throws(createServer.bind(null, { extended: true, parameterLimit: 'beep' }), /TypeError: option parameterLimit must be a positive number/) }) it('should 413 if over limit', function (done) { request(createServer({ extended: true, parameterLimit: 10 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(11)) .expect(413, '[parameters.too.many] too many parameters', done) }) it('should work when at the limit', function (done) { request(createServer({ extended: true, parameterLimit: 10 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(10)) .expect(expectKeyCount(10)) .expect(200, done) }) it('should work if number is floating point', function (done) { request(createServer({ extended: true, parameterLimit: 10.1 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(11)) .expect(413, /too many parameters/, done) }) it('should work with large limit', function (done) { request(createServer({ extended: true, parameterLimit: 5000 })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(5000)) .expect(expectKeyCount(5000)) .expect(200, done) }) it('should work with Infinity limit', function (done) { request(createServer({ extended: true, parameterLimit: Infinity })) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(10000)) .expect(expectKeyCount(10000)) .expect(200, done) }) }) }) describe('with type option', function () { describe('when "application/vnd.x-www-form-urlencoded"', function () { before(function () { this.server = createServer({ type: 'application/vnd.x-www-form-urlencoded' }) }) it('should parse for custom type', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/vnd.x-www-form-urlencoded') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) it('should ignore standard type', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(200, '{}', done) }) }) describe('when ["urlencoded", "application/x-pairs"]', function () { before(function () { this.server = createServer({ type: ['urlencoded', 'application/x-pairs'] }) }) it('should parse "application/x-www-form-urlencoded"', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) it('should parse "application/x-pairs"', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-pairs') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) it('should ignore application/x-foo', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-foo') .send('user=tobi') .expect(200, '{}', done) }) }) describe('when a function', function () { it('should parse when truthy value returned', function (done) { var server = createServer({ type: accept }) function accept (req) { return req.headers['content-type'] === 'application/vnd.something' } request(server) .post('/') .set('Content-Type', 'application/vnd.something') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) it('should work without content-type', function (done) { var server = createServer({ type: accept }) function accept (req) { return true } var test = request(server).post('/') test.write('user=tobi') test.expect(200, '{"user":"tobi"}', done) }) it('should not invoke without a body', function (done) { var server = createServer({ type: accept }) function accept (req) { throw new Error('oops!') } request(server) .get('/') .expect(200, done) }) }) }) describe('with verify option', function () { it('should assert value if function', function () { assert.throws(createServer.bind(null, { verify: 'lol' }), /TypeError: option verify must be function/) }) it('should error from verify', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x20) throw new Error('no leading space') } }) request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(' user=tobi') .expect(403, '[entity.verify.failed] no leading space', done) }) it('should allow custom codes', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] !== 0x20) return var err = new Error('no leading space') err.status = 400 throw err } }) request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(' user=tobi') .expect(400, '[entity.verify.failed] no leading space', done) }) it('should allow custom type', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] !== 0x20) return var err = new Error('no leading space') err.type = 'foo.bar' throw err } }) request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(' user=tobi') .expect(403, '[foo.bar] no leading space', done) }) it('should allow pass-through', function (done) { var server = createServer({ verify: function (req, res, buf) { if (buf[0] === 0x5b) throw new Error('no arrays') } }) request(server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(200, '{"user":"tobi"}', done) }) it('should 415 on unknown charset prior to verify', function (done) { var server = createServer({ verify: function (req, res, buf) { throw new Error('unexpected verify call') } }) var test = request(server).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) }) }) describeAsyncHooks('async local storage', function () { before(function () { var urlencodedParser = bodyParser.urlencoded() var store = { foo: 'bar' } this.server = createServer(function (req, res, next) { var asyncLocalStorage = new asyncHooks.AsyncLocalStorage() asyncLocalStorage.run(store, function () { urlencodedParser(req, res, function (err) { var local = asyncLocalStorage.getStore() if (local) { res.setHeader('x-store-foo', String(local.foo)) } next(err) }) }) }) }) it('should presist store', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') .expect(200) .expect('x-store-foo', 'bar') .expect('{"user":"tobi"}') .end(done) }) it('should presist store when unmatched content-type', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/fizzbuzz') .send('buzz') .expect(200) .expect('x-store-foo', 'bar') .expect('{}') .end(done) }) it('should presist store when inflated', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(200) test.expect('x-store-foo', 'bar') test.expect('{"name":"论"}') test.end(done) }) it('should presist store when inflate error', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex')) test.expect(400) test.expect('x-store-foo', 'bar') test.end(done) }) it('should presist store when limit exceeded', function (done) { request(this.server) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=' + Buffer.alloc(1024 * 100, '.').toString()) .expect(413) .expect('x-store-foo', 'bar') .end(done) }) }) describe('charset', function () { before(function () { this.server = createServer() }) it('should parse utf-8', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8') test.write(Buffer.from('6e616d653de8aeba', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should parse when content-length != char length', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8') test.set('Content-Length', '7') test.write(Buffer.from('746573743dc3a5', 'hex')) test.expect(200, '{"test":"å"}', done) }) it('should default to utf-8', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('6e616d653de8aeba', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should fail on unknown charset', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r') test.write(Buffer.from('6e616d653dcec5d4', 'hex')) test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done) }) }) describe('encoding', function () { before(function () { this.server = createServer({ limit: '10kb' }) }) it('should parse without encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('6e616d653de8aeba', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should support identity encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'identity') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('6e616d653de8aeba', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should support gzip encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should support deflate encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'deflate') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should be case-insensitive', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'GZIP') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) test.expect(200, '{"name":"论"}', done) }) it('should 415 on unknown encoding', function (done) { var test = request(this.server).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('000000000000', 'hex')) test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) function createManyParams (count) { var str = '' if (count === 0) { return str } str += '0=0' for (var i = 1; i < count; i++) { var n = i.toString(36) str += '&' + n + '=' + n } return str } function createServer (opts) { var _bodyParser = typeof opts !== 'function' ? bodyParser.urlencoded(opts) : opts return http.createServer(function (req, res) { _bodyParser(req, res, function (err) { if (err) { res.statusCode = err.status || 500 res.end('[' + err.type + '] ' + err.message) } else { res.statusCode = 200 res.end(JSON.stringify(req.body)) } }) }) } function expectKeyCount (count) { return function (res) { assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count) } } function tryRequire (name) { try { return require(name) } catch (e) { return {} } }