[
  {
    "path": ".editorconfig",
    "content": "# https://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[{*.js,*.json,*.yml}]\nindent_size = 2\nindent_style = space\n"
  },
  {
    "path": ".eslintignore",
    "content": "coverage\nnode_modules\n"
  },
  {
    "path": ".eslintrc.yml",
    "content": "root: true\nenv:\n  es2022: true\n  node: true\nrules:\n  eol-last: error\n  eqeqeq: [error, allow-null]\n  indent: [error, 2, { MemberExpression: \"off\", SwitchCase: 1 }]\n  no-trailing-spaces: error\n  no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }]\n  no-restricted-globals:\n    - error\n    - name: Buffer\n      message: Use `import { Buffer } from \"node:buffer\"` instead of the global Buffer.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: monthly\n\n  - package-ecosystem: npm\n    directory: /\n    schedule:\n      interval: monthly\n      time: \"23:00\"\n      timezone: Europe/London\n    open-pull-requests-limit: 10\n    ignore:\n      - dependency-name: \"*\"\n        update-types: [\"version-update:semver-major\"]"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\n\non:\n  push:\n    branches:\n      - master\n      - develop\n      - '4.x'\n      - '5.x'\n      - '5.0'\n    paths-ignore:\n      - '*.md'\n  pull_request:\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\n# Cancel in progress workflows\n# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run\nconcurrency:\n  group: \"${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}\"\n  cancel-in-progress: true\n\njobs:\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n      - name: Setup Node.js\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n\n      - name: Install dependencies\n        run: npm install --ignore-scripts --include=dev\n\n      - name: Run lint\n        run: npm run lint\n\n  test:\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, windows-latest]\n        node-version: [18, 19, 20, 21, 22, 23, 24, 25]\n        # Node.js release schedule: https://nodejs.org/en/about/releases/\n\n    name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}\n\n    runs-on: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Setup Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Configure npm loglevel\n        run: |\n          npm config set loglevel error\n        shell: bash\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Output Node and NPM versions\n        run: |\n          echo \"Node.js version: $(node -v)\"\n          echo \"NPM version: $(npm -v)\"\n\n      - name: Run tests\n        shell: bash\n        run: npm run test-ci\n\n      - name: Upload code coverage\n        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0\n        with:\n          name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}\n          path: ./coverage/lcov.info\n          retention-days: 1\n\n  coverage:\n    needs: test\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      checks: write\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Install lcov\n        shell: bash\n        run: sudo apt-get -y install lcov\n\n      - name: Collect coverage reports\n        uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0\n        with:\n          path: ./coverage\n          pattern: coverage-node-*\n\n      - name: Merge coverage reports\n        shell: bash\n        run: find ./coverage -name lcov.info -exec printf '-a %q\\n' {} \\; | xargs lcov -o ./lcov.info\n\n      - name: Upload coverage report\n        uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7\n        with:\n          file: ./lcov.info\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [\"master\"]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [\"master\"]\n  schedule:\n    - cron: \"0 0 * * 1\"\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [javascript, actions]\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4\n        with:\n          languages: ${{ matrix.language }}\n          config: |\n            paths-ignore:\n              - test\n          # If you wish to specify custom queries, you can do so here or in a config file.\n          # By default, queries listed here will override any specified in a config file.\n          # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n      # If this step fails, then you should remove it and run the build manually (see below)\n      # - name: Autobuild\n      #   uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7\n\n      # ℹ️ Command-line programs to run using the OS shell.\n      # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n\n      #   If the Autobuild fails above, remove it and uncomment the following three lines.\n      #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.\n\n      # - run: |\n      #   echo \"Run, Build Application using script\"\n      #   ./location_of_script_within_repo/buildscript.sh\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4\n"
  },
  {
    "path": ".github/workflows/legacy.yml",
    "content": "name: legacy\n\non:\n  push:\n    branches:\n      - master\n      - develop\n      - '4.x'\n      - '5.x'\n      - '5.0'\n    paths-ignore:\n      - '*.md'\n  pull_request:\n    paths-ignore:\n      - '*.md'\n  workflow_dispatch:\n  \npermissions:\n  contents: read\n\n# Cancel in progress workflows\n# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run\nconcurrency:\n  group: \"${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}\"\n  cancel-in-progress: true\n\njobs:\n  test:\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, windows-latest]\n        node-version: [16, 17]\n        # Node.js release schedule: https://nodejs.org/en/about/releases/\n\n    name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}\n\n    runs-on: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Setup Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Configure npm loglevel\n        run: |\n          npm config set loglevel error\n        shell: bash\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Output Node and NPM versions\n        run: |\n          echo \"Node.js version: $(node -v)\"\n          echo \"NPM version: $(npm -v)\"\n\n      - name: Run tests\n        shell: bash\n        run: npm run test-ci\n\n      - name: Upload code coverage\n        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0\n        with:\n          name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}\n          path: ./coverage/lcov.info\n          retention-days: 1\n\n  coverage:\n    needs: test\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      checks: write\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Install lcov\n        shell: bash\n        run: sudo apt-get -y install lcov\n\n      - name: Collect coverage reports\n        uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0\n        with:\n          path: ./coverage\n          pattern: coverage-node-*\n\n      - name: Merge coverage reports\n        shell: bash\n        run: find ./coverage -name lcov.info -exec printf '-a %q\\n' {} \\; | xargs lcov -o ./lcov.info\n\n      - name: Upload coverage report\n        uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7\n        with:\n          file: ./lcov.info\n"
  },
  {
    "path": ".github/workflows/scorecard.yml",
    "content": "# This workflow uses actions that are not certified by GitHub. They are provided\n# by a third-party and are governed by separate terms of service, privacy\n# policy, and support documentation.\n\nname: Scorecard supply-chain security\non:\n  # For Branch-Protection check. Only the default branch is supported. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection\n  branch_protection_rule:\n  # To guarantee Maintained check is occasionally updated. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained\n  schedule:\n    - cron: '16 21 * * 1'\n  push:\n    branches: [ \"master\" ]\n\n# Declare default permissions as read only.\npermissions: read-all\n\njobs:\n  analysis:\n    name: Scorecard analysis\n    runs-on: ubuntu-latest\n    permissions:\n      # Needed to upload the results to code-scanning dashboard.\n      security-events: write\n      # Needed to publish results and get a badge (see publish_results below).\n      id-token: write\n      # Uncomment the permissions below if installing in a private repository.\n      # contents: read\n      # actions: read\n\n    steps:\n      - name: \"Checkout code\"\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: \"Run analysis\"\n        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          # (Optional) \"write\" PAT token. Uncomment the `repo_token` line below if:\n          # - you want to enable the Branch-Protection check on a *public* repository, or\n          # - you are installing Scorecard on a *private* repository\n          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.\n          # repo_token: ${{ secrets.SCORECARD_TOKEN }}\n\n          # Public repositories:\n          #   - Publish results to OpenSSF REST API for easy access by consumers\n          #   - Allows the repository to include the Scorecard badge.\n          #   - See https://github.com/ossf/scorecard-action#publishing-results.\n          # For private repositories:\n          #   - `publish_results` will always be set to `false`, regardless\n          #     of the value entered here.\n          publish_results: true\n\n      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF\n      # format to the repository Actions tab.\n      - name: \"Upload artifact\"\n        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0\n        with:\n          name: SARIF file\n          path: results.sarif\n          retention-days: 5\n\n      # Upload the results to GitHub's code scanning dashboard.\n      - name: \"Upload to code-scanning\"\n        uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".gitignore",
    "content": "# npm\nnode_modules\npackage-lock.json\nnpm-shrinkwrap.json\n*.log\n*.gz\n\n# Yarn\nyarn-error.log\nyarn.lock\n\n# Coveralls\n.nyc_output\ncoverage\n\n# Benchmarking\nbenchmarks/graphs\n\n# ignore additional files using core.excludesFile\n# https://git-scm.com/docs/gitignore\n"
  },
  {
    "path": ".npmrc",
    "content": "package-lock=false\n"
  },
  {
    "path": "History.md",
    "content": "# Unreleased Changes\n\n## 🚀 Improvements\n\n* Improve HTML structure in `res.redirect()` responses when HTML format is accepted by adding `<!DOCTYPE html>`, `<title>`, and `<body>` tags for better browser compatibility - by [@Bernice55231](https://github.com/Bernice55231) in [#5167](https://github.com/expressjs/express/pull/5167)\n\n* When calling `app.render` with options set to null, the locals object is handled correctly, preventing unexpected errors and making the method behave the same as when options is omitted or an empty object is passed - by [AkaHarshit](https://github.com/AkaHarshit) in [#6903](https://github.com/expressjs/express/pull/6903)\n\n    ```js\n    app.render('index', null, callback); // now works as expected\n    ```\n\n## ⚡ Performance\n\n* Avoid duplicate Content-Type header processing in `res.send()` when sending string responses without an explicit Content-Type header - by [@bjohansebas](https://github.com/bjohansebas) in [#6991](https://github.com/expressjs/express/pull/6991)\n\n5.2.1 / 2025-12-01\n=======================\n\n* Revert security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))\n  * The prior release (5.2.0) included an erroneous breaking change related to the extended query parser. There is no actual security vulnerability associated with this behavior (CVE-2024-51999 has been rejected). The change has been fully reverted in this release.\n\n5.2.0 / 2025-12-01\n========================\n\n* Security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))\n* deps: `body-parser@^2.2.1`\n* A deprecation warning was added when using `res.redirect` with undefined arguments, Express now emits a warning to help detect calls that pass undefined as the status or URL and make them easier to fix.\n\n5.1.0 / 2025-03-31\n========================\n\n* Add support for `Uint8Array` in `res.send()`\n* Add support for ETag option in `res.sendFile()`\n* Add support for multiple links with the same rel in `res.links()`\n* Add funding field to package.json\n* perf: use loop for acceptParams\n* refactor: prefix built-in node module imports\n* deps: remove `setprototypeof`\n* deps: remove `safe-buffer`\n* deps: remove `utils-merge`\n* deps: remove `methods`\n* deps: remove `depd`\n* deps: `debug@^4.4.0`\n* deps: `body-parser@^2.2.0`\n* deps: `router@^2.2.0`\n* deps: `content-type@^1.0.5`\n* deps: `finalhandler@^2.1.0`\n* deps: `qs@^6.14.0`\n* deps: `server-static@2.2.0`\n* deps: `type-is@2.0.1`\n\n5.0.1 / 2024-10-08\n==========\n\n* Update `cookie` semver lock to address [CVE-2024-47764](https://nvd.nist.gov/vuln/detail/CVE-2024-47764)\n\n5.0.0 / 2024-09-10\n=========================\n* remove:\n  - `path-is-absolute` dependency - use `path.isAbsolute` instead\n* breaking:\n  * `res.status()` accepts only integers, and input must be greater than 99 and less than 1000\n    * will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range\n    * will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs\n  * deps: send@1.0.0\n  * `res.redirect('back')` and `res.location('back')` is no longer a supported magic string, explicitly use `req.get('Referrer') || '/'`.\n* change:\n  - `res.clearCookie` will ignore user provided `maxAge` and `expires` options\n* deps: cookie-signature@^1.2.1\n* deps: debug@4.3.6\n* deps: merge-descriptors@^2.0.0\n* deps: serve-static@^2.1.0\n* deps: qs@6.13.0\n* deps: accepts@^2.0.0\n* deps: mime-types@^3.0.0\n  - `application/javascript` => `text/javascript`\n* deps: type-is@^2.0.0\n* deps: content-disposition@^1.0.0\n* deps: finalhandler@^2.0.0\n* deps: fresh@^2.0.0\n* deps: body-parser@^2.0.1\n* deps: send@^1.1.0\n\n5.0.0-beta.3 / 2024-03-25\n=========================\n\nThis incorporates all changes after 4.19.1 up to 4.19.2.\n\n5.0.0-beta.2 / 2024-03-20\n=========================\n\nThis incorporates all changes after 4.17.2 up to 4.19.1.\n\n5.0.0-beta.1 / 2022-02-14\n=========================\n\nThis is the first Express 5.0 beta release, based off 4.17.2 and includes\nchanges from 5.0.0-alpha.8.\n\n  * change:\n    - Default \"query parser\" setting to `'simple'`\n    - Requires Node.js 4+\n    - Use `mime-types` for file to content type mapping\n  * deps: array-flatten@3.0.0\n  * deps: body-parser@2.0.0-beta.1\n    - `req.body` is no longer always initialized to `{}`\n    - `urlencoded` parser now defaults `extended` to `false`\n    - Use `on-finished` to determine when body read\n  * deps: router@2.0.0-beta.1\n    - Add new `?`, `*`, and `+` parameter modifiers\n    - Internalize private `router.process_params` method\n    - Matching group expressions are only RegExp syntax\n    - Named matching groups no longer available by position in `req.params`\n    - Regular expressions can only be used in a matching group\n    - Remove `debug` dependency\n    - Special `*` path segment behavior removed\n    - deps: array-flatten@3.0.0\n    - deps: parseurl@~1.3.3\n    - deps: path-to-regexp@3.2.0\n    - deps: setprototypeof@1.2.0\n  * deps: send@1.0.0-beta.1\n    - Change `dotfiles` option default to `'ignore'`\n    - Remove `hidden` option; use `dotfiles` option instead\n    - Use `mime-types` for file to content type mapping\n    - deps: debug@3.1.0\n  * deps: serve-static@2.0.0-beta.1\n    - Change `dotfiles` option default to `'ignore'`\n    - Remove `hidden` option; use `dotfiles` option instead\n    - Use `mime-types` for file to content type mapping\n    - Remove `express.static.mime` export; use `mime-types` package instead\n    - deps: send@1.0.0-beta.1\n\n5.0.0-alpha.8 / 2020-03-25\n==========================\n\nThis is the eighth Express 5.0 alpha release, based off 4.17.1 and includes\nchanges from 5.0.0-alpha.7.\n\n5.0.0-alpha.7 / 2018-10-26\n==========================\n\nThis is the seventh Express 5.0 alpha release, based off 4.16.4 and includes\nchanges from 5.0.0-alpha.6.\n\nThe major change with this alpha is the basic support for returned, rejected\nPromises in the router.\n\n  * remove:\n    - `path-to-regexp` dependency\n  * deps: debug@3.1.0\n    - Add `DEBUG_HIDE_DATE` environment variable\n    - Change timer to per-namespace instead of global\n    - Change non-TTY date format\n    - Remove `DEBUG_FD` environment variable support\n    - Support 256 namespace colors\n  * deps: router@2.0.0-alpha.1\n    - Add basic support for returned, rejected Promises\n    - Fix JSDoc for `Router` constructor\n    - deps: debug@3.1.0\n    - deps: parseurl@~1.3.2\n    - deps: setprototypeof@1.1.0\n    - deps: utils-merge@1.0.1\n\n5.0.0-alpha.6 / 2017-09-24\n==========================\n\nThis is the sixth Express 5.0 alpha release, based off 4.15.5 and includes\nchanges from 5.0.0-alpha.5.\n\n  * remove:\n    - `res.redirect(url, status)` signature - use `res.redirect(status, url)`\n    - `res.send(status, body)` signature - use `res.status(status).send(body)`\n  * deps: router@~1.3.1\n    - deps: debug@2.6.8\n\n5.0.0-alpha.5 / 2017-03-06\n==========================\n\nThis is the fifth Express 5.0 alpha release, based off 4.15.2 and includes\nchanges from 5.0.0-alpha.4.\n\n5.0.0-alpha.4 / 2017-03-01\n==========================\n\nThis is the fourth Express 5.0 alpha release, based off 4.15.0 and includes\nchanges from 5.0.0-alpha.3.\n\n  * remove:\n    - Remove Express 3.x middleware error stubs\n  * deps: router@~1.3.0\n    - Add `next(\"router\")` to exit from router\n    - Fix case where `router.use` skipped requests routes did not\n    - Skip routing when `req.url` is not set\n    - Use `%o` in path debug to tell types apart\n    - deps: debug@2.6.1\n    - deps: setprototypeof@1.0.3\n    - perf: add fast match path for `*` route\n\n5.0.0-alpha.3 / 2017-01-28\n==========================\n\nThis is the third Express 5.0 alpha release, based off 4.14.1 and includes\nchanges from 5.0.0-alpha.2.\n\n  * remove:\n    - `res.json(status, obj)` signature - use `res.status(status).json(obj)`\n    - `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)`\n    - `res.vary()` (no arguments) -- provide a field name as an argument\n  * deps: array-flatten@2.1.1\n  * deps: path-is-absolute@1.0.1\n  * deps: router@~1.1.5\n    - deps: array-flatten@2.0.1\n    - deps: methods@~1.1.2\n    - deps: parseurl@~1.3.1\n    - deps: setprototypeof@1.0.2\n\n5.0.0-alpha.2 / 2015-07-06\n==========================\n\nThis is the second Express 5.0 alpha release, based off 4.13.1 and includes\nchanges from 5.0.0-alpha.1.\n\n  * remove:\n    - `app.param(fn)`\n    - `req.param()` -- use `req.params`, `req.body`, or `req.query` instead\n  * change:\n    - `res.render` callback is always async, even for sync view engines\n    - The leading `:` character in `name` for `app.param(name, fn)` is no longer removed\n    - Use `router` module for routing\n    - Use `path-is-absolute` module for absolute path detection\n\n5.0.0-alpha.1 / 2014-11-06\n==========================\n\nThis is the first Express 5.0 alpha release, based off 4.10.1.\n\n  * remove:\n    - `app.del` - use `app.delete`\n    - `req.acceptsCharset` - use `req.acceptsCharsets`\n    - `req.acceptsEncoding` - use `req.acceptsEncodings`\n    - `req.acceptsLanguage` - use `req.acceptsLanguages`\n    - `res.json(obj, status)` signature - use `res.json(status, obj)`\n    - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)`\n    - `res.send(body, status)` signature - use `res.send(status, body)`\n    - `res.send(status)` signature - use `res.sendStatus(status)`\n    - `res.sendfile` - use `res.sendFile` instead\n    - `express.query` middleware\n  * change:\n    - `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname\n    - `req.query` is now a getter instead of a plain property\n  * add:\n    - `app.router` is a reference to the base router\n\n4.20.0 / 2024-09-10\n==========\n  * deps: serve-static@0.16.0\n    * Remove link renderization in html while redirecting\n  * deps: send@0.19.0\n    * Remove link renderization in html while redirecting\n  * deps: body-parser@0.6.0\n    * add `depth` option to customize the depth level in the parser\n    * IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`)\n  * Remove link renderization in html while using `res.redirect`\n  * deps: path-to-regexp@0.1.10\n    - Adds support for named matching groups in the routes using a regex\n    - Adds backtracking protection to parameters without regexes defined\n  * deps: encodeurl@~2.0.0\n    - Removes encoding of `\\`, `|`, and `^` to align better with URL spec\n  * Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie`\n    - Will be ignored in v5, clearCookie will set a cookie with an expires in the past to instruct clients to delete the cookie\n\n4.19.2 / 2024-03-25\n==========\n\n  * Improved fix for open redirect allow list bypass\n\n4.19.1 / 2024-03-20\n==========\n\n  * Allow passing non-strings to res.location with new encoding handling checks\n\n4.19.0 / 2024-03-20\n==========\n\n  * Prevent open redirect allow list bypass due to encodeurl\n  * deps: cookie@0.6.0\n\n4.18.3 / 2024-02-29\n==========\n\n  * Fix routing requests without method\n  * deps: body-parser@1.20.2\n    - Fix strict json error message on Node.js 19+\n    - deps: content-type@~1.0.5\n    - deps: raw-body@2.5.2\n  * deps: cookie@0.6.0\n    - Add `partitioned` option\n\n4.18.2 / 2022-10-08\n===================\n\n  * Fix regression routing a large stack in a single route\n  * deps: body-parser@1.20.1\n    - deps: qs@6.11.0\n    - perf: remove unnecessary object clone\n  * deps: qs@6.11.0\n\n4.18.1 / 2022-04-29\n===================\n\n  * Fix hanging on large stack of sync routes\n\n4.18.0 / 2022-04-25\n===================\n\n  * Add \"root\" option to `res.download`\n  * Allow `options` without `filename` in `res.download`\n  * Deprecate string and non-integer arguments to `res.status`\n  * Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie`\n  * Fix handling very large stacks of sync middleware\n  * Ignore `Object.prototype` values in settings through `app.set`/`app.get`\n  * Invoke `default` with same arguments as types in `res.format`\n  * Support proper 205 responses using `res.send`\n  * Use `http-errors` for `res.format` error\n  * deps: body-parser@1.20.0\n    - Fix error message for json parse whitespace in `strict`\n    - Fix internal error when inflated body exceeds limit\n    - Prevent loss of async hooks context\n    - Prevent hanging when request already read\n    - deps: depd@2.0.0\n    - deps: http-errors@2.0.0\n    - deps: on-finished@2.4.1\n    - deps: qs@6.10.3\n    - deps: raw-body@2.5.1\n  * deps: cookie@0.5.0\n    - Add `priority` option\n    - Fix `expires` option to reject invalid dates\n  * deps: depd@2.0.0\n    - Replace internal `eval` usage with `Function` constructor\n    - Use instance methods on `process` to check for listeners\n  * deps: finalhandler@1.2.0\n    - Remove set content headers that break response\n    - deps: on-finished@2.4.1\n    - deps: statuses@2.0.1\n  * deps: on-finished@2.4.1\n    - Prevent loss of async hooks context\n  * deps: qs@6.10.3\n  * deps: send@0.18.0\n    - Fix emitted 416 error missing headers property\n    - Limit the headers removed for 304 response\n    - deps: depd@2.0.0\n    - deps: destroy@1.2.0\n    - deps: http-errors@2.0.0\n    - deps: on-finished@2.4.1\n    - deps: statuses@2.0.1\n  * deps: serve-static@1.15.0\n    - deps: send@0.18.0\n  * deps: statuses@2.0.1\n    - Remove code 306\n    - Rename `425 Unordered Collection` to standard `425 Too Early`\n\n4.17.3 / 2022-02-16\n===================\n\n  * deps: accepts@~1.3.8\n    - deps: mime-types@~2.1.34\n    - deps: negotiator@0.6.3\n  * deps: body-parser@1.19.2\n    - deps: bytes@3.1.2\n    - deps: qs@6.9.7\n    - deps: raw-body@2.4.3\n  * deps: cookie@0.4.2\n  * deps: qs@6.9.7\n    * Fix handling of `__proto__` keys\n  * pref: remove unnecessary regexp for trust proxy\n\n4.17.2 / 2021-12-16\n===================\n\n  * Fix handling of `undefined` in `res.jsonp`\n  * Fix handling of `undefined` when `\"json escape\"` is enabled\n  * Fix incorrect middleware execution with unanchored `RegExp`s\n  * Fix `res.jsonp(obj, status)` deprecation message\n  * Fix typo in `res.is` JSDoc\n  * deps: body-parser@1.19.1\n    - deps: bytes@3.1.1\n    - deps: http-errors@1.8.1\n    - deps: qs@6.9.6\n    - deps: raw-body@2.4.2\n    - deps: safe-buffer@5.2.1\n    - deps: type-is@~1.6.18\n  * deps: content-disposition@0.5.4\n    - deps: safe-buffer@5.2.1\n  * deps: cookie@0.4.1\n    - Fix `maxAge` option to reject invalid values\n  * deps: proxy-addr@~2.0.7\n    - Use `req.socket` over deprecated `req.connection`\n    - deps: forwarded@0.2.0\n    - deps: ipaddr.js@1.9.1\n  * deps: qs@6.9.6\n  * deps: safe-buffer@5.2.1\n  * deps: send@0.17.2\n    - deps: http-errors@1.8.1\n    - deps: ms@2.1.3\n    - pref: ignore empty http tokens\n  * deps: serve-static@1.14.2\n    - deps: send@0.17.2\n  * deps: setprototypeof@1.2.0\n\n4.17.1 / 2019-05-25\n===================\n\n  * Revert \"Improve error message for `null`/`undefined` to `res.status`\"\n\n4.17.0 / 2019-05-16\n===================\n\n  * Add `express.raw` to parse bodies into `Buffer`\n  * Add `express.text` to parse bodies into string\n  * Improve error message for non-strings to `res.sendFile`\n  * Improve error message for `null`/`undefined` to `res.status`\n  * Support multiple hosts in `X-Forwarded-Host`\n  * deps: accepts@~1.3.7\n  * deps: body-parser@1.19.0\n    - Add encoding MIK\n    - Add petabyte (`pb`) support\n    - Fix parsing array brackets after index\n    - deps: bytes@3.1.0\n    - deps: http-errors@1.7.2\n    - deps: iconv-lite@0.4.24\n    - deps: qs@6.7.0\n    - deps: raw-body@2.4.0\n    - deps: type-is@~1.6.17\n  * deps: content-disposition@0.5.3\n  * deps: cookie@0.4.0\n    - Add `SameSite=None` support\n  * deps: finalhandler@~1.1.2\n    - Set stricter `Content-Security-Policy` header\n    - deps: parseurl@~1.3.3\n    - deps: statuses@~1.5.0\n  * deps: parseurl@~1.3.3\n  * deps: proxy-addr@~2.0.5\n    - deps: ipaddr.js@1.9.0\n  * deps: qs@6.7.0\n    - Fix parsing array brackets after index\n  * deps: range-parser@~1.2.1\n  * deps: send@0.17.1\n    - Set stricter CSP header in redirect & error responses\n    - deps: http-errors@~1.7.2\n    - deps: mime@1.6.0\n    - deps: ms@2.1.1\n    - deps: range-parser@~1.2.1\n    - deps: statuses@~1.5.0\n    - perf: remove redundant `path.normalize` call\n  * deps: serve-static@1.14.1\n    - Set stricter CSP header in redirect response\n    - deps: parseurl@~1.3.3\n    - deps: send@0.17.1\n  * deps: setprototypeof@1.1.1\n  * deps: statuses@~1.5.0\n    - Add `103 Early Hints`\n  * deps: type-is@~1.6.18\n    - deps: mime-types@~2.1.24\n    - perf: prevent internal `throw` on invalid type\n\n4.16.4 / 2018-10-10\n===================\n\n  * Fix issue where `\"Request aborted\"` may be logged in `res.sendfile`\n  * Fix JSDoc for `Router` constructor\n  * deps: body-parser@1.18.3\n    - Fix deprecation warnings on Node.js 10+\n    - Fix stack trace for strict json parse error\n    - deps: depd@~1.1.2\n    - deps: http-errors@~1.6.3\n    - deps: iconv-lite@0.4.23\n    - deps: qs@6.5.2\n    - deps: raw-body@2.3.3\n    - deps: type-is@~1.6.16\n  * deps: proxy-addr@~2.0.4\n    - deps: ipaddr.js@1.8.0\n  * deps: qs@6.5.2\n  * deps: safe-buffer@5.1.2\n\n4.16.3 / 2018-03-12\n===================\n\n  * deps: accepts@~1.3.5\n    - deps: mime-types@~2.1.18\n  * deps: depd@~1.1.2\n    - perf: remove argument reassignment\n  * deps: encodeurl@~1.0.2\n    - Fix encoding `%` as last character\n  * deps: finalhandler@1.1.1\n    - Fix 404 output for bad / missing pathnames\n    - deps: encodeurl@~1.0.2\n    - deps: statuses@~1.4.0\n  * deps: proxy-addr@~2.0.3\n    - deps: ipaddr.js@1.6.0\n  * deps: send@0.16.2\n    - Fix incorrect end tag in default error & redirects\n    - deps: depd@~1.1.2\n    - deps: encodeurl@~1.0.2\n    - deps: statuses@~1.4.0\n  * deps: serve-static@1.13.2\n    - Fix incorrect end tag in redirects\n    - deps: encodeurl@~1.0.2\n    - deps: send@0.16.2\n  * deps: statuses@~1.4.0\n  * deps: type-is@~1.6.16\n    - deps: mime-types@~2.1.18\n\n4.16.2 / 2017-10-09\n===================\n\n  * Fix `TypeError` in `res.send` when given `Buffer` and `ETag` header set\n  * perf: skip parsing of entire `X-Forwarded-Proto` header\n\n4.16.1 / 2017-09-29\n===================\n\n  * deps: send@0.16.1\n  * deps: serve-static@1.13.1\n    - Fix regression when `root` is incorrectly set to a file\n    - deps: send@0.16.1\n\n4.16.0 / 2017-09-28\n===================\n\n  * Add `\"json escape\"` setting for `res.json` and `res.jsonp`\n  * Add `express.json` and `express.urlencoded` to parse bodies\n  * Add `options` argument to `res.download`\n  * Improve error message when autoloading invalid view engine\n  * Improve error messages when non-function provided as middleware\n  * Skip `Buffer` encoding when not generating ETag for small response\n  * Use `safe-buffer` for improved Buffer API\n  * deps: accepts@~1.3.4\n    - deps: mime-types@~2.1.16\n  * deps: content-type@~1.0.4\n    - perf: remove argument reassignment\n    - perf: skip parameter parsing when no parameters\n  * deps: etag@~1.8.1\n    - perf: replace regular expression with substring\n  * deps: finalhandler@1.1.0\n    - Use `res.headersSent` when available\n  * deps: parseurl@~1.3.2\n    - perf: reduce overhead for full URLs\n    - perf: unroll the \"fast-path\" `RegExp`\n  * deps: proxy-addr@~2.0.2\n    - Fix trimming leading / trailing OWS in `X-Forwarded-For`\n    - deps: forwarded@~0.1.2\n    - deps: ipaddr.js@1.5.2\n    - perf: reduce overhead when no `X-Forwarded-For` header\n  * deps: qs@6.5.1\n    - Fix parsing & compacting very deep objects\n  * deps: send@0.16.0\n    - Add 70 new types for file extensions\n    - Add `immutable` option\n    - Fix missing `</html>` in default error & redirects\n    - Set charset as \"UTF-8\" for .js and .json\n    - Use instance methods on steam to check for listeners\n    - deps: mime@1.4.1\n    - perf: improve path validation speed\n  * deps: serve-static@1.13.0\n    - Add 70 new types for file extensions\n    - Add `immutable` option\n    - Set charset as \"UTF-8\" for .js and .json\n    - deps: send@0.16.0\n  * deps: setprototypeof@1.1.0\n  * deps: utils-merge@1.0.1\n  * deps: vary@~1.1.2\n    - perf: improve header token parsing speed\n  * perf: re-use options object when generating ETags\n  * perf: remove dead `.charset` set in `res.jsonp`\n\n4.15.5 / 2017-09-24\n===================\n\n  * deps: debug@2.6.9\n  * deps: finalhandler@~1.0.6\n    - deps: debug@2.6.9\n    - deps: parseurl@~1.3.2\n  * deps: fresh@0.5.2\n    - Fix handling of modified headers with invalid dates\n    - perf: improve ETag match loop\n    - perf: improve `If-None-Match` token parsing\n  * deps: send@0.15.6\n    - Fix handling of modified headers with invalid dates\n    - deps: debug@2.6.9\n    - deps: etag@~1.8.1\n    - deps: fresh@0.5.2\n    - perf: improve `If-Match` token parsing\n  * deps: serve-static@1.12.6\n    - deps: parseurl@~1.3.2\n    - deps: send@0.15.6\n    - perf: improve slash collapsing\n\n4.15.4 / 2017-08-06\n===================\n\n  * deps: debug@2.6.8\n  * deps: depd@~1.1.1\n    - Remove unnecessary `Buffer` loading\n  * deps: finalhandler@~1.0.4\n    - deps: debug@2.6.8\n  * deps: proxy-addr@~1.1.5\n    - Fix array argument being altered\n    - deps: ipaddr.js@1.4.0\n  * deps: qs@6.5.0\n  * deps: send@0.15.4\n    - deps: debug@2.6.8\n    - deps: depd@~1.1.1\n    - deps: http-errors@~1.6.2\n  * deps: serve-static@1.12.4\n    - deps: send@0.15.4\n\n4.15.3 / 2017-05-16\n===================\n\n  * Fix error when `res.set` cannot add charset to `Content-Type`\n  * deps: debug@2.6.7\n    - Fix `DEBUG_MAX_ARRAY_LENGTH`\n    - deps: ms@2.0.0\n  * deps: finalhandler@~1.0.3\n    - Fix missing `</html>` in HTML document\n    - deps: debug@2.6.7\n  * deps: proxy-addr@~1.1.4\n    - deps: ipaddr.js@1.3.0\n  * deps: send@0.15.3\n    - deps: debug@2.6.7\n    - deps: ms@2.0.0\n  * deps: serve-static@1.12.3\n    - deps: send@0.15.3\n  * deps: type-is@~1.6.15\n    - deps: mime-types@~2.1.15\n  * deps: vary@~1.1.1\n    - perf: hoist regular expression\n\n4.15.2 / 2017-03-06\n===================\n\n  * deps: qs@6.4.0\n    - Fix regression parsing keys starting with `[`\n\n4.15.1 / 2017-03-05\n===================\n\n  * deps: send@0.15.1\n    - Fix issue when `Date.parse` does not return `NaN` on invalid date\n    - Fix strict violation in broken environments\n  * deps: serve-static@1.12.1\n    - Fix issue when `Date.parse` does not return `NaN` on invalid date\n    - deps: send@0.15.1\n\n4.15.0 / 2017-03-01\n===================\n\n  * Add debug message when loading view engine\n  * Add `next(\"router\")` to exit from router\n  * Fix case where `router.use` skipped requests routes did not\n  * Remove usage of `res._headers` private field\n    - Improves compatibility with Node.js 8 nightly\n  * Skip routing when `req.url` is not set\n  * Use `%o` in path debug to tell types apart\n  * Use `Object.create` to setup request & response prototypes\n  * Use `setprototypeof` module to replace `__proto__` setting\n  * Use `statuses` instead of `http` module for status messages\n  * deps: debug@2.6.1\n    - Allow colors in workers\n    - Deprecated `DEBUG_FD` environment variable set to `3` or higher\n    - Fix error when running under React Native\n    - Use same color for same namespace\n    - deps: ms@0.7.2\n  * deps: etag@~1.8.0\n    - Use SHA1 instead of MD5 for ETag hashing\n    - Works with FIPS 140-2 OpenSSL configuration\n  * deps: finalhandler@~1.0.0\n    - Fix exception when `err` cannot be converted to a string\n    - Fully URL-encode the pathname in the 404\n    - Only include the pathname in the 404 message\n    - Send complete HTML document\n    - Set `Content-Security-Policy: default-src 'self'` header\n    - deps: debug@2.6.1\n  * deps: fresh@0.5.0\n    - Fix false detection of `no-cache` request directive\n    - Fix incorrect result when `If-None-Match` has both `*` and ETags\n    - Fix weak `ETag` matching to match spec\n    - perf: delay reading header values until needed\n    - perf: enable strict mode\n    - perf: hoist regular expressions\n    - perf: remove duplicate conditional\n    - perf: remove unnecessary boolean coercions\n    - perf: skip checking modified time if ETag check failed\n    - perf: skip parsing `If-None-Match` when no `ETag` header\n    - perf: use `Date.parse` instead of `new Date`\n  * deps: qs@6.3.1\n    - Fix array parsing from skipping empty values\n    - Fix compacting nested arrays\n  * deps: send@0.15.0\n    - Fix false detection of `no-cache` request directive\n    - Fix incorrect result when `If-None-Match` has both `*` and ETags\n    - Fix weak `ETag` matching to match spec\n    - Remove usage of `res._headers` private field\n    - Support `If-Match` and `If-Unmodified-Since` headers\n    - Use `res.getHeaderNames()` when available\n    - Use `res.headersSent` when available\n    - deps: debug@2.6.1\n    - deps: etag@~1.8.0\n    - deps: fresh@0.5.0\n    - deps: http-errors@~1.6.1\n  * deps: serve-static@1.12.0\n    - Fix false detection of `no-cache` request directive\n    - Fix incorrect result when `If-None-Match` has both `*` and ETags\n    - Fix weak `ETag` matching to match spec\n    - Remove usage of `res._headers` private field\n    - Send complete HTML document in redirect response\n    - Set default CSP header in redirect response\n    - Support `If-Match` and `If-Unmodified-Since` headers\n    - Use `res.getHeaderNames()` when available\n    - Use `res.headersSent` when available\n    - deps: send@0.15.0\n  * perf: add fast match path for `*` route\n  * perf: improve `req.ips` performance\n\n4.14.1 / 2017-01-28\n===================\n\n  * deps: content-disposition@0.5.2\n  * deps: finalhandler@0.5.1\n    - Fix exception when `err.headers` is not an object\n    - deps: statuses@~1.3.1\n    - perf: hoist regular expressions\n    - perf: remove duplicate validation path\n  * deps: proxy-addr@~1.1.3\n    - deps: ipaddr.js@1.2.0\n  * deps: send@0.14.2\n    - deps: http-errors@~1.5.1\n    - deps: ms@0.7.2\n    - deps: statuses@~1.3.1\n  * deps: serve-static@~1.11.2\n    - deps: send@0.14.2\n  * deps: type-is@~1.6.14\n    - deps: mime-types@~2.1.13\n\n4.14.0 / 2016-06-16\n===================\n\n  * Add `acceptRanges` option to `res.sendFile`/`res.sendfile`\n  * Add `cacheControl` option to `res.sendFile`/`res.sendfile`\n  * Add `options` argument to `req.range`\n    - Includes the `combine` option\n  * Encode URL in `res.location`/`res.redirect` if not already encoded\n  * Fix some redirect handling in `res.sendFile`/`res.sendfile`\n  * Fix Windows absolute path check using forward slashes\n  * Improve error with invalid arguments to `req.get()`\n  * Improve performance for `res.json`/`res.jsonp` in most cases\n  * Improve `Range` header handling in `res.sendFile`/`res.sendfile`\n  * deps: accepts@~1.3.3\n    - Fix including type extensions in parameters in `Accept` parsing\n    - Fix parsing `Accept` parameters with quoted equals\n    - Fix parsing `Accept` parameters with quoted semicolons\n    - Many performance improvements\n    - deps: mime-types@~2.1.11\n    - deps: negotiator@0.6.1\n  * deps: content-type@~1.0.2\n    - perf: enable strict mode\n  * deps: cookie@0.3.1\n    - Add `sameSite` option\n    - Fix cookie `Max-Age` to never be a floating point number\n    - Improve error message when `encode` is not a function\n    - Improve error message when `expires` is not a `Date`\n    - Throw better error for invalid argument to parse\n    - Throw on invalid values provided to `serialize`\n    - perf: enable strict mode\n    - perf: hoist regular expression\n    - perf: use for loop in parse\n    - perf: use string concatenation for serialization\n  * deps: finalhandler@0.5.0\n    - Change invalid or non-numeric status code to 500\n    - Overwrite status message to match set status code\n    - Prefer `err.statusCode` if `err.status` is invalid\n    - Set response headers from `err.headers` object\n    - Use `statuses` instead of `http` module for status messages\n  * deps: proxy-addr@~1.1.2\n    - Fix accepting various invalid netmasks\n    - Fix IPv6-mapped IPv4 validation edge cases\n    - IPv4 netmasks must be contiguous\n    - IPv6 addresses cannot be used as a netmask\n    - deps: ipaddr.js@1.1.1\n  * deps: qs@6.2.0\n    - Add `decoder` option in `parse` function\n  * deps: range-parser@~1.2.0\n    - Add `combine` option to combine overlapping ranges\n    - Fix incorrectly returning -1 when there is at least one valid range\n    - perf: remove internal function\n  * deps: send@0.14.1\n    - Add `acceptRanges` option\n    - Add `cacheControl` option\n    - Attempt to combine multiple ranges into single range\n    - Correctly inherit from `Stream` class\n    - Fix `Content-Range` header in 416 responses when using `start`/`end` options\n    - Fix `Content-Range` header missing from default 416 responses\n    - Fix redirect error when `path` contains raw non-URL characters\n    - Fix redirect when `path` starts with multiple forward slashes\n    - Ignore non-byte `Range` headers\n    - deps: http-errors@~1.5.0\n    - deps: range-parser@~1.2.0\n    - deps: statuses@~1.3.0\n    - perf: remove argument reassignment\n  * deps: serve-static@~1.11.1\n    - Add `acceptRanges` option\n    - Add `cacheControl` option\n    - Attempt to combine multiple ranges into single range\n    - Fix redirect error when `req.url` contains raw non-URL characters\n    - Ignore non-byte `Range` headers\n    - Use status code 301 for redirects\n    - deps: send@0.14.1\n  * deps: type-is@~1.6.13\n    - Fix type error when given invalid type to match against\n    - deps: mime-types@~2.1.11\n  * deps: vary@~1.1.0\n    - Only accept valid field names in the `field` argument\n  * perf: use strict equality when possible\n\n4.13.4 / 2016-01-21\n===================\n\n  * deps: content-disposition@0.5.1\n    - perf: enable strict mode\n  * deps: cookie@0.1.5\n    - Throw on invalid values provided to `serialize`\n  * deps: depd@~1.1.0\n    - Support web browser loading\n    - perf: enable strict mode\n  * deps: escape-html@~1.0.3\n    - perf: enable strict mode\n    - perf: optimize string replacement\n    - perf: use faster string coercion\n  * deps: finalhandler@0.4.1\n    - deps: escape-html@~1.0.3\n  * deps: merge-descriptors@1.0.1\n    - perf: enable strict mode\n  * deps: methods@~1.1.2\n    - perf: enable strict mode\n  * deps: parseurl@~1.3.1\n    - perf: enable strict mode\n  * deps: proxy-addr@~1.0.10\n    - deps: ipaddr.js@1.0.5\n    - perf: enable strict mode\n  * deps: range-parser@~1.0.3\n    - perf: enable strict mode\n  * deps: send@0.13.1\n    - deps: depd@~1.1.0\n    - deps: destroy@~1.0.4\n    - deps: escape-html@~1.0.3\n    - deps: range-parser@~1.0.3\n  * deps: serve-static@~1.10.2\n    - deps: escape-html@~1.0.3\n    - deps: parseurl@~1.3.0\n    - deps: send@0.13.1\n\n4.13.3 / 2015-08-02\n===================\n\n  * Fix infinite loop condition using `mergeParams: true`\n  * Fix inner numeric indices incorrectly altering parent `req.params`\n\n4.13.2 / 2015-07-31\n===================\n\n  * deps: accepts@~1.2.12\n    - deps: mime-types@~2.1.4\n  * deps: array-flatten@1.1.1\n    - perf: enable strict mode\n  * deps: path-to-regexp@0.1.7\n    - Fix regression with escaped round brackets and matching groups\n  * deps: type-is@~1.6.6\n    - deps: mime-types@~2.1.4\n\n4.13.1 / 2015-07-05\n===================\n\n  * deps: accepts@~1.2.10\n    - deps: mime-types@~2.1.2\n  * deps: qs@4.0.0\n    - Fix dropping parameters like `hasOwnProperty`\n    - Fix various parsing edge cases\n  * deps: type-is@~1.6.4\n    - deps: mime-types@~2.1.2\n    - perf: enable strict mode\n    - perf: remove argument reassignment\n\n4.13.0 / 2015-06-20\n===================\n\n  * Add settings to debug output\n  * Fix `res.format` error when only `default` provided\n  * Fix issue where `next('route')` in `app.param` would incorrectly skip values\n  * Fix hiding platform issues with `decodeURIComponent`\n    - Only `URIError`s are a 400\n  * Fix using `*` before params in routes\n  * Fix using capture groups before params in routes\n  * Simplify `res.cookie` to call `res.append`\n  * Use `array-flatten` module for flattening arrays\n  * deps: accepts@~1.2.9\n    - deps: mime-types@~2.1.1\n    - perf: avoid argument reassignment & argument slice\n    - perf: avoid negotiator recursive construction\n    - perf: enable strict mode\n    - perf: remove unnecessary bitwise operator\n  * deps: cookie@0.1.3\n    - perf: deduce the scope of try-catch deopt\n    - perf: remove argument reassignments\n  * deps: escape-html@1.0.2\n  * deps: etag@~1.7.0\n    - Always include entity length in ETags for hash length extensions\n    - Generate non-Stats ETags using MD5 only (no longer CRC32)\n    - Improve stat performance by removing hashing\n    - Improve support for JXcore\n    - Remove base64 padding in ETags to shorten\n    - Support \"fake\" stats objects in environments without fs\n    - Use MD5 instead of MD4 in weak ETags over 1KB\n  * deps: finalhandler@0.4.0\n    - Fix a false-positive when unpiping in Node.js 0.8\n    - Support `statusCode` property on `Error` objects\n    - Use `unpipe` module for unpiping requests\n    - deps: escape-html@1.0.2\n    - deps: on-finished@~2.3.0\n    - perf: enable strict mode\n    - perf: remove argument reassignment\n  * deps: fresh@0.3.0\n    - Add weak `ETag` matching support\n  * deps: on-finished@~2.3.0\n    - Add defined behavior for HTTP `CONNECT` requests\n    - Add defined behavior for HTTP `Upgrade` requests\n    - deps: ee-first@1.1.1\n  * deps: path-to-regexp@0.1.6\n  * deps: send@0.13.0\n    - Allow Node.js HTTP server to set `Date` response header\n    - Fix incorrectly removing `Content-Location` on 304 response\n    - Improve the default redirect response headers\n    - Send appropriate headers on default error response\n    - Use `http-errors` for standard emitted errors\n    - Use `statuses` instead of `http` module for status messages\n    - deps: escape-html@1.0.2\n    - deps: etag@~1.7.0\n    - deps: fresh@0.3.0\n    - deps: on-finished@~2.3.0\n    - perf: enable strict mode\n    - perf: remove unnecessary array allocations\n  * deps: serve-static@~1.10.0\n    - Add `fallthrough` option\n    - Fix reading options from options prototype\n    - Improve the default redirect response headers\n    - Malformed URLs now `next()` instead of 400\n    - deps: escape-html@1.0.2\n    - deps: send@0.13.0\n    - perf: enable strict mode\n    - perf: remove argument reassignment\n  * deps: type-is@~1.6.3\n    - deps: mime-types@~2.1.1\n    - perf: reduce try block size\n    - perf: remove bitwise operations\n  * perf: enable strict mode\n  * perf: isolate `app.render` try block\n  * perf: remove argument reassignments in application\n  * perf: remove argument reassignments in request prototype\n  * perf: remove argument reassignments in response prototype\n  * perf: remove argument reassignments in routing\n  * perf: remove argument reassignments in `View`\n  * perf: skip attempting to decode zero length string\n  * perf: use saved reference to `http.STATUS_CODES`\n\n4.12.4 / 2015-05-17\n===================\n\n  * deps: accepts@~1.2.7\n    - deps: mime-types@~2.0.11\n    - deps: negotiator@0.5.3\n  * deps: debug@~2.2.0\n    - deps: ms@0.7.1\n  * deps: depd@~1.0.1\n  * deps: etag@~1.6.0\n    - Improve support for JXcore\n    - Support \"fake\" stats objects in environments without `fs`\n  * deps: finalhandler@0.3.6\n    - deps: debug@~2.2.0\n    - deps: on-finished@~2.2.1\n  * deps: on-finished@~2.2.1\n    - Fix `isFinished(req)` when data buffered\n  * deps: proxy-addr@~1.0.8\n    - deps: ipaddr.js@1.0.1\n  * deps: qs@2.4.2\n   - Fix allowing parameters like `constructor`\n  * deps: send@0.12.3\n    - deps: debug@~2.2.0\n    - deps: depd@~1.0.1\n    - deps: etag@~1.6.0\n    - deps: ms@0.7.1\n    - deps: on-finished@~2.2.1\n  * deps: serve-static@~1.9.3\n    - deps: send@0.12.3\n  * deps: type-is@~1.6.2\n    - deps: mime-types@~2.0.11\n\n4.12.3 / 2015-03-17\n===================\n\n  * deps: accepts@~1.2.5\n    - deps: mime-types@~2.0.10\n  * deps: debug@~2.1.3\n    - Fix high intensity foreground color for bold\n    - deps: ms@0.7.0\n  * deps: finalhandler@0.3.4\n    - deps: debug@~2.1.3\n  * deps: proxy-addr@~1.0.7\n    - deps: ipaddr.js@0.1.9\n  * deps: qs@2.4.1\n    - Fix error when parameter `hasOwnProperty` is present\n  * deps: send@0.12.2\n    - Throw errors early for invalid `extensions` or `index` options\n    - deps: debug@~2.1.3\n  * deps: serve-static@~1.9.2\n    - deps: send@0.12.2\n  * deps: type-is@~1.6.1\n    - deps: mime-types@~2.0.10\n\n4.12.2 / 2015-03-02\n===================\n\n  * Fix regression where `\"Request aborted\"` is logged using `res.sendFile`\n\n4.12.1 / 2015-03-01\n===================\n\n  * Fix constructing application with non-configurable prototype properties\n  * Fix `ECONNRESET` errors from `res.sendFile` usage\n  * Fix `req.host` when using \"trust proxy\" hops count\n  * Fix `req.protocol`/`req.secure` when using \"trust proxy\" hops count\n  * Fix wrong `code` on aborted connections from `res.sendFile`\n  * deps: merge-descriptors@1.0.0\n\n4.12.0 / 2015-02-23\n===================\n\n  * Fix `\"trust proxy\"` setting to inherit when app is mounted\n  * Generate `ETag`s for all request responses\n    - No longer restricted to only responses for `GET` and `HEAD` requests\n  * Use `content-type` to parse `Content-Type` headers\n  * deps: accepts@~1.2.4\n    - Fix preference sorting to be stable for long acceptable lists\n    - deps: mime-types@~2.0.9\n    - deps: negotiator@0.5.1\n  * deps: cookie-signature@1.0.6\n  * deps: send@0.12.1\n    - Always read the stat size from the file\n    - Fix mutating passed-in `options`\n    - deps: mime@1.3.4\n  * deps: serve-static@~1.9.1\n    - deps: send@0.12.1\n  * deps: type-is@~1.6.0\n    - fix argument reassignment\n    - fix false-positives in `hasBody` `Transfer-Encoding` check\n    - support wildcard for both type and subtype (`*/*`)\n    - deps: mime-types@~2.0.9\n\n4.11.2 / 2015-02-01\n===================\n\n  * Fix `res.redirect` double-calling `res.end` for `HEAD` requests\n  * deps: accepts@~1.2.3\n    - deps: mime-types@~2.0.8\n  * deps: proxy-addr@~1.0.6\n    - deps: ipaddr.js@0.1.8\n  * deps: type-is@~1.5.6\n    - deps: mime-types@~2.0.8\n\n4.11.1 / 2015-01-20\n===================\n\n  * deps: send@0.11.1\n    - Fix root path disclosure\n  * deps: serve-static@~1.8.1\n    - Fix redirect loop in Node.js 0.11.14\n    - Fix root path disclosure\n    - deps: send@0.11.1\n\n4.11.0 / 2015-01-13\n===================\n\n  * Add `res.append(field, val)` to append headers\n  * Deprecate leading `:` in `name` for `app.param(name, fn)`\n  * Deprecate `req.param()` -- use `req.params`, `req.body`, or `req.query` instead\n  * Deprecate `app.param(fn)`\n  * Fix `OPTIONS` responses to include the `HEAD` method properly\n  * Fix `res.sendFile` not always detecting aborted connection\n  * Match routes iteratively to prevent stack overflows\n  * deps: accepts@~1.2.2\n    - deps: mime-types@~2.0.7\n    - deps: negotiator@0.5.0\n  * deps: send@0.11.0\n    - deps: debug@~2.1.1\n    - deps: etag@~1.5.1\n    - deps: ms@0.7.0\n    - deps: on-finished@~2.2.0\n  * deps: serve-static@~1.8.0\n    - deps: send@0.11.0\n\n4.10.8 / 2015-01-13\n===================\n\n  * Fix crash from error within `OPTIONS` response handler\n  * deps: proxy-addr@~1.0.5\n    - deps: ipaddr.js@0.1.6\n\n4.10.7 / 2015-01-04\n===================\n\n  * Fix `Allow` header for `OPTIONS` to not contain duplicate methods\n  * Fix incorrect \"Request aborted\" for `res.sendFile` when `HEAD` or 304\n  * deps: debug@~2.1.1\n  * deps: finalhandler@0.3.3\n    - deps: debug@~2.1.1\n    - deps: on-finished@~2.2.0\n  * deps: methods@~1.1.1\n  * deps: on-finished@~2.2.0\n  * deps: serve-static@~1.7.2\n    - Fix potential open redirect when mounted at root\n  * deps: type-is@~1.5.5\n    - deps: mime-types@~2.0.7\n\n4.10.6 / 2014-12-12\n===================\n\n  * Fix exception in `req.fresh`/`req.stale` without response headers\n\n4.10.5 / 2014-12-10\n===================\n\n  * Fix `res.send` double-calling `res.end` for `HEAD` requests\n  * deps: accepts@~1.1.4\n    - deps: mime-types@~2.0.4\n  * deps: type-is@~1.5.4\n    - deps: mime-types@~2.0.4\n\n4.10.4 / 2014-11-24\n===================\n\n  * Fix `res.sendfile` logging standard write errors\n\n4.10.3 / 2014-11-23\n===================\n\n  * Fix `res.sendFile` logging standard write errors\n  * deps: etag@~1.5.1\n  * deps: proxy-addr@~1.0.4\n    - deps: ipaddr.js@0.1.5\n  * deps: qs@2.3.3\n    - Fix `arrayLimit` behavior\n\n4.10.2 / 2014-11-09\n===================\n\n  * Correctly invoke async router callback asynchronously\n  * deps: accepts@~1.1.3\n    - deps: mime-types@~2.0.3\n  * deps: type-is@~1.5.3\n    - deps: mime-types@~2.0.3\n\n4.10.1 / 2014-10-28\n===================\n\n  * Fix handling of URLs containing `://` in the path\n  * deps: qs@2.3.2\n    - Fix parsing of mixed objects and values\n\n4.10.0 / 2014-10-23\n===================\n\n  * Add support for `app.set('views', array)`\n    - Views are looked up in sequence in array of directories\n  * Fix `res.send(status)` to mention `res.sendStatus(status)`\n  * Fix handling of invalid empty URLs\n  * Use `content-disposition` module for `res.attachment`/`res.download`\n    - Sends standards-compliant `Content-Disposition` header\n    - Full Unicode support\n  * Use `path.resolve` in view lookup\n  * deps: debug@~2.1.0\n    - Implement `DEBUG_FD` env variable support\n  * deps: depd@~1.0.0\n  * deps: etag@~1.5.0\n    - Improve string performance\n    - Slightly improve speed for weak ETags over 1KB\n  * deps: finalhandler@0.3.2\n    - Terminate in progress response only on error\n    - Use `on-finished` to determine request status\n    - deps: debug@~2.1.0\n    - deps: on-finished@~2.1.1\n  * deps: on-finished@~2.1.1\n    - Fix handling of pipelined requests\n  * deps: qs@2.3.0\n    - Fix parsing of mixed implicit and explicit arrays\n  * deps: send@0.10.1\n    - deps: debug@~2.1.0\n    - deps: depd@~1.0.0\n    - deps: etag@~1.5.0\n    - deps: on-finished@~2.1.1\n  * deps: serve-static@~1.7.1\n    - deps: send@0.10.1\n\n4.9.8 / 2014-10-17\n==================\n\n  * Fix `res.redirect` body when redirect status specified\n  * deps: accepts@~1.1.2\n    - Fix error when media type has invalid parameter\n    - deps: negotiator@0.4.9\n\n4.9.7 / 2014-10-10\n==================\n\n  * Fix using same param name in array of paths\n\n4.9.6 / 2014-10-08\n==================\n\n  * deps: accepts@~1.1.1\n    - deps: mime-types@~2.0.2\n    - deps: negotiator@0.4.8\n  * deps: serve-static@~1.6.4\n    - Fix redirect loop when index file serving disabled\n  * deps: type-is@~1.5.2\n    - deps: mime-types@~2.0.2\n\n4.9.5 / 2014-09-24\n==================\n\n  * deps: etag@~1.4.0\n  * deps: proxy-addr@~1.0.3\n    - Use `forwarded` npm module\n  * deps: send@0.9.3\n    - deps: etag@~1.4.0\n  * deps: serve-static@~1.6.3\n    - deps: send@0.9.3\n\n4.9.4 / 2014-09-19\n==================\n\n  * deps: qs@2.2.4\n    - Fix issue with object keys starting with numbers truncated\n\n4.9.3 / 2014-09-18\n==================\n\n  * deps: proxy-addr@~1.0.2\n    - Fix a global leak when multiple subnets are trusted\n    - deps: ipaddr.js@0.1.3\n\n4.9.2 / 2014-09-17\n==================\n\n  * Fix regression for empty string `path` in `app.use`\n  * Fix `router.use` to accept array of middleware without path\n  * Improve error message for bad `app.use` arguments\n\n4.9.1 / 2014-09-16\n==================\n\n  * Fix `app.use` to accept array of middleware without path\n  * deps: depd@0.4.5\n  * deps: etag@~1.3.1\n  * deps: send@0.9.2\n    - deps: depd@0.4.5\n    - deps: etag@~1.3.1\n    - deps: range-parser@~1.0.2\n  * deps: serve-static@~1.6.2\n    - deps: send@0.9.2\n\n4.9.0 / 2014-09-08\n==================\n\n  * Add `res.sendStatus`\n  * Invoke callback for sendfile when client aborts\n    - Applies to `res.sendFile`, `res.sendfile`, and `res.download`\n    - `err` will be populated with request aborted error\n  * Support IP address host in `req.subdomains`\n  * Use `etag` to generate `ETag` headers\n  * deps: accepts@~1.1.0\n    - update `mime-types`\n  * deps: cookie-signature@1.0.5\n  * deps: debug@~2.0.0\n  * deps: finalhandler@0.2.0\n    - Set `X-Content-Type-Options: nosniff` header\n    - deps: debug@~2.0.0\n  * deps: fresh@0.2.4\n  * deps: media-typer@0.3.0\n    - Throw error when parameter format invalid on parse\n  * deps: qs@2.2.3\n    - Fix issue where first empty value in array is discarded\n  * deps: range-parser@~1.0.2\n  * deps: send@0.9.1\n    - Add `lastModified` option\n    - Use `etag` to generate `ETag` header\n    - deps: debug@~2.0.0\n    - deps: fresh@0.2.4\n  * deps: serve-static@~1.6.1\n    - Add `lastModified` option\n    - deps: send@0.9.1\n  * deps: type-is@~1.5.1\n    - fix `hasbody` to be true for `content-length: 0`\n    - deps: media-typer@0.3.0\n    - deps: mime-types@~2.0.1\n  * deps: vary@~1.0.0\n    - Accept valid `Vary` header string as `field`\n\n4.8.8 / 2014-09-04\n==================\n\n  * deps: send@0.8.5\n    - Fix a path traversal issue when using `root`\n    - Fix malicious path detection for empty string path\n  * deps: serve-static@~1.5.4\n    - deps: send@0.8.5\n\n4.8.7 / 2014-08-29\n==================\n\n  * deps: qs@2.2.2\n    - Remove unnecessary cloning\n\n4.8.6 / 2014-08-27\n==================\n\n  * deps: qs@2.2.0\n    - Array parsing fix\n    - Performance improvements\n\n4.8.5 / 2014-08-18\n==================\n\n  * deps: send@0.8.3\n    - deps: destroy@1.0.3\n    - deps: on-finished@2.1.0\n  * deps: serve-static@~1.5.3\n    - deps: send@0.8.3\n\n4.8.4 / 2014-08-14\n==================\n\n  * deps: qs@1.2.2\n  * deps: send@0.8.2\n    - Work around `fd` leak in Node.js 0.10 for `fs.ReadStream`\n  * deps: serve-static@~1.5.2\n    - deps: send@0.8.2\n\n4.8.3 / 2014-08-10\n==================\n\n  * deps: parseurl@~1.3.0\n  * deps: qs@1.2.1\n  * deps: serve-static@~1.5.1\n    - Fix parsing of weird `req.originalUrl` values\n    - deps: parseurl@~1.3.0\n    - deps: utils-merge@1.0.0\n\n4.8.2 / 2014-08-07\n==================\n\n  * deps: qs@1.2.0\n    - Fix parsing array of objects\n\n4.8.1 / 2014-08-06\n==================\n\n  * fix incorrect deprecation warnings on `res.download`\n  * deps: qs@1.1.0\n    - Accept urlencoded square brackets\n    - Accept empty values in implicit array notation\n\n4.8.0 / 2014-08-05\n==================\n\n  * add `res.sendFile`\n    - accepts a file system path instead of a URL\n    - requires an absolute path or `root` option specified\n  * deprecate `res.sendfile` -- use `res.sendFile` instead\n  * support mounted app as any argument to `app.use()`\n  * deps: qs@1.0.2\n    - Complete rewrite\n    - Limits array length to 20\n    - Limits object depth to 5\n    - Limits parameters to 1,000\n  * deps: send@0.8.1\n    - Add `extensions` option\n  * deps: serve-static@~1.5.0\n    - Add `extensions` option\n    - deps: send@0.8.1\n\n4.7.4 / 2014-08-04\n==================\n\n  * fix `res.sendfile` regression for serving directory index files\n  * deps: send@0.7.4\n    - Fix incorrect 403 on Windows and Node.js 0.11\n    - Fix serving index files without root dir\n  * deps: serve-static@~1.4.4\n    - deps: send@0.7.4\n\n4.7.3 / 2014-08-04\n==================\n\n  * deps: send@0.7.3\n    - Fix incorrect 403 on Windows and Node.js 0.11\n  * deps: serve-static@~1.4.3\n    - Fix incorrect 403 on Windows and Node.js 0.11\n    - deps: send@0.7.3\n\n4.7.2 / 2014-07-27\n==================\n\n  * deps: depd@0.4.4\n    - Work-around v8 generating empty stack traces\n  * deps: send@0.7.2\n    - deps: depd@0.4.4\n  * deps: serve-static@~1.4.2\n\n4.7.1 / 2014-07-26\n==================\n\n  * deps: depd@0.4.3\n    - Fix exception when global `Error.stackTraceLimit` is too low\n  * deps: send@0.7.1\n    - deps: depd@0.4.3\n  * deps: serve-static@~1.4.1\n\n4.7.0 / 2014-07-25\n==================\n\n  * fix `req.protocol` for proxy-direct connections\n  * configurable query parser with `app.set('query parser', parser)`\n    - `app.set('query parser', 'extended')` parse with \"qs\" module\n    - `app.set('query parser', 'simple')` parse with \"querystring\" core module\n    - `app.set('query parser', false)` disable query string parsing\n    - `app.set('query parser', true)` enable simple parsing\n  * deprecate `res.json(status, obj)` -- use `res.status(status).json(obj)` instead\n  * deprecate `res.jsonp(status, obj)` -- use `res.status(status).jsonp(obj)` instead\n  * deprecate `res.send(status, body)` -- use `res.status(status).send(body)` instead\n  * deps: debug@1.0.4\n  * deps: depd@0.4.2\n    - Add `TRACE_DEPRECATION` environment variable\n    - Remove non-standard grey color from color output\n    - Support `--no-deprecation` argument\n    - Support `--trace-deprecation` argument\n  * deps: finalhandler@0.1.0\n    - Respond after request fully read\n    - deps: debug@1.0.4\n  * deps: parseurl@~1.2.0\n    - Cache URLs based on original value\n    - Remove no-longer-needed URL mis-parse work-around\n    - Simplify the \"fast-path\" `RegExp`\n  * deps: send@0.7.0\n    - Add `dotfiles` option\n    - Cap `maxAge` value to 1 year\n    - deps: debug@1.0.4\n    - deps: depd@0.4.2\n  * deps: serve-static@~1.4.0\n    - deps: parseurl@~1.2.0\n    - deps: send@0.7.0\n  * perf: prevent multiple `Buffer` creation in `res.send`\n\n4.6.1 / 2014-07-12\n==================\n\n  * fix `subapp.mountpath` regression for `app.use(subapp)`\n\n4.6.0 / 2014-07-11\n==================\n\n  * accept multiple callbacks to `app.use()`\n  * add explicit \"Rosetta Flash JSONP abuse\" protection\n    - previous versions are not vulnerable; this is just explicit protection\n  * catch errors in multiple `req.param(name, fn)` handlers\n  * deprecate `res.redirect(url, status)` -- use `res.redirect(status, url)` instead\n  * fix `res.send(status, num)` to send `num` as json (not error)\n  * remove unnecessary escaping when `res.jsonp` returns JSON response\n  * support non-string `path` in `app.use(path, fn)`\n    - supports array of paths\n    - supports `RegExp`\n  * router: fix optimization on router exit\n  * router: refactor location of `try` blocks\n  * router: speed up standard `app.use(fn)`\n  * deps: debug@1.0.3\n    - Add support for multiple wildcards in namespaces\n  * deps: finalhandler@0.0.3\n    - deps: debug@1.0.3\n  * deps: methods@1.1.0\n    - add `CONNECT`\n  * deps: parseurl@~1.1.3\n    - faster parsing of href-only URLs\n  * deps: path-to-regexp@0.1.3\n  * deps: send@0.6.0\n    - deps: debug@1.0.3\n  * deps: serve-static@~1.3.2\n    - deps: parseurl@~1.1.3\n    - deps: send@0.6.0\n  * perf: fix arguments reassign deopt in some `res` methods\n\n4.5.1 / 2014-07-06\n==================\n\n * fix routing regression when altering `req.method`\n\n4.5.0 / 2014-07-04\n==================\n\n * add deprecation message to non-plural `req.accepts*`\n * add deprecation message to `res.send(body, status)`\n * add deprecation message to `res.vary()`\n * add `headers` option to `res.sendfile`\n   - use to set headers on successful file transfer\n * add `mergeParams` option to `Router`\n   - merges `req.params` from parent routes\n * add `req.hostname` -- correct name for what `req.host` returns\n * deprecate things with `depd` module\n * deprecate `req.host` -- use `req.hostname` instead\n * fix behavior when handling request without routes\n * fix handling when `route.all` is only route\n * invoke `router.param()` only when route matches\n * restore `req.params` after invoking router\n * use `finalhandler` for final response handling\n * use `media-typer` to alter content-type charset\n * deps: accepts@~1.0.7\n * deps: send@0.5.0\n   - Accept string for `maxage` (converted by `ms`)\n   - Include link in default redirect response\n * deps: serve-static@~1.3.0\n   - Accept string for `maxAge` (converted by `ms`)\n   - Add `setHeaders` option\n   - Include HTML link in redirect response\n   - deps: send@0.5.0\n * deps: type-is@~1.3.2\n\n4.4.5 / 2014-06-26\n==================\n\n * deps: cookie-signature@1.0.4\n   - fix for timing attacks\n\n4.4.4 / 2014-06-20\n==================\n\n * fix `res.attachment` Unicode filenames in Safari\n * fix \"trim prefix\" debug message in `express:router`\n * deps: accepts@~1.0.5\n * deps: buffer-crc32@0.2.3\n\n4.4.3 / 2014-06-11\n==================\n\n * fix persistence of modified `req.params[name]` from `app.param()`\n * deps: accepts@1.0.3\n   - deps: negotiator@0.4.6\n * deps: debug@1.0.2\n * deps: send@0.4.3\n   - Do not throw uncatchable error on file open race condition\n   - Use `escape-html` for HTML escaping\n   - deps: debug@1.0.2\n   - deps: finished@1.2.2\n   - deps: fresh@0.2.2\n * deps: serve-static@1.2.3\n   - Do not throw uncatchable error on file open race condition\n   - deps: send@0.4.3\n\n4.4.2 / 2014-06-09\n==================\n\n * fix catching errors from top-level handlers\n * use `vary` module for `res.vary`\n * deps: debug@1.0.1\n * deps: proxy-addr@1.0.1\n * deps: send@0.4.2\n   - fix \"event emitter leak\" warnings\n   - deps: debug@1.0.1\n   - deps: finished@1.2.1\n * deps: serve-static@1.2.2\n   - fix \"event emitter leak\" warnings\n   - deps: send@0.4.2\n * deps: type-is@1.2.1\n\n4.4.1 / 2014-06-02\n==================\n\n * deps: methods@1.0.1\n * deps: send@0.4.1\n   - Send `max-age` in `Cache-Control` in correct format\n * deps: serve-static@1.2.1\n   - use `escape-html` for escaping\n   - deps: send@0.4.1\n\n4.4.0 / 2014-05-30\n==================\n\n * custom etag control with `app.set('etag', val)`\n   - `app.set('etag', function(body, encoding){ return '\"etag\"' })` custom etag generation\n   - `app.set('etag', 'weak')` weak tag\n   - `app.set('etag', 'strong')` strong etag\n   - `app.set('etag', false)` turn off\n   - `app.set('etag', true)` standard etag\n * mark `res.send` ETag as weak and reduce collisions\n * update accepts to 1.0.2\n   - Fix interpretation when header not in request\n * update send to 0.4.0\n   - Calculate ETag with md5 for reduced collisions\n   - Ignore stream errors after request ends\n   - deps: debug@0.8.1\n * update serve-static to 1.2.0\n   - Calculate ETag with md5 for reduced collisions\n   - Ignore stream errors after request ends\n   - deps: send@0.4.0\n\n4.3.2 / 2014-05-28\n==================\n\n * fix handling of errors from `router.param()` callbacks\n\n4.3.1 / 2014-05-23\n==================\n\n * revert \"fix behavior of multiple `app.VERB` for the same path\"\n   - this caused a regression in the order of route execution\n\n4.3.0 / 2014-05-21\n==================\n\n * add `req.baseUrl` to access the path stripped from `req.url` in routes\n * fix behavior of multiple `app.VERB` for the same path\n * fix issue routing requests among sub routers\n * invoke `router.param()` only when necessary instead of every match\n * proper proxy trust with `app.set('trust proxy', trust)`\n   - `app.set('trust proxy', 1)` trust first hop\n   - `app.set('trust proxy', 'loopback')` trust loopback addresses\n   - `app.set('trust proxy', '10.0.0.1')` trust single IP\n   - `app.set('trust proxy', '10.0.0.1/16')` trust subnet\n   - `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list\n   - `app.set('trust proxy', false)` turn off\n   - `app.set('trust proxy', true)` trust everything\n * set proper `charset` in `Content-Type` for `res.send`\n * update type-is to 1.2.0\n   - support suffix matching\n\n4.2.0 / 2014-05-11\n==================\n\n * deprecate `app.del()` -- use `app.delete()` instead\n * deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead\n   - the edge-case `res.json(status, num)` requires `res.status(status).json(num)`\n * deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead\n   - the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)`\n * fix `req.next` when inside router instance\n * include `ETag` header in `HEAD` requests\n * keep previous `Content-Type` for `res.jsonp`\n * support PURGE method\n   - add `app.purge`\n   - add `router.purge`\n   - include PURGE in `app.all`\n * update debug to 0.8.0\n   - add `enable()` method\n   - change from stderr to stdout\n * update methods to 1.0.0\n   - add PURGE\n\n4.1.2 / 2014-05-08\n==================\n\n * fix `req.host` for IPv6 literals\n * fix `res.jsonp` error if callback param is object\n\n4.1.1 / 2014-04-27\n==================\n\n * fix package.json to reflect supported node version\n\n4.1.0 / 2014-04-24\n==================\n\n * pass options from `res.sendfile` to `send`\n * preserve casing of headers in `res.header` and `res.set`\n * support unicode file names in `res.attachment` and `res.download`\n * update accepts to 1.0.1\n   - deps: negotiator@0.4.0\n * update cookie to 0.1.2\n   - Fix for maxAge == 0\n   - made compat with expires field\n * update send to 0.3.0\n   - Accept API options in options object\n   - Coerce option types\n   - Control whether to generate etags\n   - Default directory access to 403 when index disabled\n   - Fix sending files with dots without root set\n   - Include file path in etag\n   - Make \"Can't set headers after they are sent.\" catchable\n   - Send full entity-body for multi range requests\n   - Set etags to \"weak\"\n   - Support \"If-Range\" header\n   - Support multiple index paths\n   - deps: mime@1.2.11\n * update serve-static to 1.1.0\n   - Accept options directly to `send` module\n   - Resolve relative paths at middleware setup\n   - Use parseurl to parse the URL from request\n   - deps: send@0.3.0\n * update type-is to 1.1.0\n   - add non-array values support\n   - add `multipart` as a shorthand\n\n4.0.0 / 2014-04-09\n==================\n\n * remove:\n   - node 0.8 support\n   - connect and connect's patches except for charset handling\n   - express(1) - moved to [express-generator](https://github.com/expressjs/generator)\n   - `express.createServer()` - it has been deprecated for a long time. Use `express()`\n   - `app.configure` - use logic in your own app code\n   - `app.router` - is removed\n   - `req.auth` - use `basic-auth` instead\n   - `req.accepted*` - use `req.accepts*()` instead\n   - `res.location` - relative URL resolution is removed\n   - `res.charset` - include the charset in the content type when using `res.set()`\n   - all bundled middleware except `static`\n * change:\n   - `app.route` -> `app.mountpath` when mounting an express app in another express app\n   - `json spaces` no longer enabled by default in development\n   - `req.accepts*` -> `req.accepts*s` - i.e. `req.acceptsEncoding` -> `req.acceptsEncodings`\n   - `req.params` is now an object instead of an array\n   - `res.locals` is no longer a function. It is a plain js object. Treat it as such.\n   - `res.headerSent` -> `res.headersSent` to match node.js ServerResponse object\n * refactor:\n   - `req.accepts*` with [accepts](https://github.com/expressjs/accepts)\n   - `req.is` with [type-is](https://github.com/expressjs/type-is)\n   - [path-to-regexp](https://github.com/component/path-to-regexp)\n * add:\n   - `app.router()` - returns the app Router instance\n   - `app.route()` - Proxy to the app's `Router#route()` method to create a new route\n   - Router & Route - public API\n\n3.21.2 / 2015-07-31\n===================\n\n  * deps: connect@2.30.2\n    - deps: body-parser@~1.13.3\n    - deps: compression@~1.5.2\n    - deps: errorhandler@~1.4.2\n    - deps: method-override@~2.3.5\n    - deps: serve-index@~1.7.2\n    - deps: type-is@~1.6.6\n    - deps: vhost@~3.0.1\n  * deps: vary@~1.0.1\n    - Fix setting empty header from empty `field`\n    - perf: enable strict mode\n    - perf: remove argument reassignments\n\n3.21.1 / 2015-07-05\n===================\n\n  * deps: basic-auth@~1.0.3\n  * deps: connect@2.30.1\n    - deps: body-parser@~1.13.2\n    - deps: compression@~1.5.1\n    - deps: errorhandler@~1.4.1\n    - deps: morgan@~1.6.1\n    - deps: pause@0.1.0\n    - deps: qs@4.0.0\n    - deps: serve-index@~1.7.1\n    - deps: type-is@~1.6.4\n\n3.21.0 / 2015-06-18\n===================\n\n  * deps: basic-auth@1.0.2\n    - perf: enable strict mode\n    - perf: hoist regular expression\n    - perf: parse with regular expressions\n    - perf: remove argument reassignment\n  * deps: connect@2.30.0\n    - deps: body-parser@~1.13.1\n    - deps: bytes@2.1.0\n    - deps: compression@~1.5.0\n    - deps: cookie@0.1.3\n    - deps: cookie-parser@~1.3.5\n    - deps: csurf@~1.8.3\n    - deps: errorhandler@~1.4.0\n    - deps: express-session@~1.11.3\n    - deps: finalhandler@0.4.0\n    - deps: fresh@0.3.0\n    - deps: morgan@~1.6.0\n    - deps: serve-favicon@~2.3.0\n    - deps: serve-index@~1.7.0\n    - deps: serve-static@~1.10.0\n    - deps: type-is@~1.6.3\n  * deps: cookie@0.1.3\n    - perf: deduce the scope of try-catch deopt\n    - perf: remove argument reassignments\n  * deps: escape-html@1.0.2\n  * deps: etag@~1.7.0\n    - Always include entity length in ETags for hash length extensions\n    - Generate non-Stats ETags using MD5 only (no longer CRC32)\n    - Improve stat performance by removing hashing\n    - Improve support for JXcore\n    - Remove base64 padding in ETags to shorten\n    - Support \"fake\" stats objects in environments without fs\n    - Use MD5 instead of MD4 in weak ETags over 1KB\n  * deps: fresh@0.3.0\n    - Add weak `ETag` matching support\n  * deps: mkdirp@0.5.1\n    - Work in global strict mode\n  * deps: send@0.13.0\n    - Allow Node.js HTTP server to set `Date` response header\n    - Fix incorrectly removing `Content-Location` on 304 response\n    - Improve the default redirect response headers\n    - Send appropriate headers on default error response\n    - Use `http-errors` for standard emitted errors\n    - Use `statuses` instead of `http` module for status messages\n    - deps: escape-html@1.0.2\n    - deps: etag@~1.7.0\n    - deps: fresh@0.3.0\n    - deps: on-finished@~2.3.0\n    - perf: enable strict mode\n    - perf: remove unnecessary array allocations\n\n3.20.3 / 2015-05-17\n===================\n\n  * deps: connect@2.29.2\n    - deps: body-parser@~1.12.4\n    - deps: compression@~1.4.4\n    - deps: connect-timeout@~1.6.2\n    - deps: debug@~2.2.0\n    - deps: depd@~1.0.1\n    - deps: errorhandler@~1.3.6\n    - deps: finalhandler@0.3.6\n    - deps: method-override@~2.3.3\n    - deps: morgan@~1.5.3\n    - deps: qs@2.4.2\n    - deps: response-time@~2.3.1\n    - deps: serve-favicon@~2.2.1\n    - deps: serve-index@~1.6.4\n    - deps: serve-static@~1.9.3\n    - deps: type-is@~1.6.2\n  * deps: debug@~2.2.0\n    - deps: ms@0.7.1\n  * deps: depd@~1.0.1\n  * deps: proxy-addr@~1.0.8\n    - deps: ipaddr.js@1.0.1\n  * deps: send@0.12.3\n    - deps: debug@~2.2.0\n    - deps: depd@~1.0.1\n    - deps: etag@~1.6.0\n    - deps: ms@0.7.1\n    - deps: on-finished@~2.2.1\n\n3.20.2 / 2015-03-16\n===================\n\n  * deps: connect@2.29.1\n    - deps: body-parser@~1.12.2\n    - deps: compression@~1.4.3\n    - deps: connect-timeout@~1.6.1\n    - deps: debug@~2.1.3\n    - deps: errorhandler@~1.3.5\n    - deps: express-session@~1.10.4\n    - deps: finalhandler@0.3.4\n    - deps: method-override@~2.3.2\n    - deps: morgan@~1.5.2\n    - deps: qs@2.4.1\n    - deps: serve-index@~1.6.3\n    - deps: serve-static@~1.9.2\n    - deps: type-is@~1.6.1\n  * deps: debug@~2.1.3\n    - Fix high intensity foreground color for bold\n    - deps: ms@0.7.0\n  * deps: merge-descriptors@1.0.0\n  * deps: proxy-addr@~1.0.7\n    - deps: ipaddr.js@0.1.9\n  * deps: send@0.12.2\n    - Throw errors early for invalid `extensions` or `index` options\n    - deps: debug@~2.1.3\n\n3.20.1 / 2015-02-28\n===================\n\n  * Fix `req.host` when using \"trust proxy\" hops count\n  * Fix `req.protocol`/`req.secure` when using \"trust proxy\" hops count\n\n3.20.0 / 2015-02-18\n===================\n\n  * Fix `\"trust proxy\"` setting to inherit when app is mounted\n  * Generate `ETag`s for all request responses\n    - No longer restricted to only responses for `GET` and `HEAD` requests\n  * Use `content-type` to parse `Content-Type` headers\n  * deps: connect@2.29.0\n    - Use `content-type` to parse `Content-Type` headers\n    - deps: body-parser@~1.12.0\n    - deps: compression@~1.4.1\n    - deps: connect-timeout@~1.6.0\n    - deps: cookie-parser@~1.3.4\n    - deps: cookie-signature@1.0.6\n    - deps: csurf@~1.7.0\n    - deps: errorhandler@~1.3.4\n    - deps: express-session@~1.10.3\n    - deps: http-errors@~1.3.1\n    - deps: response-time@~2.3.0\n    - deps: serve-index@~1.6.2\n    - deps: serve-static@~1.9.1\n    - deps: type-is@~1.6.0\n  * deps: cookie-signature@1.0.6\n  * deps: send@0.12.1\n    - Always read the stat size from the file\n    - Fix mutating passed-in `options`\n    - deps: mime@1.3.4\n\n3.19.2 / 2015-02-01\n===================\n\n  * deps: connect@2.28.3\n    - deps: compression@~1.3.1\n    - deps: csurf@~1.6.6\n    - deps: errorhandler@~1.3.3\n    - deps: express-session@~1.10.2\n    - deps: serve-index@~1.6.1\n    - deps: type-is@~1.5.6\n  * deps: proxy-addr@~1.0.6\n    - deps: ipaddr.js@0.1.8\n\n3.19.1 / 2015-01-20\n===================\n\n  * deps: connect@2.28.2\n    - deps: body-parser@~1.10.2\n    - deps: serve-static@~1.8.1\n  * deps: send@0.11.1\n    - Fix root path disclosure\n\n3.19.0 / 2015-01-09\n===================\n\n  * Fix `OPTIONS` responses to include the `HEAD` method property\n  * Use `readline` for prompt in `express(1)`\n  * deps: commander@2.6.0\n  * deps: connect@2.28.1\n    - deps: body-parser@~1.10.1\n    - deps: compression@~1.3.0\n    - deps: connect-timeout@~1.5.0\n    - deps: csurf@~1.6.4\n    - deps: debug@~2.1.1\n    - deps: errorhandler@~1.3.2\n    - deps: express-session@~1.10.1\n    - deps: finalhandler@0.3.3\n    - deps: method-override@~2.3.1\n    - deps: morgan@~1.5.1\n    - deps: serve-favicon@~2.2.0\n    - deps: serve-index@~1.6.0\n    - deps: serve-static@~1.8.0\n    - deps: type-is@~1.5.5\n  * deps: debug@~2.1.1\n  * deps: methods@~1.1.1\n  * deps: proxy-addr@~1.0.5\n    - deps: ipaddr.js@0.1.6\n  * deps: send@0.11.0\n    - deps: debug@~2.1.1\n    - deps: etag@~1.5.1\n    - deps: ms@0.7.0\n    - deps: on-finished@~2.2.0\n\n3.18.6 / 2014-12-12\n===================\n\n  * Fix exception in `req.fresh`/`req.stale` without response headers\n\n3.18.5 / 2014-12-11\n===================\n\n  * deps: connect@2.27.6\n    - deps: compression@~1.2.2\n    - deps: express-session@~1.9.3\n    - deps: http-errors@~1.2.8\n    - deps: serve-index@~1.5.3\n    - deps: type-is@~1.5.4\n\n3.18.4 / 2014-11-23\n===================\n\n  * deps: connect@2.27.4\n    - deps: body-parser@~1.9.3\n    - deps: compression@~1.2.1\n    - deps: errorhandler@~1.2.3\n    - deps: express-session@~1.9.2\n    - deps: qs@2.3.3\n    - deps: serve-favicon@~2.1.7\n    - deps: serve-static@~1.5.1\n    - deps: type-is@~1.5.3\n  * deps: etag@~1.5.1\n  * deps: proxy-addr@~1.0.4\n    - deps: ipaddr.js@0.1.5\n\n3.18.3 / 2014-11-09\n===================\n\n  * deps: connect@2.27.3\n    - Correctly invoke async callback asynchronously\n    - deps: csurf@~1.6.3\n\n3.18.2 / 2014-10-28\n===================\n\n  * deps: connect@2.27.2\n    - Fix handling of URLs containing `://` in the path\n    - deps: body-parser@~1.9.2\n    - deps: qs@2.3.2\n\n3.18.1 / 2014-10-22\n===================\n\n  * Fix internal `utils.merge` deprecation warnings\n  * deps: connect@2.27.1\n    - deps: body-parser@~1.9.1\n    - deps: express-session@~1.9.1\n    - deps: finalhandler@0.3.2\n    - deps: morgan@~1.4.1\n    - deps: qs@2.3.0\n    - deps: serve-static@~1.7.1\n  * deps: send@0.10.1\n    - deps: on-finished@~2.1.1\n\n3.18.0 / 2014-10-17\n===================\n\n  * Use `content-disposition` module for `res.attachment`/`res.download`\n    - Sends standards-compliant `Content-Disposition` header\n    - Full Unicode support\n  * Use `etag` module to generate `ETag` headers\n  * deps: connect@2.27.0\n    - Use `http-errors` module for creating errors\n    - Use `utils-merge` module for merging objects\n    - deps: body-parser@~1.9.0\n    - deps: compression@~1.2.0\n    - deps: connect-timeout@~1.4.0\n    - deps: debug@~2.1.0\n    - deps: depd@~1.0.0\n    - deps: express-session@~1.9.0\n    - deps: finalhandler@0.3.1\n    - deps: method-override@~2.3.0\n    - deps: morgan@~1.4.0\n    - deps: response-time@~2.2.0\n    - deps: serve-favicon@~2.1.6\n    - deps: serve-index@~1.5.0\n    - deps: serve-static@~1.7.0\n  * deps: debug@~2.1.0\n    - Implement `DEBUG_FD` env variable support\n  * deps: depd@~1.0.0\n  * deps: send@0.10.0\n    - deps: debug@~2.1.0\n    - deps: depd@~1.0.0\n    - deps: etag@~1.5.0\n\n3.17.8 / 2014-10-15\n===================\n\n  * deps: connect@2.26.6\n    - deps: compression@~1.1.2\n    - deps: csurf@~1.6.2\n    - deps: errorhandler@~1.2.2\n\n3.17.7 / 2014-10-08\n===================\n\n  * deps: connect@2.26.5\n    - Fix accepting non-object arguments to `logger`\n    - deps: serve-static@~1.6.4\n\n3.17.6 / 2014-10-02\n===================\n\n  * deps: connect@2.26.4\n    - deps: morgan@~1.3.2\n    - deps: type-is@~1.5.2\n\n3.17.5 / 2014-09-24\n===================\n\n  * deps: connect@2.26.3\n    - deps: body-parser@~1.8.4\n    - deps: serve-favicon@~2.1.5\n    - deps: serve-static@~1.6.3\n  * deps: proxy-addr@~1.0.3\n    - Use `forwarded` npm module\n  * deps: send@0.9.3\n    - deps: etag@~1.4.0\n\n3.17.4 / 2014-09-19\n===================\n\n  * deps: connect@2.26.2\n    - deps: body-parser@~1.8.3\n    - deps: qs@2.2.4\n\n3.17.3 / 2014-09-18\n===================\n\n  * deps: proxy-addr@~1.0.2\n    - Fix a global leak when multiple subnets are trusted\n    - deps: ipaddr.js@0.1.3\n\n3.17.2 / 2014-09-15\n===================\n\n  * Use `crc` instead of `buffer-crc32` for speed\n  * deps: connect@2.26.1\n    - deps: body-parser@~1.8.2\n    - deps: depd@0.4.5\n    - deps: express-session@~1.8.2\n    - deps: morgan@~1.3.1\n    - deps: serve-favicon@~2.1.3\n    - deps: serve-static@~1.6.2\n  * deps: depd@0.4.5\n  * deps: send@0.9.2\n    - deps: depd@0.4.5\n    - deps: etag@~1.3.1\n    - deps: range-parser@~1.0.2\n\n3.17.1 / 2014-09-08\n===================\n\n  * Fix error in `req.subdomains` on empty host\n\n3.17.0 / 2014-09-08\n===================\n\n  * Support `X-Forwarded-Host` in `req.subdomains`\n  * Support IP address host in `req.subdomains`\n  * deps: connect@2.26.0\n    - deps: body-parser@~1.8.1\n    - deps: compression@~1.1.0\n    - deps: connect-timeout@~1.3.0\n    - deps: cookie-parser@~1.3.3\n    - deps: cookie-signature@1.0.5\n    - deps: csurf@~1.6.1\n    - deps: debug@~2.0.0\n    - deps: errorhandler@~1.2.0\n    - deps: express-session@~1.8.1\n    - deps: finalhandler@0.2.0\n    - deps: fresh@0.2.4\n    - deps: media-typer@0.3.0\n    - deps: method-override@~2.2.0\n    - deps: morgan@~1.3.0\n    - deps: qs@2.2.3\n    - deps: serve-favicon@~2.1.3\n    - deps: serve-index@~1.2.1\n    - deps: serve-static@~1.6.1\n    - deps: type-is@~1.5.1\n    - deps: vhost@~3.0.0\n  * deps: cookie-signature@1.0.5\n  * deps: debug@~2.0.0\n  * deps: fresh@0.2.4\n  * deps: media-typer@0.3.0\n    - Throw error when parameter format invalid on parse\n  * deps: range-parser@~1.0.2\n  * deps: send@0.9.1\n    - Add `lastModified` option\n    - Use `etag` to generate `ETag` header\n    - deps: debug@~2.0.0\n    - deps: fresh@0.2.4\n  * deps: vary@~1.0.0\n    - Accept valid `Vary` header string as `field`\n\n3.16.10 / 2014-09-04\n====================\n\n  * deps: connect@2.25.10\n    - deps: serve-static@~1.5.4\n  * deps: send@0.8.5\n    - Fix a path traversal issue when using `root`\n    - Fix malicious path detection for empty string path\n\n3.16.9 / 2014-08-29\n===================\n\n  * deps: connect@2.25.9\n    - deps: body-parser@~1.6.7\n    - deps: qs@2.2.2\n\n3.16.8 / 2014-08-27\n===================\n\n  * deps: connect@2.25.8\n    - deps: body-parser@~1.6.6\n    - deps: csurf@~1.4.1\n    - deps: qs@2.2.0\n\n3.16.7 / 2014-08-18\n===================\n\n  * deps: connect@2.25.7\n    - deps: body-parser@~1.6.5\n    - deps: express-session@~1.7.6\n    - deps: morgan@~1.2.3\n    - deps: serve-static@~1.5.3\n  * deps: send@0.8.3\n    - deps: destroy@1.0.3\n    - deps: on-finished@2.1.0\n\n3.16.6 / 2014-08-14\n===================\n\n  * deps: connect@2.25.6\n    - deps: body-parser@~1.6.4\n    - deps: qs@1.2.2\n    - deps: serve-static@~1.5.2\n  * deps: send@0.8.2\n    - Work around `fd` leak in Node.js 0.10 for `fs.ReadStream`\n\n3.16.5 / 2014-08-11\n===================\n\n  * deps: connect@2.25.5\n    - Fix backwards compatibility in `logger`\n\n3.16.4 / 2014-08-10\n===================\n\n  * Fix original URL parsing in `res.location`\n  * deps: connect@2.25.4\n    - Fix `query` middleware breaking with argument\n    - deps: body-parser@~1.6.3\n    - deps: compression@~1.0.11\n    - deps: connect-timeout@~1.2.2\n    - deps: express-session@~1.7.5\n    - deps: method-override@~2.1.3\n    - deps: on-headers@~1.0.0\n    - deps: parseurl@~1.3.0\n    - deps: qs@1.2.1\n    - deps: response-time@~2.0.1\n    - deps: serve-index@~1.1.6\n    - deps: serve-static@~1.5.1\n  * deps: parseurl@~1.3.0\n\n3.16.3 / 2014-08-07\n===================\n\n  * deps: connect@2.25.3\n    - deps: multiparty@3.3.2\n\n3.16.2 / 2014-08-07\n===================\n\n  * deps: connect@2.25.2\n    - deps: body-parser@~1.6.2\n    - deps: qs@1.2.0\n\n3.16.1 / 2014-08-06\n===================\n\n  * deps: connect@2.25.1\n    - deps: body-parser@~1.6.1\n    - deps: qs@1.1.0\n\n3.16.0 / 2014-08-05\n===================\n\n  * deps: connect@2.25.0\n    - deps: body-parser@~1.6.0\n    - deps: compression@~1.0.10\n    - deps: csurf@~1.4.0\n    - deps: express-session@~1.7.4\n    - deps: qs@1.0.2\n    - deps: serve-static@~1.5.0\n  * deps: send@0.8.1\n    - Add `extensions` option\n\n3.15.3 / 2014-08-04\n===================\n\n  * fix `res.sendfile` regression for serving directory index files\n  * deps: connect@2.24.3\n    - deps: serve-index@~1.1.5\n    - deps: serve-static@~1.4.4\n  * deps: send@0.7.4\n    - Fix incorrect 403 on Windows and Node.js 0.11\n    - Fix serving index files without root dir\n\n3.15.2 / 2014-07-27\n===================\n\n  * deps: connect@2.24.2\n    - deps: body-parser@~1.5.2\n    - deps: depd@0.4.4\n    - deps: express-session@~1.7.2\n    - deps: morgan@~1.2.2\n    - deps: serve-static@~1.4.2\n  * deps: depd@0.4.4\n    - Work-around v8 generating empty stack traces\n  * deps: send@0.7.2\n    - deps: depd@0.4.4\n\n3.15.1 / 2014-07-26\n===================\n\n  * deps: connect@2.24.1\n    - deps: body-parser@~1.5.1\n    - deps: depd@0.4.3\n    - deps: express-session@~1.7.1\n    - deps: morgan@~1.2.1\n    - deps: serve-index@~1.1.4\n    - deps: serve-static@~1.4.1\n  * deps: depd@0.4.3\n    - Fix exception when global `Error.stackTraceLimit` is too low\n  * deps: send@0.7.1\n    - deps: depd@0.4.3\n\n3.15.0 / 2014-07-22\n===================\n\n  * Fix `req.protocol` for proxy-direct connections\n  * Pass options from `res.sendfile` to `send`\n  * deps: connect@2.24.0\n    - deps: body-parser@~1.5.0\n    - deps: compression@~1.0.9\n    - deps: connect-timeout@~1.2.1\n    - deps: debug@1.0.4\n    - deps: depd@0.4.2\n    - deps: express-session@~1.7.0\n    - deps: finalhandler@0.1.0\n    - deps: method-override@~2.1.2\n    - deps: morgan@~1.2.0\n    - deps: multiparty@3.3.1\n    - deps: parseurl@~1.2.0\n    - deps: serve-static@~1.4.0\n  * deps: debug@1.0.4\n  * deps: depd@0.4.2\n    - Add `TRACE_DEPRECATION` environment variable\n    - Remove non-standard grey color from color output\n    - Support `--no-deprecation` argument\n    - Support `--trace-deprecation` argument\n  * deps: parseurl@~1.2.0\n    - Cache URLs based on original value\n    - Remove no-longer-needed URL mis-parse work-around\n    - Simplify the \"fast-path\" `RegExp`\n  * deps: send@0.7.0\n    - Add `dotfiles` option\n    - Cap `maxAge` value to 1 year\n    - deps: debug@1.0.4\n    - deps: depd@0.4.2\n\n3.14.0 / 2014-07-11\n===================\n\n * add explicit \"Rosetta Flash JSONP abuse\" protection\n   - previous versions are not vulnerable; this is just explicit protection\n * deprecate `res.redirect(url, status)` -- use `res.redirect(status, url)` instead\n * fix `res.send(status, num)` to send `num` as json (not error)\n * remove unnecessary escaping when `res.jsonp` returns JSON response\n * deps: basic-auth@1.0.0\n   - support empty password\n   - support empty username\n * deps: connect@2.23.0\n   - deps: debug@1.0.3\n   - deps: express-session@~1.6.4\n   - deps: method-override@~2.1.0\n   - deps: parseurl@~1.1.3\n   - deps: serve-static@~1.3.1\n  * deps: debug@1.0.3\n    - Add support for multiple wildcards in namespaces\n  * deps: methods@1.1.0\n    - add `CONNECT`\n  * deps: parseurl@~1.1.3\n    - faster parsing of href-only URLs\n\n3.13.0 / 2014-07-03\n===================\n\n * add deprecation message to `app.configure`\n * add deprecation message to `req.auth`\n * use `basic-auth` to parse `Authorization` header\n * deps: connect@2.22.0\n   - deps: csurf@~1.3.0\n   - deps: express-session@~1.6.1\n   - deps: multiparty@3.3.0\n   - deps: serve-static@~1.3.0\n * deps: send@0.5.0\n   - Accept string for `maxage` (converted by `ms`)\n   - Include link in default redirect response\n\n3.12.1 / 2014-06-26\n===================\n\n * deps: connect@2.21.1\n   - deps: cookie-parser@1.3.2\n   - deps: cookie-signature@1.0.4\n   - deps: express-session@~1.5.2\n   - deps: type-is@~1.3.2\n * deps: cookie-signature@1.0.4\n   - fix for timing attacks\n\n3.12.0 / 2014-06-21\n===================\n\n * use `media-typer` to alter content-type charset\n * deps: connect@2.21.0\n   - deprecate `connect(middleware)` -- use `app.use(middleware)` instead\n   - deprecate `connect.createServer()` -- use `connect()` instead\n   - fix `res.setHeader()` patch to work with get -> append -> set pattern\n   - deps: compression@~1.0.8\n   - deps: errorhandler@~1.1.1\n   - deps: express-session@~1.5.0\n   - deps: serve-index@~1.1.3\n\n3.11.0 / 2014-06-19\n===================\n\n * deprecate things with `depd` module\n * deps: buffer-crc32@0.2.3\n * deps: connect@2.20.2\n   - deprecate `verify` option to `json` -- use `body-parser` npm module instead\n   - deprecate `verify` option to `urlencoded` -- use `body-parser` npm module instead\n   - deprecate things with `depd` module\n   - use `finalhandler` for final response handling\n   - use `media-typer` to parse `content-type` for charset\n   - deps: body-parser@1.4.3\n   - deps: connect-timeout@1.1.1\n   - deps: cookie-parser@1.3.1\n   - deps: csurf@1.2.2\n   - deps: errorhandler@1.1.0\n   - deps: express-session@1.4.0\n   - deps: multiparty@3.2.9\n   - deps: serve-index@1.1.2\n   - deps: type-is@1.3.1\n   - deps: vhost@2.0.0\n\n3.10.5 / 2014-06-11\n===================\n\n * deps: connect@2.19.6\n   - deps: body-parser@1.3.1\n   - deps: compression@1.0.7\n   - deps: debug@1.0.2\n   - deps: serve-index@1.1.1\n   - deps: serve-static@1.2.3\n * deps: debug@1.0.2\n * deps: send@0.4.3\n   - Do not throw uncatchable error on file open race condition\n   - Use `escape-html` for HTML escaping\n   - deps: debug@1.0.2\n   - deps: finished@1.2.2\n   - deps: fresh@0.2.2\n\n3.10.4 / 2014-06-09\n===================\n\n * deps: connect@2.19.5\n   - fix \"event emitter leak\" warnings\n   - deps: csurf@1.2.1\n   - deps: debug@1.0.1\n   - deps: serve-static@1.2.2\n   - deps: type-is@1.2.1\n * deps: debug@1.0.1\n * deps: send@0.4.2\n   - fix \"event emitter leak\" warnings\n   - deps: finished@1.2.1\n   - deps: debug@1.0.1\n\n3.10.3 / 2014-06-05\n===================\n\n * use `vary` module for `res.vary`\n * deps: connect@2.19.4\n   - deps: errorhandler@1.0.2\n   - deps: method-override@2.0.2\n   - deps: serve-favicon@2.0.1\n * deps: debug@1.0.0\n\n3.10.2 / 2014-06-03\n===================\n\n * deps: connect@2.19.3\n   - deps: compression@1.0.6\n\n3.10.1 / 2014-06-03\n===================\n\n * deps: connect@2.19.2\n   - deps: compression@1.0.4\n * deps: proxy-addr@1.0.1\n\n3.10.0 / 2014-06-02\n===================\n\n * deps: connect@2.19.1\n   - deprecate `methodOverride()` -- use `method-override` npm module instead\n   - deps: body-parser@1.3.0\n   - deps: method-override@2.0.1\n   - deps: multiparty@3.2.8\n   - deps: response-time@2.0.0\n   - deps: serve-static@1.2.1\n * deps: methods@1.0.1\n * deps: send@0.4.1\n   - Send `max-age` in `Cache-Control` in correct format\n\n3.9.0 / 2014-05-30\n==================\n\n * custom etag control with `app.set('etag', val)`\n   - `app.set('etag', function(body, encoding){ return '\"etag\"' })` custom etag generation\n   - `app.set('etag', 'weak')` weak tag\n   - `app.set('etag', 'strong')` strong etag\n   - `app.set('etag', false)` turn off\n   - `app.set('etag', true)` standard etag\n * Include ETag in HEAD requests\n * mark `res.send` ETag as weak and reduce collisions\n * update connect to 2.18.0\n   - deps: compression@1.0.3\n   - deps: serve-index@1.1.0\n   - deps: serve-static@1.2.0\n * update send to 0.4.0\n   - Calculate ETag with md5 for reduced collisions\n   - Ignore stream errors after request ends\n   - deps: debug@0.8.1\n\n3.8.1 / 2014-05-27\n==================\n\n * update connect to 2.17.3\n   - deps: body-parser@1.2.2\n   - deps: express-session@1.2.1\n   - deps: method-override@1.0.2\n\n3.8.0 / 2014-05-21\n==================\n\n * keep previous `Content-Type` for `res.jsonp`\n * set proper `charset` in `Content-Type` for `res.send`\n * update connect to 2.17.1\n   - fix `res.charset` appending charset when `content-type` has one\n   - deps: express-session@1.2.0\n   - deps: morgan@1.1.1\n   - deps: serve-index@1.0.3\n\n3.7.0 / 2014-05-18\n==================\n\n * proper proxy trust with `app.set('trust proxy', trust)`\n   - `app.set('trust proxy', 1)` trust first hop\n   - `app.set('trust proxy', 'loopback')` trust loopback addresses\n   - `app.set('trust proxy', '10.0.0.1')` trust single IP\n   - `app.set('trust proxy', '10.0.0.1/16')` trust subnet\n   - `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list\n   - `app.set('trust proxy', false)` turn off\n   - `app.set('trust proxy', true)` trust everything\n * update connect to 2.16.2\n   - deprecate `res.headerSent` -- use `res.headersSent`\n   - deprecate `res.on(\"header\")` -- use on-headers module instead\n   - fix edge-case in `res.appendHeader` that would append in wrong order\n   - json: use body-parser\n   - urlencoded: use body-parser\n   - dep: bytes@1.0.0\n   - dep: cookie-parser@1.1.0\n   - dep: csurf@1.2.0\n   - dep: express-session@1.1.0\n   - dep: method-override@1.0.1\n\n3.6.0 / 2014-05-09\n==================\n\n * deprecate `app.del()` -- use `app.delete()` instead\n * deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead\n   - the edge-case `res.json(status, num)` requires `res.status(status).json(num)`\n * deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead\n   - the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)`\n * support PURGE method\n   - add `app.purge`\n   - add `router.purge`\n   - include PURGE in `app.all`\n * update connect to 2.15.0\n   * Add `res.appendHeader`\n   * Call error stack even when response has been sent\n   * Patch `res.headerSent` to return Boolean\n   * Patch `res.headersSent` for node.js 0.8\n   * Prevent default 404 handler after response sent\n   * dep: compression@1.0.2\n   * dep: connect-timeout@1.1.0\n   * dep: debug@^0.8.0\n   * dep: errorhandler@1.0.1\n   * dep: express-session@1.0.4\n   * dep: morgan@1.0.1\n   * dep: serve-favicon@2.0.0\n   * dep: serve-index@1.0.2\n * update debug to 0.8.0\n   * add `enable()` method\n   * change from stderr to stdout\n * update methods to 1.0.0\n   - add PURGE\n * update mkdirp to 0.5.0\n\n3.5.3 / 2014-05-08\n==================\n\n * fix `req.host` for IPv6 literals\n * fix `res.jsonp` error if callback param is object\n\n3.5.2 / 2014-04-24\n==================\n\n * update connect to 2.14.5\n * update cookie to 0.1.2\n * update mkdirp to 0.4.0\n * update send to 0.3.0\n\n3.5.1 / 2014-03-25\n==================\n\n * pin less-middleware in generated app\n\n3.5.0 / 2014-03-06\n==================\n\n * bump deps\n\n3.4.8 / 2014-01-13\n==================\n\n * prevent incorrect automatic OPTIONS responses #1868 @dpatti\n * update binary and examples for jade 1.0 #1876 @yossi, #1877 @reqshark, #1892 @matheusazzi\n * throw 400 in case of malformed paths @rlidwka\n\n3.4.7 / 2013-12-10\n==================\n\n * update connect\n\n3.4.6 / 2013-12-01\n==================\n\n * update connect (raw-body)\n\n3.4.5 / 2013-11-27\n==================\n\n * update connect\n * res.location: remove leading ./ #1802 @kapouer\n * res.redirect: fix `res.redirect('toString') #1829 @michaelficarra\n * res.send: always send ETag when content-length > 0\n * router: add Router.all() method\n\n3.4.4 / 2013-10-29\n==================\n\n * update connect\n * update supertest\n * update methods\n * express(1): replace bodyParser() with urlencoded() and json() #1795 @chirag04\n\n3.4.3 / 2013-10-23\n==================\n\n * update connect\n\n3.4.2 / 2013-10-18\n==================\n\n * update connect\n * downgrade commander\n\n3.4.1 / 2013-10-15\n==================\n\n * update connect\n * update commander\n * jsonp: check if callback is a function\n * router: wrap encodeURIComponent in a try/catch #1735 (@lxe)\n * res.format: now includes charset @1747 (@sorribas)\n * res.links: allow multiple calls @1746 (@sorribas)\n\n3.4.0 / 2013-09-07\n==================\n\n * add res.vary(). Closes #1682\n * update connect\n\n3.3.8 / 2013-09-02\n==================\n\n * update connect\n\n3.3.7 / 2013-08-28\n==================\n\n * update connect\n\n3.3.6 / 2013-08-27\n==================\n\n * Revert \"remove charset from json responses. Closes #1631\" (causes issues in some clients)\n * add: req.accepts take an argument list\n\n3.3.4 / 2013-07-08\n==================\n\n * update send and connect\n\n3.3.3 / 2013-07-04\n==================\n\n * update connect\n\n3.3.2 / 2013-07-03\n==================\n\n * update connect\n * update send\n * remove .version export\n\n3.3.1 / 2013-06-27\n==================\n\n * update connect\n\n3.3.0 / 2013-06-26\n==================\n\n * update connect\n * add support for multiple X-Forwarded-Proto values. Closes #1646\n * change: remove charset from json responses. Closes #1631\n * change: return actual booleans from req.accept* functions\n * fix jsonp callback array throw\n\n3.2.6 / 2013-06-02\n==================\n\n * update connect\n\n3.2.5 / 2013-05-21\n==================\n\n * update connect\n * update node-cookie\n * add: throw a meaningful error when there is no default engine\n * change generation of ETags with res.send() to GET requests only. Closes #1619\n\n3.2.4 / 2013-05-09\n==================\n\n  * fix `req.subdomains` when no Host is present\n  * fix `req.host` when no Host is present, return undefined\n\n3.2.3 / 2013-05-07\n==================\n\n  * update connect / qs\n\n3.2.2 / 2013-05-03\n==================\n\n  * update qs\n\n3.2.1 / 2013-04-29\n==================\n\n  * add app.VERB() paths array deprecation warning\n  * update connect\n  * update qs and remove all ~ semver crap\n  * fix: accept number as value of Signed Cookie\n\n3.2.0 / 2013-04-15\n==================\n\n  * add \"view\" constructor setting to override view behaviour\n  * add req.acceptsEncoding(name)\n  * add req.acceptedEncodings\n  * revert cookie signature change causing session race conditions\n  * fix sorting of Accept values of the same quality\n\n3.1.2 / 2013-04-12\n==================\n\n  * add support for custom Accept parameters\n  * update cookie-signature\n\n3.1.1 / 2013-04-01\n==================\n\n  * add X-Forwarded-Host support to `req.host`\n  * fix relative redirects\n  * update mkdirp\n  * update buffer-crc32\n  * remove legacy app.configure() method from app template.\n\n3.1.0 / 2013-01-25\n==================\n\n  * add support for leading \".\" in \"view engine\" setting\n  * add array support to `res.set()`\n  * add node 0.8.x to travis.yml\n  * add \"subdomain offset\" setting for tweaking `req.subdomains`\n  * add `res.location(url)` implementing `res.redirect()`-like setting of Location\n  * use app.get() for x-powered-by setting for inheritance\n  * fix colons in passwords for `req.auth`\n\n3.0.6 / 2013-01-04\n==================\n\n  * add http verb methods to Router\n  * update connect\n  * fix mangling of the `res.cookie()` options object\n  * fix jsonp whitespace escape. Closes #1132\n\n3.0.5 / 2012-12-19\n==================\n\n  * add throwing when a non-function is passed to a route\n  * fix: explicitly remove Transfer-Encoding header from 204 and 304 responses\n  * revert \"add 'etag' option\"\n\n3.0.4 / 2012-12-05\n==================\n\n  * add 'etag' option to disable `res.send()` Etags\n  * add escaping of urls in text/plain in `res.redirect()`\n    for old browsers interpreting as html\n  * change crc32 module for a more liberal license\n  * update connect\n\n3.0.3 / 2012-11-13\n==================\n\n  * update connect\n  * update cookie module\n  * fix cookie max-age\n\n3.0.2 / 2012-11-08\n==================\n\n  * add OPTIONS to cors example. Closes #1398\n  * fix route chaining regression. Closes #1397\n\n3.0.1 / 2012-11-01\n==================\n\n  * update connect\n\n3.0.0 / 2012-10-23\n==================\n\n  * add `make clean`\n  * add \"Basic\" check to req.auth\n  * add `req.auth` test coverage\n  * add cb && cb(payload) to `res.jsonp()`. Closes #1374\n  * add backwards compat for `res.redirect()` status. Closes #1336\n  * add support for `res.json()` to retain previously defined Content-Types. Closes #1349\n  * update connect\n  * change `res.redirect()` to utilize a pathname-relative Location again. Closes #1382\n  * remove non-primitive string support for `res.send()`\n  * fix view-locals example. Closes #1370\n  * fix route-separation example\n\n3.0.0rc5 / 2012-09-18\n==================\n\n  * update connect\n  * add redis search example\n  * add static-files example\n  * add \"x-powered-by\" setting (`app.disable('x-powered-by')`)\n  * add \"application/octet-stream\" redirect Accept test case. Closes #1317\n\n3.0.0rc4 / 2012-08-30\n==================\n\n  * add `res.jsonp()`. Closes #1307\n  * add \"verbose errors\" option to error-pages example\n  * add another route example to express(1) so people are not so confused\n  * add redis online user activity tracking example\n  * update connect dep\n  * fix etag quoting. Closes #1310\n  * fix error-pages 404 status\n  * fix jsonp callback char restrictions\n  * remove old OPTIONS default response\n\n3.0.0rc3 / 2012-08-13\n==================\n\n  * update connect dep\n  * fix signed cookies to work with `connect.cookieParser()` (\"s:\" prefix was missing) [tnydwrds]\n  * fix `res.render()` clobbering of \"locals\"\n\n3.0.0rc2 / 2012-08-03\n==================\n\n  * add CORS example\n  * update connect dep\n  * deprecate `.createServer()` & remove old stale examples\n  * fix: escape `res.redirect()` link\n  * fix vhost example\n\n3.0.0rc1 / 2012-07-24\n==================\n\n  * add more examples to view-locals\n  * add scheme-relative redirects (`res.redirect(\"//foo.com\")`) support\n  * update cookie dep\n  * update connect dep\n  * update send dep\n  * fix `express(1)` -h flag, use -H for hogan. Closes #1245\n  * fix `res.sendfile()` socket error handling regression\n\n3.0.0beta7 / 2012-07-16\n==================\n\n  * update connect dep for `send()` root normalization regression\n\n3.0.0beta6 / 2012-07-13\n==================\n\n  * add `err.view` property for view errors. Closes #1226\n  * add \"jsonp callback name\" setting\n  * add support for \"/foo/:bar*\" non-greedy matches\n  * change `res.sendfile()` to use `send()` module\n  * change `res.send` to use \"response-send\" module\n  * remove `app.locals.use` and `res.locals.use`, use regular middleware\n\n3.0.0beta5 / 2012-07-03\n==================\n\n  * add \"make check\" support\n  * add route-map example\n  * add `res.json(obj, status)` support back for BC\n  * add \"methods\" dep, remove internal methods module\n  * update connect dep\n  * update auth example to utilize cores pbkdf2\n  * updated tests to use \"supertest\"\n\n3.0.0beta4 / 2012-06-25\n==================\n\n  * Added `req.auth`\n  * Added `req.range(size)`\n  * Added `res.links(obj)`\n  * Added `res.send(body, status)` support back for backwards compat\n  * Added `.default()` support to `res.format()`\n  * Added 2xx / 304 check to `req.fresh`\n  * Revert \"Added + support to the router\"\n  * Fixed `res.send()` freshness check, respect res.statusCode\n\n3.0.0beta3 / 2012-06-15\n==================\n\n  * Added hogan `--hjs` to express(1) [nullfirm]\n  * Added another example to content-negotiation\n  * Added `fresh` dep\n  * Changed: `res.send()` always checks freshness\n  * Fixed: expose connects mime module. Closes #1165\n\n3.0.0beta2 / 2012-06-06\n==================\n\n  * Added `+` support to the router\n  * Added `req.host`\n  * Changed `req.param()` to check route first\n  * Update connect dep\n\n3.0.0beta1 / 2012-06-01\n==================\n\n  * Added `res.format()` callback to override default 406 behaviour\n  * Fixed `res.redirect()` 406. Closes #1154\n\n3.0.0alpha5 / 2012-05-30\n==================\n\n  * Added `req.ip`\n  * Added `{ signed: true }` option to `res.cookie()`\n  * Removed `res.signedCookie()`\n  * Changed: dont reverse `req.ips`\n  * Fixed \"trust proxy\" setting check for `req.ips`\n\n3.0.0alpha4 / 2012-05-09\n==================\n\n  * Added: allow `[]` in jsonp callback. Closes #1128\n  * Added `PORT` env var support in generated template. Closes #1118 [benatkin]\n  * Updated: connect 2.2.2\n\n3.0.0alpha3 / 2012-05-04\n==================\n\n  * Added public `app.routes`. Closes #887\n  * Added _view-locals_ example\n  * Added _mvc_ example\n  * Added `res.locals.use()`. Closes #1120\n  * Added conditional-GET support to `res.send()`\n  * Added: coerce `res.set()` values to strings\n  * Changed: moved `static()` in generated apps below router\n  * Changed: `res.send()` only set ETag when not previously set\n  * Changed connect 2.2.1 dep\n  * Changed: `make test` now runs unit / acceptance tests\n  * Fixed req/res proto inheritance\n\n3.0.0alpha2 / 2012-04-26\n==================\n\n  * Added `make benchmark` back\n  * Added `res.send()` support for `String` objects\n  * Added client-side data exposing example\n  * Added `res.header()` and `req.header()` aliases for BC\n  * Added `express.createServer()` for BC\n  * Perf: memoize parsed urls\n  * Perf: connect 2.2.0 dep\n  * Changed: make `expressInit()` middleware self-aware\n  * Fixed: use app.get() for all core settings\n  * Fixed redis session example\n  * Fixed session example. Closes #1105\n  * Fixed generated express dep. Closes #1078\n\n3.0.0alpha1 / 2012-04-15\n==================\n\n  * Added `app.locals.use(callback)`\n  * Added `app.locals` object\n  * Added `app.locals(obj)`\n  * Added `res.locals` object\n  * Added `res.locals(obj)`\n  * Added `res.format()` for content-negotiation\n  * Added `app.engine()`\n  * Added `res.cookie()` JSON cookie support\n  * Added \"trust proxy\" setting\n  * Added `req.subdomains`\n  * Added `req.protocol`\n  * Added `req.secure`\n  * Added `req.path`\n  * Added `req.ips`\n  * Added `req.fresh`\n  * Added `req.stale`\n  * Added comma-delimited / array support for `req.accepts()`\n  * Added debug instrumentation\n  * Added `res.set(obj)`\n  * Added `res.set(field, value)`\n  * Added `res.get(field)`\n  * Added `app.get(setting)`. Closes #842\n  * Added `req.acceptsLanguage()`\n  * Added `req.acceptsCharset()`\n  * Added `req.accepted`\n  * Added `req.acceptedLanguages`\n  * Added `req.acceptedCharsets`\n  * Added \"json replacer\" setting\n  * Added \"json spaces\" setting\n  * Added X-Forwarded-Proto support to `res.redirect()`. Closes #92\n  * Added `--less` support to express(1)\n  * Added `express.response` prototype\n  * Added `express.request` prototype\n  * Added `express.application` prototype\n  * Added `app.path()`\n  * Added `app.render()`\n  * Added `res.type()` to replace `res.contentType()`\n  * Changed: `res.redirect()` to add relative support\n  * Changed: enable \"jsonp callback\" by default\n  * Changed: renamed \"case sensitive routes\" to \"case sensitive routing\"\n  * Rewrite of all tests with mocha\n  * Removed \"root\" setting\n  * Removed `res.redirect('home')` support\n  * Removed `req.notify()`\n  * Removed `app.register()`\n  * Removed `app.redirect()`\n  * Removed `app.is()`\n  * Removed `app.helpers()`\n  * Removed `app.dynamicHelpers()`\n  * Fixed `res.sendfile()` with non-GET. Closes #723\n  * Fixed express(1) public dir for windows. Closes #866\n\n2.5.9/ 2012-04-02\n==================\n\n  * Added support for PURGE request method [pbuyle]\n  * Fixed `express(1)` generated app `app.address()` before `listening` [mmalecki]\n\n2.5.8 / 2012-02-08\n==================\n\n  * Update mkdirp dep. Closes #991\n\n2.5.7 / 2012-02-06\n==================\n\n  * Fixed `app.all` duplicate DELETE requests [mscdex]\n\n2.5.6 / 2012-01-13\n==================\n\n  * Updated hamljs dev dep. Closes #953\n\n2.5.5 / 2012-01-08\n==================\n\n  * Fixed: set `filename` on cached templates [matthewleon]\n\n2.5.4 / 2012-01-02\n==================\n\n  * Fixed `express(1)` eol on 0.4.x. Closes #947\n\n2.5.3 / 2011-12-30\n==================\n\n  * Fixed `req.is()` when a charset is present\n\n2.5.2 / 2011-12-10\n==================\n\n  * Fixed: express(1) LF -> CRLF for windows\n\n2.5.1 / 2011-11-17\n==================\n\n  * Changed: updated connect to 1.8.x\n  * Removed sass.js support from express(1)\n\n2.5.0 / 2011-10-24\n==================\n\n  * Added ./routes dir for generated app by default\n  * Added npm install reminder to express(1) app gen\n  * Added 0.5.x support\n  * Removed `make test-cov` since it wont work with node 0.5.x\n  * Fixed express(1) public dir for windows. Closes #866\n\n2.4.7 / 2011-10-05\n==================\n\n  * Added mkdirp to express(1). Closes #795\n  * Added simple _json-config_ example\n  * Added  shorthand for the parsed request's pathname via `req.path`\n  * Changed connect dep to 1.7.x to fix npm issue...\n  * Fixed `res.redirect()` __HEAD__ support. [reported by xerox]\n  * Fixed `req.flash()`, only escape args\n  * Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie]\n\n2.4.6 / 2011-08-22\n==================\n\n  * Fixed multiple param callback regression. Closes #824 [reported by TroyGoode]\n\n2.4.5 / 2011-08-19\n==================\n\n  * Added support for routes to handle errors. Closes #809\n  * Added `app.routes.all()`. Closes #803\n  * Added \"basepath\" setting to work in conjunction with reverse proxies etc.\n  * Refactored `Route` to use a single array of callbacks\n  * Added support for multiple callbacks for `app.param()`. Closes #801\nCloses #805\n  * Changed: removed .call(self) for route callbacks\n  * Dependency: `qs >= 0.3.1`\n  * Fixed `res.redirect()` on windows due to `join()` usage. Closes #808\n\n2.4.4 / 2011-08-05\n==================\n\n  * Fixed `res.header()` intention of a set, even when `undefined`\n  * Fixed `*`, value no longer required\n  * Fixed `res.send(204)` support. Closes #771\n\n2.4.3 / 2011-07-14\n==================\n\n  * Added docs for `status` option special-case. Closes #739\n  * Fixed `options.filename`, exposing the view path to template engines\n\n2.4.2. / 2011-07-06\n==================\n\n  * Revert \"removed jsonp stripping\" for XSS\n\n2.4.1 / 2011-07-06\n==================\n\n  * Added `res.json()` JSONP support. Closes #737\n  * Added _extending-templates_ example. Closes #730\n  * Added \"strict routing\" setting for trailing slashes\n  * Added support for multiple envs in `app.configure()` calls. Closes #735\n  * Changed: `res.send()` using `res.json()`\n  * Changed: when cookie `path === null` don't default it\n  * Changed; default cookie path to \"home\" setting. Closes #731\n  * Removed _pids/logs_ creation from express(1)\n\n2.4.0 / 2011-06-28\n==================\n\n  * Added chainable `res.status(code)`\n  * Added `res.json()`, an explicit version of `res.send(obj)`\n  * Added simple web-service example\n\n2.3.12 / 2011-06-22\n==================\n\n  * \\#express is now on freenode! come join!\n  * Added `req.get(field, param)`\n  * Added links to Japanese documentation, thanks @hideyukisaito!\n  * Added; the `express(1)` generated app outputs the env\n  * Added `content-negotiation` example\n  * Dependency: connect >= 1.5.1 < 2.0.0\n  * Fixed view layout bug. Closes #720\n  * Fixed; ignore body on 304. Closes #701\n\n2.3.11 / 2011-06-04\n==================\n\n  * Added `npm test`\n  * Removed generation of dummy test file from `express(1)`\n  * Fixed; `express(1)` adds express as a dep\n  * Fixed; prune on `prepublish`\n\n2.3.10 / 2011-05-27\n==================\n\n  * Added `req.route`, exposing the current route\n  * Added _package.json_ generation support to `express(1)`\n  * Fixed call to `app.param()` function for optional params. Closes #682\n\n2.3.9 / 2011-05-25\n==================\n\n  * Fixed bug-ish with `../' in `res.partial()` calls\n\n2.3.8 / 2011-05-24\n==================\n\n  * Fixed `app.options()`\n\n2.3.7 / 2011-05-23\n==================\n\n  * Added route `Collection`, ex: `app.get('/user/:id').remove();`\n  * Added support for `app.param(fn)` to define param logic\n  * Removed `app.param()` support for callback with return value\n  * Removed module.parent check from express(1) generated app. Closes #670\n  * Refactored router. Closes #639\n\n2.3.6 / 2011-05-20\n==================\n\n  * Changed; using devDependencies instead of git submodules\n  * Fixed redis session example\n  * Fixed markdown example\n  * Fixed view caching, should not be enabled in development\n\n2.3.5 / 2011-05-20\n==================\n\n  * Added export `.view` as alias for `.View`\n\n2.3.4 / 2011-05-08\n==================\n\n  * Added `./examples/say`\n  * Fixed `res.sendfile()` bug preventing the transfer of files with spaces\n\n2.3.3 / 2011-05-03\n==================\n\n  * Added \"case sensitive routes\" option.\n  * Changed; split methods supported per rfc [slaskis]\n  * Fixed route-specific middleware when using the same callback function several times\n\n2.3.2 / 2011-04-27\n==================\n\n  * Fixed view hints\n\n2.3.1 / 2011-04-26\n==================\n\n  * Added `app.match()` as `app.match.all()`\n  * Added `app.lookup()` as `app.lookup.all()`\n  * Added `app.remove()` for `app.remove.all()`\n  * Added `app.remove.VERB()`\n  * Fixed template caching collision issue. Closes #644\n  * Moved router over from connect and started refactor\n\n2.3.0 / 2011-04-25\n==================\n\n  * Added options support to `res.clearCookie()`\n  * Added `res.helpers()` as alias of `res.locals()`\n  * Added; json defaults to UTF-8 with `res.send()`. Closes #632. [Daniel   * Dependency `connect >= 1.4.0`\n  * Changed; auto set Content-Type in res.attachement [Aaron Heckmann]\n  * Renamed \"cache views\" to \"view cache\". Closes #628\n  * Fixed caching of views when using several apps. Closes #637\n  * Fixed gotcha invoking `app.param()` callbacks once per route middleware.\nCloses #638\n  * Fixed partial lookup precedence. Closes #631\nShaw]\n\n2.2.2 / 2011-04-12\n==================\n\n  * Added second callback support for `res.download()` connection errors\n  * Fixed `filename` option passing to template engine\n\n2.2.1 / 2011-04-04\n==================\n\n  * Added `layout(path)` helper to change the layout within a view. Closes #610\n  * Fixed `partial()` collection object support.\n    Previously only anything with `.length` would work.\n    When `.length` is present one must still be aware of holes,\n    however now `{ collection: {foo: 'bar'}}` is valid, exposes\n    `keyInCollection` and `keysInCollection`.\n\n  * Performance improved with better view caching\n  * Removed `request` and `response` locals\n  * Changed; errorHandler page title is now `Express` instead of `Connect`\n\n2.2.0 / 2011-03-30\n==================\n\n  * Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606\n  * Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606\n  * Added `app.VERB(path)` as alias of `app.lookup.VERB()`.\n  * Dependency `connect >= 1.2.0`\n\n2.1.1 / 2011-03-29\n==================\n\n  * Added; expose `err.view` object when failing to locate a view\n  * Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann]\n  * Fixed; `res.send(undefined)` responds with 204 [aheckmann]\n\n2.1.0 / 2011-03-24\n==================\n\n  * Added `<root>/_?<name>` partial lookup support. Closes #447\n  * Added `request`, `response`, and `app` local variables\n  * Added `settings` local variable, containing the app's settings\n  * Added `req.flash()` exception if `req.session` is not available\n  * Added `res.send(bool)` support (json response)\n  * Fixed stylus example for latest version\n  * Fixed; wrap try/catch around `res.render()`\n\n2.0.0 / 2011-03-17\n==================\n\n  * Fixed up index view path alternative.\n  * Changed; `res.locals()` without object returns the locals\n\n2.0.0rc3 / 2011-03-17\n==================\n\n  * Added `res.locals(obj)` to compliment `res.local(key, val)`\n  * Added `res.partial()` callback support\n  * Fixed recursive error reporting issue in `res.render()`\n\n2.0.0rc2 / 2011-03-17\n==================\n\n  * Changed; `partial()` \"locals\" are now optional\n  * Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01]\n  * Fixed .filename view engine option [reported by drudge]\n  * Fixed blog example\n  * Fixed `{req,res}.app` reference when mounting [Ben Weaver]\n\n2.0.0rc / 2011-03-14\n==================\n\n  * Fixed; expose `HTTPSServer` constructor\n  * Fixed express(1) default test charset. Closes #579 [reported by secoif]\n  * Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]\n\n2.0.0beta3 / 2011-03-09\n==================\n\n  * Added support for `res.contentType()` literal\n    The original `res.contentType('.json')`,\n    `res.contentType('application/json')`, and `res.contentType('json')`\n    will work now.\n  * Added `res.render()` status option support back\n  * Added charset option for `res.render()`\n  * Added `.charset` support (via connect 1.0.4)\n  * Added view resolution hints when in development and a lookup fails\n  * Added layout lookup support relative to the page view.\n    For example while rendering `./views/user/index.jade` if you create\n    `./views/user/layout.jade` it will be used in favour of the root layout.\n  * Fixed `res.redirect()`. RFC states absolute url [reported by unlink]\n  * Fixed; default `res.send()` string charset to utf8\n  * Removed `Partial` constructor (not currently used)\n\n2.0.0beta2 / 2011-03-07\n==================\n\n  * Added res.render() `.locals` support back to aid in migration process\n  * Fixed flash example\n\n2.0.0beta / 2011-03-03\n==================\n\n  * Added HTTPS support\n  * Added `res.cookie()` maxAge support\n  * Added `req.header()` _Referrer_ / _Referer_ special-case, either works\n  * Added mount support for `res.redirect()`, now respects the mount-point\n  * Added `union()` util, taking place of `merge(clone())` combo\n  * Added stylus support to express(1) generated app\n  * Added secret to session middleware used in examples and generated app\n  * Added `res.local(name, val)` for progressive view locals\n  * Added default param support to `req.param(name, default)`\n  * Added `app.disabled()` and `app.enabled()`\n  * Added `app.register()` support for omitting leading \".\", either works\n  * Added `res.partial()`, using the same interface as `partial()` within a view. Closes #539\n  * Added `app.param()` to map route params to async/sync logic\n  * Added; aliased `app.helpers()` as `app.locals()`. Closes #481\n  * Added extname with no leading \".\" support to `res.contentType()`\n  * Added `cache views` setting, defaulting to enabled in \"production\" env\n  * Added index file partial resolution, eg: partial('user') may try _views/user/index.jade_.\n  * Added `req.accepts()` support for extensions\n  * Changed; `res.download()` and `res.sendfile()` now utilize Connect's\n    static file server `connect.static.send()`.\n  * Changed; replaced `connect.utils.mime()` with npm _mime_ module\n  * Changed; allow `req.query` to be pre-defined (via middleware or other parent\n  * Changed view partial resolution, now relative to parent view\n  * Changed view engine signature. no longer `engine.render(str, options, callback)`, now `engine.compile(str, options) -> Function`, the returned function accepts `fn(locals)`.\n  * Fixed `req.param()` bug returning Array.prototype methods. Closes #552\n  * Fixed; using `Stream#pipe()` instead of `sys.pump()` in `res.sendfile()`\n  * Fixed; using _qs_ module instead of _querystring_\n  * Fixed; strip unsafe chars from jsonp callbacks\n  * Removed \"stream threshold\" setting\n\n1.0.8 / 2011-03-01\n==================\n\n  * Allow `req.query` to be pre-defined (via middleware or other parent app)\n  * \"connect\": \">= 0.5.0 < 1.0.0\". Closes #547\n  * Removed the long deprecated __EXPRESS_ENV__ support\n\n1.0.7 / 2011-02-07\n==================\n\n  * Fixed `render()` setting inheritance.\n    Mounted apps would not inherit \"view engine\"\n\n1.0.6 / 2011-02-07\n==================\n\n  * Fixed `view engine` setting bug when period is in dirname\n\n1.0.5 / 2011-02-05\n==================\n\n  * Added secret to generated app `session()` call\n\n1.0.4 / 2011-02-05\n==================\n\n  * Added `qs` dependency to _package.json_\n  * Fixed namespaced `require()`s for latest connect support\n\n1.0.3 / 2011-01-13\n==================\n\n  * Remove unsafe characters from JSONP callback names [Ryan Grove]\n\n1.0.2 / 2011-01-10\n==================\n\n  * Removed nested require, using `connect.router`\n\n1.0.1 / 2010-12-29\n==================\n\n  * Fixed for middleware stacked via `createServer()`\n    previously the `foo` middleware passed to `createServer(foo)`\n    would not have access to Express methods such as `res.send()`\n    or props like `req.query` etc.\n\n1.0.0 / 2010-11-16\n==================\n\n  * Added; deduce partial object names from the last segment.\n    For example by default `partial('forum/post', postObject)` will\n    give you the _post_ object, providing a meaningful default.\n  * Added http status code string representation to `res.redirect()` body\n  * Added; `res.redirect()` supporting _text/plain_ and _text/html_ via __Accept__.\n  * Added `req.is()` to aid in content negotiation\n  * Added partial local inheritance [suggested by masylum]. Closes #102\n    providing access to parent template locals.\n  * Added _-s, --session[s]_ flag to express(1) to add session related middleware\n  * Added _--template_ flag to express(1) to specify the\n    template engine to use.\n  * Added _--css_ flag to express(1) to specify the\n    stylesheet engine to use (or just plain css by default).\n  * Added `app.all()` support [thanks aheckmann]\n  * Added partial direct object support.\n    You may now `partial('user', user)` providing the \"user\" local,\n    vs previously `partial('user', { object: user })`.\n  * Added _route-separation_ example since many people question ways\n    to do this with CommonJS modules. Also view the _blog_ example for\n    an alternative.\n  * Performance; caching view path derived partial object names\n  * Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454\n  * Fixed jsonp support; _text/javascript_ as per mailinglist discussion\n\n1.0.0rc4 / 2010-10-14\n==================\n\n  * Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0\n  * Added route-middleware support (very helpful, see the [docs](http://expressjs.com/guide.html#Route-Middleware))\n  * Added _jsonp callback_ setting to enable/disable jsonp autowrapping [Dav Glass]\n  * Added callback query check on response.send to autowrap JSON objects for simple webservice implementations [Dav Glass]\n  * Added `partial()` support for array-like collections. Closes #434\n  * Added support for swappable querystring parsers\n  * Added session usage docs. Closes #443\n  * Added dynamic helper caching. Closes #439 [suggested by maritz]\n  * Added authentication example\n  * Added basic Range support to `res.sendfile()` (and `res.download()` etc)\n  * Changed; `express(1)` generated app using 2 spaces instead of 4\n  * Default env to \"development\" again [aheckmann]\n  * Removed _context_ option is no more, use \"scope\"\n  * Fixed; exposing _./support_ libs to examples so they can run without installs\n  * Fixed mvc example\n\n1.0.0rc3 / 2010-09-20\n==================\n\n  * Added confirmation for `express(1)` app generation. Closes #391\n  * Added extending of flash formatters via `app.flashFormatters`\n  * Added flash formatter support. Closes #411\n  * Added streaming support to `res.sendfile()` using `sys.pump()` when >= \"stream threshold\"\n  * Added _stream threshold_ setting for `res.sendfile()`\n  * Added `res.send()` __HEAD__ support\n  * Added `res.clearCookie()`\n  * Added `res.cookie()`\n  * Added `res.render()` headers option\n  * Added `res.redirect()` response bodies\n  * Added `res.render()` status option support. Closes #425 [thanks aheckmann]\n  * Fixed `res.sendfile()` responding with 403 on malicious path\n  * Fixed `res.download()` bug; when an error occurs remove _Content-Disposition_\n  * Fixed; mounted apps settings now inherit from parent app [aheckmann]\n  * Fixed; stripping Content-Length / Content-Type when 204\n  * Fixed `res.send()` 204. Closes #419\n  * Fixed multiple _Set-Cookie_ headers via `res.header()`. Closes #402\n  * Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo]\n\n\n1.0.0rc2 / 2010-08-17\n==================\n\n  * Added `app.register()` for template engine mapping. Closes #390\n  * Added `res.render()` callback support as second argument (no options)\n  * Added callback support to `res.download()`\n  * Added callback support for `res.sendfile()`\n  * Added support for middleware access via `express.middlewareName()` vs `connect.middlewareName()`\n  * Added \"partials\" setting to docs\n  * Added default expresso tests to `express(1)` generated app. Closes #384\n  * Fixed `res.sendfile()` error handling, defer via `next()`\n  * Fixed `res.render()` callback when a layout is used [thanks guillermo]\n  * Fixed; `make install` creating ~/.node_libraries when not present\n  * Fixed issue preventing error handlers from being defined anywhere. Closes #387\n\n1.0.0rc / 2010-07-28\n==================\n\n  * Added mounted hook. Closes #369\n  * Added connect dependency to _package.json_\n\n  * Removed \"reload views\" setting and support code\n    development env never caches, production always caches.\n\n  * Removed _param_ in route callbacks, signature is now\n    simply (req, res, next), previously (req, res, params, next).\n    Use _req.params_ for path captures, _req.query_ for GET params.\n\n  * Fixed \"home\" setting\n  * Fixed middleware/router precedence issue. Closes #366\n  * Fixed; _configure()_ callbacks called immediately. Closes #368\n\n1.0.0beta2 / 2010-07-23\n==================\n\n  * Added more examples\n  * Added; exporting `Server` constructor\n  * Added `Server#helpers()` for view locals\n  * Added `Server#dynamicHelpers()` for dynamic view locals. Closes #349\n  * Added support for absolute view paths\n  * Added; _home_ setting defaults to `Server#route` for mounted apps. Closes #363\n  * Added Guillermo Rauch to the contributor list\n  * Added support for \"as\" for non-collection partials. Closes #341\n  * Fixed _install.sh_, ensuring _~/.node_libraries_ exists. Closes #362 [thanks jf]\n  * Fixed `res.render()` exceptions, now passed to `next()` when no callback is given [thanks guillermo]\n  * Fixed instanceof `Array` checks, now `Array.isArray()`\n  * Fixed express(1) expansion of public dirs. Closes #348\n  * Fixed middleware precedence. Closes #345\n  * Fixed view watcher, now async [thanks aheckmann]\n\n1.0.0beta / 2010-07-15\n==================\n\n  * Re-write\n    - much faster\n    - much lighter\n    - Check [ExpressJS.com](http://expressjs.com) for migration guide and updated docs\n\n0.14.0 / 2010-06-15\n==================\n\n  * Utilize relative requires\n  * Added Static bufferSize option [aheckmann]\n  * Fixed caching of view and partial subdirectories [aheckmann]\n  * Fixed mime.type() comments now that \".ext\" is not supported\n  * Updated haml submodule\n  * Updated class submodule\n  * Removed bin/express\n\n0.13.0 / 2010-06-01\n==================\n\n  * Added node v0.1.97 compatibility\n  * Added support for deleting cookies via Request#cookie('key', null)\n  * Updated haml submodule\n  * Fixed not-found page, now using charset utf-8\n  * Fixed show-exceptions page, now using charset utf-8\n  * Fixed view support due to fs.readFile Buffers\n  * Changed; mime.type() no longer accepts \".type\" due to node extname() changes\n\n0.12.0 / 2010-05-22\n==================\n\n  * Added node v0.1.96 compatibility\n  * Added view `helpers` export which act as additional local variables\n  * Updated haml submodule\n  * Changed ETag; removed inode, modified time only\n  * Fixed LF to CRLF for setting multiple cookies\n  * Fixed cookie compilation; values are now urlencoded\n  * Fixed cookies parsing; accepts quoted values and url escaped cookies\n\n0.11.0 / 2010-05-06\n==================\n\n  * Added support for layouts using different engines\n    - this.render('page.html.haml', { layout: 'super-cool-layout.html.ejs' })\n    - this.render('page.html.haml', { layout: 'foo' }) // assumes 'foo.html.haml'\n    - this.render('page.html.haml', { layout: false }) // no layout\n  * Updated ext submodule\n  * Updated haml submodule\n  * Fixed EJS partial support by passing along the context. Issue #307\n\n0.10.1 / 2010-05-03\n==================\n\n  * Fixed binary uploads.\n\n0.10.0 / 2010-04-30\n==================\n\n  * Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s\n    encoding is set to 'utf8' or 'utf-8').\n  * Added \"encoding\" option to Request#render(). Closes #299\n  * Added \"dump exceptions\" setting, which is enabled by default.\n  * Added simple ejs template engine support\n  * Added error response support for text/plain, application/json. Closes #297\n  * Added callback function param to Request#error()\n  * Added Request#sendHead()\n  * Added Request#stream()\n  * Added support for Request#respond(304, null) for empty response bodies\n  * Added ETag support to Request#sendfile()\n  * Added options to Request#sendfile(), passed to fs.createReadStream()\n  * Added filename arg to Request#download()\n  * Performance enhanced due to pre-reversing plugins so that plugins.reverse() is not called on each request\n  * Performance enhanced by preventing several calls to toLowerCase() in Router#match()\n  * Changed; Request#sendfile() now streams\n  * Changed; Renamed Request#halt() to Request#respond(). Closes #289\n  * Changed; Using sys.inspect() instead of JSON.encode() for error output\n  * Changed; run() returns the http.Server instance. Closes #298\n  * Changed; Defaulting Server#host to null (INADDR_ANY)\n  * Changed; Logger \"common\" format scale of 0.4f\n  * Removed Logger \"request\" format\n  * Fixed; Catching ENOENT in view caching, preventing error when \"views/partials\" is not found\n  * Fixed several issues with http client\n  * Fixed Logger Content-Length output\n  * Fixed bug preventing Opera from retaining the generated session id. Closes #292\n\n0.9.0 / 2010-04-14\n==================\n\n  * Added DSL level error() route support\n  * Added DSL level notFound() route support\n  * Added Request#error()\n  * Added Request#notFound()\n  * Added Request#render() callback function. Closes #258\n  * Added \"max upload size\" setting\n  * Added \"magic\" variables to collection partials (\\_\\_index\\_\\_, \\_\\_length\\_\\_, \\_\\_isFirst\\_\\_, \\_\\_isLast\\_\\_). Closes #254\n  * Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js\n  * Added callback function support to Request#halt() as 3rd/4th arg\n  * Added preprocessing of route param wildcards using param(). Closes #251\n  * Added view partial support (with collections etc.)\n  * Fixed bug preventing falsey params (such as ?page=0). Closes #286\n  * Fixed setting of multiple cookies. Closes #199\n  * Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)\n  * Changed; session cookie is now httpOnly\n  * Changed; Request is no longer global\n  * Changed; Event is no longer global\n  * Changed; \"sys\" module is no longer global\n  * Changed; moved Request#download to Static plugin where it belongs\n  * Changed; Request instance created before body parsing. Closes #262\n  * Changed; Pre-caching views in memory when \"cache view contents\" is enabled. Closes #253\n  * Changed; Pre-caching view partials in memory when \"cache view partials\" is enabled\n  * Updated support to node --version 0.1.90\n  * Updated dependencies\n  * Removed set(\"session cookie\") in favour of use(Session, { cookie: { ... }})\n  * Removed utils.mixin(); use Object#mergeDeep()\n\n0.8.0 / 2010-03-19\n==================\n\n  * Added coffeescript example app. Closes #242\n  * Changed; cache api now async friendly. Closes #240\n  * Removed deprecated 'express/static' support. Use 'express/plugins/static'\n\n0.7.6 / 2010-03-19\n==================\n\n  * Added Request#isXHR. Closes #229\n  * Added `make install` (for the executable)\n  * Added `express` executable for setting up simple app templates\n  * Added \"GET /public/*\" to Static plugin, defaulting to <root>/public\n  * Added Static plugin\n  * Fixed; Request#render() only calls cache.get() once\n  * Fixed; Namespacing View caches with \"view:\"\n  * Fixed; Namespacing Static caches with \"static:\"\n  * Fixed; Both example apps now use the Static plugin\n  * Fixed set(\"views\"). Closes #239\n  * Fixed missing space for combined log format\n  * Deprecated Request#sendfile() and 'express/static'\n  * Removed Server#running\n\n0.7.5 / 2010-03-16\n==================\n\n  * Added Request#flash() support without args, now returns all flashes\n  * Updated ext submodule\n\n0.7.4 / 2010-03-16\n==================\n\n  * Fixed session reaper\n  * Changed; class.js replacing js-oo Class implementation (quite a bit faster, no browser cruft)\n\n0.7.3 / 2010-03-16\n==================\n\n  * Added package.json\n  * Fixed requiring of haml / sass due to kiwi removal\n\n0.7.2 / 2010-03-16\n==================\n\n  * Fixed GIT submodules (HAH!)\n\n0.7.1 / 2010-03-16\n==================\n\n  * Changed; Express now using submodules again until a PM is adopted\n  * Changed; chat example using millisecond conversions from ext\n\n0.7.0 / 2010-03-15\n==================\n\n  * Added Request#pass() support (finds the next matching route, or the given path)\n  * Added Logger plugin (default \"common\" format replaces CommonLogger)\n  * Removed Profiler plugin\n  * Removed CommonLogger plugin\n\n0.6.0 / 2010-03-11\n==================\n\n  * Added seed.yml for kiwi package management support\n  * Added HTTP client query string support when method is GET. Closes #205\n\n  * Added support for arbitrary view engines.\n    For example \"foo.engine.html\" will now require('engine'),\n    the exports from this module are cached after the first require().\n\n  * Added async plugin support\n\n  * Removed usage of RESTful route funcs as http client\n    get() etc, use http.get() and friends\n\n  * Removed custom exceptions\n\n0.5.0 / 2010-03-10\n==================\n\n  * Added ext dependency (library of js extensions)\n  * Removed extname() / basename() utils. Use path module\n  * Removed toArray() util. Use arguments.values\n  * Removed escapeRegexp() util. Use RegExp.escape()\n  * Removed process.mixin() dependency. Use utils.mixin()\n  * Removed Collection\n  * Removed ElementCollection\n  * Shameless self promotion of ebook \"Advanced JavaScript\" (http://dev-mag.com)  ;)\n\n0.4.0 / 2010-02-11\n==================\n\n  * Added flash() example to sample upload app\n  * Added high level restful http client module (express/http)\n  * Changed; RESTful route functions double as HTTP clients. Closes #69\n  * Changed; throwing error when routes are added at runtime\n  * Changed; defaulting render() context to the current Request. Closes #197\n  * Updated haml submodule\n\n0.3.0 / 2010-02-11\n==================\n\n  * Updated haml / sass submodules. Closes #200\n  * Added flash message support. Closes #64\n  * Added accepts() now allows multiple args. fixes #117\n  * Added support for plugins to halt. Closes #189\n  * Added alternate layout support. Closes #119\n  * Removed Route#run(). Closes #188\n  * Fixed broken specs due to use(Cookie) missing\n\n0.2.1 / 2010-02-05\n==================\n\n  * Added \"plot\" format option for Profiler (for gnuplot processing)\n  * Added request number to Profiler plugin\n  * Fixed binary encoding for multipart file uploads, was previously defaulting to UTF8\n  * Fixed issue with routes not firing when not files are present. Closes #184\n  * Fixed process.Promise -> events.Promise\n\n0.2.0 / 2010-02-03\n==================\n\n  * Added parseParam() support for name[] etc. (allows for file inputs with \"multiple\" attr) Closes #180\n  * Added Both Cache and Session option \"reapInterval\" may be \"reapEvery\". Closes #174\n  * Added expiration support to cache api with reaper. Closes #133\n  * Added cache Store.Memory#reap()\n  * Added Cache; cache api now uses first class Cache instances\n  * Added abstract session Store. Closes #172\n  * Changed; cache Memory.Store#get() utilizing Collection\n  * Renamed MemoryStore -> Store.Memory\n  * Fixed use() of the same plugin several time will always use latest options. Closes #176\n\n0.1.0 / 2010-02-03\n==================\n\n  * Changed; Hooks (before / after) pass request as arg as well as evaluated in their context\n  * Updated node support to 0.1.27 Closes #169\n  * Updated dirname(__filename) -> __dirname\n  * Updated libxmljs support to v0.2.0\n  * Added session support with memory store / reaping\n  * Added quick uid() helper\n  * Added multi-part upload support\n  * Added Sass.js support / submodule\n  * Added production env caching view contents and static files\n  * Added static file caching. Closes #136\n  * Added cache plugin with memory stores\n  * Added support to StaticFile so that it works with non-textual files.\n  * Removed dirname() helper\n  * Removed several globals (now their modules must be required)\n\n0.0.2 / 2010-01-10\n==================\n\n  * Added view benchmarks; currently haml vs ejs\n  * Added Request#attachment() specs. Closes #116\n  * Added use of node's parseQuery() util. Closes #123\n  * Added `make init` for submodules\n  * Updated Haml\n  * Updated sample chat app to show messages on load\n  * Updated libxmljs parseString -> parseHtmlString\n  * Fixed `make init` to work with older versions of git\n  * Fixed specs can now run independent specs for those who can't build deps. Closes #127\n  * Fixed issues introduced by the node url module changes. Closes 126.\n  * Fixed two assertions failing due to Collection#keys() returning strings\n  * Fixed faulty Collection#toArray() spec due to keys() returning strings\n  * Fixed `make test` now builds libxmljs.node before testing\n\n0.0.1 / 2010-01-03\n==================\n\n  * Initial release\n"
  },
  {
    "path": "LICENSE",
    "content": "(The MIT License)\n\nCopyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>\nCopyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>\nCopyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Readme.md",
    "content": "[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](https://expressjs.com/)\n\n**Fast, unopinionated, minimalist web framework for [Node.js](https://nodejs.org).**\n\n**This project has a [Code of Conduct].**\n\n## Table of contents\n\n- [Table of contents](#table-of-contents)\n- [Installation](#installation)\n- [Features](#features)\n- [Docs \\& Community](#docs--community)\n- [Quick Start](#quick-start)\n- [Philosophy](#philosophy)\n- [Examples](#examples)\n- [Contributing](#contributing)\n  - [Security Issues](#security-issues)\n  - [Running Tests](#running-tests)\n- [Current project team members](#current-project-team-members)\n  - [TC (Technical Committee)](#tc-technical-committee)\n    - [TC emeriti members](#tc-emeriti-members)\n  - [Triagers](#triagers)\n    - [Emeritus Triagers](#emeritus-triagers)\n- [License](#license)\n\n\n[![NPM Version][npm-version-image]][npm-url]\n[![NPM Downloads][npm-downloads-image]][npm-downloads-url]\n[![Linux Build][github-actions-ci-image]][github-actions-ci-url]\n[![Test Coverage][coveralls-image]][coveralls-url]\n[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\n\n```js\nimport express from 'express'\n\nconst app = express()\n\napp.get('/', (req, res) => {\n  res.send('Hello World')\n})\n\napp.listen(3000, () => {\n  console.log('Server is running on http://localhost:3000')\n})\n```\n\n## Installation\n\nThis is a [Node.js](https://nodejs.org/en/) module available through the\n[npm registry](https://www.npmjs.com/).\n\nBefore installing, [download and install Node.js](https://nodejs.org/en/download/).\nNode.js 18 or higher is required.\n\nIf this is a brand new project, make sure to create a `package.json` first with\nthe [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).\n\nInstallation is done using the\n[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):\n\n```bash\nnpm install express\n```\n\nFollow [our installing guide](https://expressjs.com/en/starter/installing.html)\nfor more information.\n\n## Features\n\n  * Robust routing\n  * Focus on high performance\n  * Super-high test coverage\n  * HTTP helpers (redirection, caching, etc)\n  * View system supporting 14+ template engines\n  * Content negotiation\n  * Executable for generating applications quickly\n\n## Docs & Community\n\n  * [Website and Documentation](https://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]\n  * [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules\n  * [Github Discussions](https://github.com/expressjs/discussions) for discussion on the development and usage of Express\n\n**PROTIP** Be sure to read the [migration guide to v5](https://expressjs.com/en/guide/migrating-5)\n\n## Quick Start\n\n  The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:\n\n  Install the executable. The executable's major version will match Express's:\n\n```bash\nnpm install -g express-generator@4\n```\n\n  Create the app:\n\n```bash\nexpress /tmp/foo && cd /tmp/foo\n```\n\n  Install dependencies:\n\n```bash\nnpm install\n```\n\n  Start the server:\n\n```bash\nnpm start\n```\n\n  View the website at: http://localhost:3000\n\n## Philosophy\n\n  The Express philosophy is to provide small, robust tooling for HTTP servers, making\n  it a great solution for single page applications, websites, hybrids, or public\n  HTTP APIs.\n\n  Express does not force you to use any specific ORM or template engine. With support for over\n  14 template engines via [@ladjs/consolidate](https://github.com/ladjs/consolidate),\n  you can quickly craft your perfect framework.\n\n## Examples\n\n  To view the examples, clone the Express repository:\n\n```bash\ngit clone https://github.com/expressjs/express.git --depth 1 && cd express\n```\n\n  Then install the dependencies:\n\n```bash\nnpm install\n```\n\n  Then run whichever example you want:\n\n```bash\nnode examples/content-negotiation\n```\n\n## Contributing\n\nThe Express.js project welcomes all constructive contributions. Contributions take many forms,\nfrom code for bug fixes and enhancements, to additions and fixes to documentation, additional\ntests, triaging incoming pull requests and issues, and more!\n\nSee the [Contributing Guide] for more technical details on contributing.\n\n### Security Issues\n\nIf you discover a security vulnerability in Express, please see [Security Policies and Procedures](https://github.com/expressjs/express/security/policy).\n\n### Running Tests\n\nTo run the test suite, first install the dependencies:\n\n```bash\nnpm install\n```\n\nThen run `npm test`:\n\n```bash\nnpm test\n```\n\n## Current project team members\n\nFor information about the governance of the express.js project, see [GOVERNANCE.md](https://github.com/expressjs/discussions/blob/HEAD/docs/GOVERNANCE.md).\n\nThe original author of Express is [TJ Holowaychuk](https://github.com/tj)\n\n[List of all contributors](https://github.com/expressjs/express/graphs/contributors)\n\n### TC (Technical Committee)\n\n* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)\n* [jonchurch](https://github.com/jonchurch) - **Jon Church**\n* [wesleytodd](https://github.com/wesleytodd) - **Wes Todd**\n* [LinusU](https://github.com/LinusU) - **Linus Unnebäck**\n* [blakeembrey](https://github.com/blakeembrey) - **Blake Embrey**\n* [sheplu](https://github.com/sheplu) - **Jean Burellier**\n* [crandmck](https://github.com/crandmck) - **Rand McKinney**\n* [ctcpip](https://github.com/ctcpip) - **Chris de Almeida**\n\n<details>\n<summary>TC emeriti members</summary>\n\n#### TC emeriti members\n\n  * [dougwilson](https://github.com/dougwilson) - **Douglas Wilson**\n  * [hacksparrow](https://github.com/hacksparrow) - **Hage Yaapa**\n  * [jonathanong](https://github.com/jonathanong) - **jongleberry**\n  * [niftylettuce](https://github.com/niftylettuce) - **niftylettuce**\n  * [troygoode](https://github.com/troygoode) - **Troy Goode**\n</details>\n\n\n### Triagers\n\n* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair**\n* [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran**\n* [carpasse](https://github.com/carpasse) - **Carlos Serrano**\n* [CBID2](https://github.com/CBID2) - **Christine Belzie**\n* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)\n* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him)\n* [Phillip9587](https://github.com/Phillip9587) - **Phillip Barta**\n* [efekrskl](https://github.com/efekrskl) - **Efe Karasakal**\n* [rxmarbles](https://github.com/rxmarbles) - **Rick Markins** (he/him)\n* [krzysdz](https://github.com/krzysdz)\n* [GroophyLifefor](https://github.com/GroophyLifefor) - **Murat Kirazkaya**\n\n<details>\n<summary>Triagers emeriti members</summary>\n\n#### Emeritus Triagers\n\n  * [AuggieH](https://github.com/AuggieH) - **Auggie Hudak**\n  * [G-Rath](https://github.com/G-Rath) - **Gareth Jones**\n  * [MohammadXroid](https://github.com/MohammadXroid) - **Mohammad Ayashi**\n  * [NawafSwe](https://github.com/NawafSwe) - **Nawaf Alsharqi**\n  * [NotMoni](https://github.com/NotMoni) - **Moni**\n  * [VigneshMurugan](https://github.com/VigneshMurugan) - **Vignesh Murugan**\n  * [davidmashe](https://github.com/davidmashe) - **David Ashe**\n  * [digitaIfabric](https://github.com/digitaIfabric) - **David**\n  * [e-l-i-s-e](https://github.com/e-l-i-s-e) - **Elise Bonner**\n  * [fed135](https://github.com/fed135) - **Frederic Charette**\n  * [firmanJS](https://github.com/firmanJS) - **Firman Abdul Hakim**\n  * [getspooky](https://github.com/getspooky) - **Yasser Ameur**\n  * [ghinks](https://github.com/ghinks) - **Glenn**\n  * [ghousemohamed](https://github.com/ghousemohamed) - **Ghouse Mohamed**\n  * [gireeshpunathil](https://github.com/gireeshpunathil) - **Gireesh Punathil**\n  * [jake32321](https://github.com/jake32321) - **Jake Reed**\n  * [jonchurch](https://github.com/jonchurch) - **Jon Church**\n  * [lekanikotun](https://github.com/lekanikotun) - **Troy Goode**\n  * [marsonya](https://github.com/marsonya) - **Lekan Ikotun**\n  * [mastermatt](https://github.com/mastermatt) - **Matt R. Wilson**\n  * [maxakuru](https://github.com/maxakuru) - **Max Edell**\n  * [mlrawlings](https://github.com/mlrawlings) - **Michael Rawlings**\n  * [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov**\n  * [sheplu](https://github.com/sheplu) - **Jean Burellier**\n  * [tarunyadav1](https://github.com/tarunyadav1) - **Tarun yadav**\n  * [tunniclm](https://github.com/tunniclm) - **Mike Tunnicliffe**\n  * [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**\n  * [0ss](https://github.com/0ss) - **Salah**\n  * [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him)\n  * [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)\n  * [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**\n  * [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin**\n  * [dpopp07](https://github.com/dpopp07) - **Dustin Popp**\n  * [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**\n  * [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi**\n\n</details>\n\n\n## License\n\n  [MIT](LICENSE)\n\n[coveralls-image]: https://img.shields.io/coverallsCoverage/github/expressjs/express?branch=master\n[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master\n[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/express/ci.yml?branch=master&label=ci\n[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml\n[npm-downloads-image]: https://img.shields.io/npm/dm/express\n[npm-downloads-url]: https://npmcharts.com/compare/express?minimal=true\n[npm-url]: https://npmjs.org/package/express\n[npm-version-image]: https://img.shields.io/npm/v/express\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/express\n[Code of Conduct]: https://github.com/expressjs/.github/blob/HEAD/CODE_OF_CONDUCT.md\n[Contributing Guide]: https://github.com/expressjs/.github/blob/HEAD/CONTRIBUTING.md\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Express examples\n\nThis page contains list of examples using Express.\n\n- [auth](./auth) - Authentication with login and password\n- [content-negotiation](./content-negotiation) - HTTP content negotiation\n- [cookie-sessions](./cookie-sessions) - Working with cookie-based sessions\n- [cookies](./cookies) - Working with cookies\n- [downloads](./downloads) - Transferring files to client\n- [ejs](./ejs) - Working with Embedded JavaScript templating (ejs)\n- [error-pages](./error-pages) - Creating error pages\n- [error](./error) - Working with error middleware\n- [hello-world](./hello-world) - Simple request handler\n- [markdown](./markdown) - Markdown as template engine\n- [multi-router](./multi-router) - Working with multiple Express routers\n- [mvc](./mvc) - MVC-style controllers\n- [online](./online) - Tracking online user activity with `online` and `redis` packages\n- [params](./params) - Working with route parameters\n- [resource](./resource) - Multiple HTTP operations on the same resource\n- [route-map](./route-map) - Organizing routes using a map\n- [route-middleware](./route-middleware) - Working with route middleware\n- [route-separation](./route-separation) - Organizing routes per each resource\n- [search](./search) - Search API\n- [session](./session) - User sessions\n- [static-files](./static-files) - Serving static files\n- [vhost](./vhost) - Working with virtual hosts\n- [view-constructor](./view-constructor) - Rendering views dynamically\n- [view-locals](./view-locals) - Saving data in request object between middleware calls\n- [web-service](./web-service) - Simple API service\n"
  },
  {
    "path": "examples/auth/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar hash = require('pbkdf2-password')()\nvar path = require('node:path');\nvar session = require('express-session');\n\nvar app = module.exports = express();\n\n// config\n\napp.set('view engine', 'ejs');\napp.set('views', path.join(__dirname, 'views'));\n\n// middleware\n\napp.use(express.urlencoded())\napp.use(session({\n  resave: false, // don't save session if unmodified\n  saveUninitialized: false, // don't create session until something stored\n  secret: 'shhhh, very secret'\n}));\n\n// Session-persisted message middleware\n\napp.use(function(req, res, next){\n  var err = req.session.error;\n  var msg = req.session.success;\n  delete req.session.error;\n  delete req.session.success;\n  res.locals.message = '';\n  if (err) res.locals.message = '<p class=\"msg error\">' + err + '</p>';\n  if (msg) res.locals.message = '<p class=\"msg success\">' + msg + '</p>';\n  next();\n});\n\n// placeholder database\n\nvar users = {\n  tj: { name: 'tj' }\n};\n\n// when you create a user, generate a salt\n// and hash the password ('foobar' is the pass here)\n\nhash({ password: 'foobar' }, function (err, pass, salt, hash) {\n  if (err) throw err;\n  // store the salt & hash in the \"db\"\n  users.tj.salt = salt;\n  users.tj.hash = hash;\n});\n\n\n// Authenticate using our plain-object database of doom!\n\nfunction authenticate(name, pass, fn) {\n  if (!module.parent) console.log('authenticating %s:%s', name, pass);\n  var user = users[name];\n  // query the db for the given username\n  if (!user) return fn(null, null)\n  // apply the same algorithm to the POSTed password, applying\n  // the hash against the pass / salt, if there is a match we\n  // found the user\n  hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {\n    if (err) return fn(err);\n    if (hash === user.hash) return fn(null, user)\n    fn(null, null)\n  });\n}\n\nfunction restrict(req, res, next) {\n  if (req.session.user) {\n    next();\n  } else {\n    req.session.error = 'Access denied!';\n    res.redirect('/login');\n  }\n}\n\napp.get('/', function(req, res){\n  res.redirect('/login');\n});\n\napp.get('/restricted', restrict, function(req, res){\n  res.send('Wahoo! restricted area, click to <a href=\"/logout\">logout</a>');\n});\n\napp.get('/logout', function(req, res){\n  // destroy the user's session to log them out\n  // will be re-created next request\n  req.session.destroy(function(){\n    res.redirect('/');\n  });\n});\n\napp.get('/login', function(req, res){\n  res.render('login');\n});\n\napp.post('/login', function (req, res, next) {\n  if (!req.body) return res.sendStatus(400)\n  authenticate(req.body.username, req.body.password, function(err, user){\n    if (err) return next(err)\n    if (user) {\n      // Regenerate session when signing in\n      // to prevent fixation\n      req.session.regenerate(function(){\n        // Store the user's primary key\n        // in the session store to be retrieved,\n        // or in this case the entire user object\n        req.session.user = user;\n        req.session.success = 'Authenticated as ' + user.name\n          + ' click to <a href=\"/logout\">logout</a>. '\n          + ' You may now access <a href=\"/restricted\">/restricted</a>.';\n        res.redirect(req.get('Referrer') || '/');\n      });\n    } else {\n      req.session.error = 'Authentication failed, please check your '\n        + ' username and password.'\n        + ' (use \"tj\" and \"foobar\")';\n      res.redirect('/login');\n    }\n  });\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/auth/views/foot.ejs",
    "content": "  </body>\n</html>\n"
  },
  {
    "path": "examples/auth/views/head.ejs",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title><%= title %></title>\n    <style>\n      body {\n        padding: 50px;\n        font: 13px Helvetica, Arial, sans-serif;\n      }\n      .error {\n          color: red;\n      }\n      .success {\n          color: green;\n      }\n    </style>\n  </head>\n  <body>\n"
  },
  {
    "path": "examples/auth/views/login.ejs",
    "content": "\n<%- include('head', { title: 'Authentication Example' }) -%>\n\n<h1>Login</h1>\n<%- message %>\nTry accessing <a href=\"/restricted\">/restricted</a>, then authenticate with \"tj\" and \"foobar\".\n<form method=\"post\" action=\"/login\">\n  <p>\n    <label for=\"username\">Username:</label>\n    <input type=\"text\" name=\"username\" id=\"username\">\n  </p>\n  <p>\n    <label for=\"password\">Password:</label>\n    <input type=\"text\" name=\"password\" id=\"password\">\n  </p>\n  <p>\n    <input type=\"submit\" value=\"Login\">\n  </p>\n</form>\n\n<%- include('foot') -%>\n"
  },
  {
    "path": "examples/content-negotiation/db.js",
    "content": "'use strict'\n\nvar users = [];\n\nusers.push({ name: 'Tobi' });\nusers.push({ name: 'Loki' });\nusers.push({ name: 'Jane' });\n\nmodule.exports = users;\n"
  },
  {
    "path": "examples/content-negotiation/index.js",
    "content": "'use strict'\n\nvar express = require('../../');\nvar app = module.exports = express();\nvar users = require('./db');\n\n// so either you can deal with different types of formatting\n// for expected response in index.js\napp.get('/', function(req, res){\n  res.format({\n    html: function(){\n      res.send('<ul>' + users.map(function(user){\n        return '<li>' + user.name + '</li>';\n      }).join('') + '</ul>');\n    },\n\n    text: function(){\n      res.send(users.map(function(user){\n        return ' - ' + user.name + '\\n';\n      }).join(''));\n    },\n\n    json: function(){\n      res.json(users);\n    }\n  });\n});\n\n// or you could write a tiny middleware like\n// this to add a layer of abstraction\n// and make things a bit more declarative:\n\nfunction format(path) {\n  var obj = require(path);\n  return function(req, res){\n    res.format(obj);\n  };\n}\n\napp.get('/users', format('./users'));\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/content-negotiation/users.js",
    "content": "'use strict'\n\nvar users = require('./db');\n\nexports.html = function(req, res){\n  res.send('<ul>' + users.map(function(user){\n    return '<li>' + user.name + '</li>';\n  }).join('') + '</ul>');\n};\n\nexports.text = function(req, res){\n  res.send(users.map(function(user){\n    return ' - ' + user.name + '\\n';\n  }).join(''));\n};\n\nexports.json = function(req, res){\n  res.json(users);\n};\n"
  },
  {
    "path": "examples/cookie-sessions/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar cookieSession = require('cookie-session');\nvar express = require('../../');\n\nvar app = module.exports = express();\n\n// add req.session cookie support\napp.use(cookieSession({ secret: 'manny is cool' }));\n\n// do something with the session\napp.get('/', function (req, res) {\n  req.session.count = (req.session.count || 0) + 1\n  res.send('viewed ' + req.session.count + ' times\\n')\n})\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/cookies/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../');\nvar app = module.exports = express();\nvar logger = require('morgan');\nvar cookieParser = require('cookie-parser');\n\n// custom log format\nif (process.env.NODE_ENV !== 'test') app.use(logger(':method :url'))\n\n// parses request cookies, populating\n// req.cookies and req.signedCookies\n// when the secret is passed, used\n// for signing the cookies.\napp.use(cookieParser('my secret here'));\n\n// parses x-www-form-urlencoded\napp.use(express.urlencoded())\n\napp.get('/', function(req, res){\n  if (req.cookies.remember) {\n    res.send('Remembered :). Click to <a href=\"/forget\">forget</a>!.');\n  } else {\n    res.send('<form method=\"post\"><p>Check to <label>'\n      + '<input type=\"checkbox\" name=\"remember\"/> remember me</label> '\n      + '<input type=\"submit\" value=\"Submit\"/>.</p></form>');\n  }\n});\n\napp.get('/forget', function(req, res){\n  res.clearCookie('remember');\n  res.redirect(req.get('Referrer') || '/');\n});\n\napp.post('/', function(req, res){\n  var minute = 60000;\n\n  if (req.body && req.body.remember) {\n    res.cookie('remember', 1, { maxAge: minute })\n  }\n\n  res.redirect(req.get('Referrer') || '/');\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/downloads/files/CCTV大赛上海分赛区.txt",
    "content": "Only for test.\nThe file name is faked."
  },
  {
    "path": "examples/downloads/files/amazing.txt",
    "content": "what an amazing download"
  },
  {
    "path": "examples/downloads/files/notes/groceries.txt",
    "content": "* milk\n* eggs\n* bread\n"
  },
  {
    "path": "examples/downloads/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../');\nvar path = require('node:path');\n\nvar app = module.exports = express();\n\n// path to where the files are stored on disk\nvar FILES_DIR = path.join(__dirname, 'files')\n\napp.get('/', function(req, res){\n  res.send('<ul>' +\n    '<li>Download <a href=\"/files/notes/groceries.txt\">notes/groceries.txt</a>.</li>' +\n    '<li>Download <a href=\"/files/amazing.txt\">amazing.txt</a>.</li>' +\n    '<li>Download <a href=\"/files/missing.txt\">missing.txt</a>.</li>' +\n    '<li>Download <a href=\"/files/CCTV大赛上海分赛区.txt\">CCTV大赛上海分赛区.txt</a>.</li>' +\n    '</ul>')\n});\n\n// /files/* is accessed via req.params[0]\n// but here we name it :file\napp.get('/files/*file', function (req, res, next) {\n  res.download(req.params.file.join('/'), { root: FILES_DIR }, function (err) {\n    if (!err) return; // file sent\n    if (err.status !== 404) return next(err); // non-404 error\n    // file for download not found\n    res.statusCode = 404;\n    res.send('Cant find that file, sorry!');\n  });\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/ejs/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../');\nvar path = require('node:path');\n\nvar app = module.exports = express();\n\n// Register ejs as .html. If we did\n// not call this, we would need to\n// name our views foo.ejs instead\n// of foo.html. The __express method\n// is simply a function that engines\n// use to hook into the Express view\n// system by default, so if we want\n// to change \"foo.ejs\" to \"foo.html\"\n// we simply pass _any_ function, in this\n// case `ejs.__express`.\n\napp.engine('.html', require('ejs').__express);\n\n// Optional since express defaults to CWD/views\n\napp.set('views', path.join(__dirname, 'views'));\n\n// Path to our public directory\n\napp.use(express.static(path.join(__dirname, 'public')));\n\n// Without this you would need to\n// supply the extension to res.render()\n// ex: res.render('users.html').\napp.set('view engine', 'html');\n\n// Placeholder users\nvar users = [\n  { name: 'tobi', email: 'tobi@learnboost.com' },\n  { name: 'loki', email: 'loki@learnboost.com' },\n  { name: 'jane', email: 'jane@learnboost.com' }\n];\n\napp.get('/', function(req, res){\n  res.render('users', {\n    users: users,\n    title: \"EJS example\",\n    header: \"Some users\"\n  });\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/ejs/public/stylesheets/style.css",
    "content": "body {\n  padding: 50px 80px;\n  font: 14px \"Helvetica Neue\", \"Lucida Grande\", Arial, sans-serif;\n}\n"
  },
  {
    "path": "examples/ejs/views/footer.html",
    "content": "</body>\n</html>\n"
  },
  {
    "path": "examples/ejs/views/header.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <title><%= title %></title>\n  <link rel=\"stylesheet\" href=\"/stylesheets/style.css\">\n</head>\n<body>\n"
  },
  {
    "path": "examples/ejs/views/users.html",
    "content": "<%- include('header.html') -%>\n\n<h1>Users</h1>\n<ul id=\"users\">\n  <% users.forEach(function(user){ %>\n    <li><%= user.name %> &lt;<%= user.email %>&gt;</li>\n  <% }) %>\n</ul>\n\n<%- include('footer.html') -%>\n"
  },
  {
    "path": "examples/error/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../');\nvar logger = require('morgan');\nvar app = module.exports = express();\nvar test = app.get('env') === 'test'\n\nif (!test) app.use(logger('dev'));\n\n// error handling middleware have an arity of 4\n// instead of the typical (req, res, next),\n// otherwise they behave exactly like regular\n// middleware, you may have several of them,\n// in different orders etc.\n\nfunction error(err, req, res, next) {\n  // log it\n  if (!test) console.error(err.stack);\n\n  // respond with 500 \"Internal Server Error\".\n  res.status(500);\n  res.send('Internal Server Error');\n}\n\napp.get('/', function () {\n  // Caught and passed down to the errorHandler middleware\n  throw new Error('something broke!');\n});\n\napp.get('/next', function(req, res, next){\n  // We can also pass exceptions to next()\n  // The reason for process.nextTick() is to show that\n  // next() can be called inside an async operation,\n  // in real life it can be a DB read or HTTP request.\n  process.nextTick(function(){\n    next(new Error('oh no!'));\n  });\n});\n\n// the error handler is placed after routes\n// if it were above it would not receive errors\n// from app.get() etc\napp.use(error);\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/error-pages/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../');\nvar path = require('node:path');\nvar app = module.exports = express();\nvar logger = require('morgan');\nvar silent = process.env.NODE_ENV === 'test'\n\n// general config\napp.set('views', path.join(__dirname, 'views'));\napp.set('view engine', 'ejs');\n\n// our custom \"verbose errors\" setting\n// which we can use in the templates\n// via settings['verbose errors']\napp.enable('verbose errors');\n\n// disable them in production\n// use $ NODE_ENV=production node examples/error-pages\nif (app.settings.env === 'production') app.disable('verbose errors')\n\nsilent || app.use(logger('dev'));\n\n// Routes\n\napp.get('/', function(req, res){\n  res.render('index.ejs');\n});\n\napp.get('/404', function(req, res, next){\n  // trigger a 404 since no other middleware\n  // will match /404 after this one, and we're not\n  // responding here\n  next();\n});\n\napp.get('/403', function(req, res, next){\n  // trigger a 403 error\n  var err = new Error('not allowed!');\n  err.status = 403;\n  next(err);\n});\n\napp.get('/500', function(req, res, next){\n  // trigger a generic (500) error\n  next(new Error('keyboard cat!'));\n});\n\n// Error handlers\n\n// Since this is the last non-error-handling\n// middleware use()d, we assume 404, as nothing else\n// responded.\n\n// $ curl http://localhost:3000/notfound\n// $ curl http://localhost:3000/notfound -H \"Accept: application/json\"\n// $ curl http://localhost:3000/notfound -H \"Accept: text/plain\"\n\napp.use(function(req, res, next){\n  res.status(404);\n\n  res.format({\n    html: function () {\n      res.render('404', { url: req.url })\n    },\n    json: function () {\n      res.json({ error: 'Not found' })\n    },\n    default: function () {\n      res.type('txt').send('Not found')\n    }\n  })\n});\n\n// error-handling middleware, take the same form\n// as regular middleware, however they require an\n// arity of 4, aka the signature (err, req, res, next).\n// when connect has an error, it will invoke ONLY error-handling\n// middleware.\n\n// If we were to next() here any remaining non-error-handling\n// middleware would then be executed, or if we next(err) to\n// continue passing the error, only error-handling middleware\n// would remain being executed, however here\n// we simply respond with an error page.\n\napp.use(function(err, req, res, next){\n  // we may use properties of the error object\n  // here and next(err) appropriately, or if\n  // we possibly recovered from the error, simply next().\n  res.status(err.status || 500);\n  res.render('500', { error: err });\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/error-pages/views/404.ejs",
    "content": "<%- include('error_header') -%>\n<h2>Cannot find <%= url %></h2>\n<%- include('footer') -%>\n"
  },
  {
    "path": "examples/error-pages/views/500.ejs",
    "content": "<%- include('error_header') -%>\n<h2>Error: <%= error.message %></h2>\n<% if (settings['verbose errors']) { %>\n  <pre><%= error.stack %></pre>\n<% } else { %>\n  <p>An error occurred!</p>\n<% } %>\n<%- include('footer') -%>\n"
  },
  {
    "path": "examples/error-pages/views/error_header.ejs",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>Error</title>\n</head>\n\n<body>\n<h1>An error occurred!</h1>\n"
  },
  {
    "path": "examples/error-pages/views/footer.ejs",
    "content": "</body>\n</html>\n"
  },
  {
    "path": "examples/error-pages/views/index.ejs",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>Custom Pages Example</title>\n</head>\n\n<body>\n<h1>My Site</h1>\n<h2>Pages Example</h2>\n\n<ul>\n<li>visit <a href=\"/500\">500</a></li>\n<li>visit <a href=\"/404\">404</a></li>\n<li>visit <a href=\"/403\">403</a></li>\n</ul>\n\n</body>\n</html>\n"
  },
  {
    "path": "examples/hello-world/index.js",
    "content": "'use strict'\n\nvar express = require('../../');\n\nvar app = module.exports = express()\n\napp.get('/', function(req, res){\n  res.send('Hello World');\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/markdown/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar escapeHtml = require('escape-html');\nvar express = require('../..');\nvar fs = require('node:fs');\nvar marked = require('marked');\nvar path = require('node:path');\n\nvar app = module.exports = express();\n\n// register .md as an engine in express view system\n\napp.engine('md', function(path, options, fn){\n  fs.readFile(path, 'utf8', function(err, str){\n    if (err) return fn(err);\n    var html = marked.parse(str).replace(/\\{([^}]+)\\}/g, function(_, name){\n      return escapeHtml(options[name] || '');\n    });\n    fn(null, html);\n  });\n});\n\napp.set('views', path.join(__dirname, 'views'));\n\n// make it the default, so we don't need .md\napp.set('view engine', 'md');\n\napp.get('/', function(req, res){\n  res.render('index', { title: 'Markdown Example' });\n});\n\napp.get('/fail', function(req, res){\n  res.render('missing', { title: 'Markdown Example' });\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/markdown/views/index.md",
    "content": "\n# {title}\n\nJust an example view rendered with _markdown_."
  },
  {
    "path": "examples/multi-router/controllers/api_v1.js",
    "content": "'use strict'\n\nvar express = require('../../..');\n\nvar apiv1 = express.Router();\n\napiv1.get('/', function(req, res) {\n  res.send('Hello from APIv1 root route.');\n});\n\napiv1.get('/users', function(req, res) {\n  res.send('List of APIv1 users.');\n});\n\nmodule.exports = apiv1;\n"
  },
  {
    "path": "examples/multi-router/controllers/api_v2.js",
    "content": "'use strict'\n\nvar express = require('../../..');\n\nvar apiv2 = express.Router();\n\napiv2.get('/', function(req, res) {\n  res.send('Hello from APIv2 root route.');\n});\n\napiv2.get('/users', function(req, res) {\n  res.send('List of APIv2 users.');\n});\n\nmodule.exports = apiv2;\n"
  },
  {
    "path": "examples/multi-router/index.js",
    "content": "'use strict'\n\nvar express = require('../..');\n\nvar app = module.exports = express();\n\napp.use('/api/v1', require('./controllers/api_v1'));\napp.use('/api/v2', require('./controllers/api_v2'));\n\napp.get('/', function(req, res) {\n  res.send('Hello from root route.')\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/mvc/controllers/main/index.js",
    "content": "'use strict'\n\nexports.index = function(req, res){\n  res.redirect('/users');\n};\n"
  },
  {
    "path": "examples/mvc/controllers/pet/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar db = require('../../db');\n\nexports.engine = 'ejs';\n\nexports.before = function(req, res, next){\n  var pet = db.pets[req.params.pet_id];\n  if (!pet) return next('route');\n  req.pet = pet;\n  next();\n};\n\nexports.show = function(req, res, next){\n  res.render('show', { pet: req.pet });\n};\n\nexports.edit = function(req, res, next){\n  res.render('edit', { pet: req.pet });\n};\n\nexports.update = function(req, res, next){\n  var body = req.body;\n  req.pet.name = body.pet.name;\n  res.message('Information updated!');\n  res.redirect('/pet/' + req.pet.id);\n};\n"
  },
  {
    "path": "examples/mvc/controllers/pet/views/edit.ejs",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<link rel=\"stylesheet\" href=\"/style.css\">\n<title>Edit <%= pet.name %></title>\n</head>\n\n<body>\n<h1><%= pet.name %></h1>\n<form action=\"/pet/<%= pet.id %>?_method=put\" method=\"post\">\n  <label>Name: <input type=\"text\" name=\"pet[name]\" value=\"<%= pet.name %>\"></label>\n  <input type=\"submit\" value=\"Update\">\n</form>\n</body>\n</html>\n"
  },
  {
    "path": "examples/mvc/controllers/pet/views/show.ejs",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<link rel=\"stylesheet\" href=\"/style.css\">\n<title><%= pet.name %></title>\n</head>\n\n<body>\n<h1><%= pet.name %> <a href=\"/pet/<%= pet.id %>/edit\">edit</a></h1>\n\n<p>You are viewing <%= pet.name %></p>\n</body>\n</html>\n"
  },
  {
    "path": "examples/mvc/controllers/user/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar db = require('../../db');\n\nexports.engine = 'hbs';\n\nexports.before = function(req, res, next){\n  var id = req.params.user_id;\n  if (!id) return next();\n  // pretend to query a database...\n  process.nextTick(function(){\n    req.user = db.users[id];\n    // cant find that user\n    if (!req.user) return next('route');\n    // found it, move on to the routes\n    next();\n  });\n};\n\nexports.list = function(req, res, next){\n  res.render('list', { users: db.users });\n};\n\nexports.edit = function(req, res, next){\n  res.render('edit', { user: req.user });\n};\n\nexports.show = function(req, res, next){\n  res.render('show', { user: req.user });\n};\n\nexports.update = function(req, res, next){\n  var body = req.body;\n  req.user.name = body.user.name;\n  res.message('Information updated!');\n  res.redirect('/user/' + req.user.id);\n};\n"
  },
  {
    "path": "examples/mvc/controllers/user/views/edit.hbs",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <link rel=\"stylesheet\" href=\"/style.css\">\n  <title>Edit {{user.name}}</title>\n</head>\n<body>\n  <h1>{{user.name}}</h1>\n  <form action=\"/user/{{user.id}}?_method=put\" method=\"post\">\n    <label for=\"user[name]\">Name:\n      <input type=\"text\" name=\"user[name]\" value=\"{{user.name}}\">\n    </label>\n\n    <input type=\"submit\" name=\"submit\" value=\"Update\">\n  </form>\n\n  <form action=\"/user/{{user.id}}/pet\" method=\"post\">\n    <label for=\"pet[name]\">Pet:\n      <input type=\"text\" name=\"pet[name]\" placeholder=\"Pet Name\">\n    </label>\n\n    <input type=\"submit\" name=\"submit\" value=\"Add\">\n  </form>\n</body>\n</html>\n"
  },
  {
    "path": "examples/mvc/controllers/user/views/list.hbs",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <link rel=\"stylesheet\" href=\"/style.css\">\n  <title>Users</title>\n</head>\n<body>\n  <h1>Users</h1>\n  <p>Click a user below to view their pets.</p>\n  <ul>\n    {{#each users}}\n      <li><a href=\"/user/{{id}}\">{{name}}</a></li>\n    {{/each}}\n  </ul>\n</body>\n</html>\n"
  },
  {
    "path": "examples/mvc/controllers/user/views/show.hbs",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <link rel=\"stylesheet\" href=\"/style.css\">\n  <title>{{user.name}}</title>\n</head>\n<body>\n<h1>{{user.name}} <a href=\"/user/{{user.id}}/edit\">edit</a></h1>\n\n{{#if hasMessages}}\n  <ul>\n    {{#each messages}}\n      <li>{{this}}</li>\n    {{/each}}\n  </ul>\n{{/if}}\n\n{{#if user.pets.length}}\n  <p>View {{user.name}}'s pets:</p>\n  <ul>\n    {{#each user.pets}}\n      <li><a href=\"/pet/{{id}}\">{{name}}</a></li>\n    {{/each}}\n  </ul>\n{{else}}\n  <p>No pets!</p>\n{{/if}}\n</body>\n</html>\n"
  },
  {
    "path": "examples/mvc/controllers/user-pet/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar db = require('../../db');\n\nexports.name = 'pet';\nexports.prefix = '/user/:user_id';\n\nexports.create = function(req, res, next){\n  var id = req.params.user_id;\n  var user = db.users[id];\n  var body = req.body;\n  if (!user) return next('route');\n  var pet = { name: body.pet.name };\n  pet.id = db.pets.push(pet) - 1;\n  user.pets.push(pet);\n  res.message('Added pet ' + body.pet.name);\n  res.redirect('/user/' + id);\n};\n"
  },
  {
    "path": "examples/mvc/db.js",
    "content": "'use strict'\n\n// faux database\n\nvar pets = exports.pets = [];\n\npets.push({ name: 'Tobi', id: 0 });\npets.push({ name: 'Loki', id: 1 });\npets.push({ name: 'Jane', id: 2 });\npets.push({ name: 'Raul', id: 3 });\n\nvar users = exports.users = [];\n\nusers.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0  });\nusers.push({ name: 'Guillermo', pets: [pets[3]], id: 1 });\nusers.push({ name: 'Nathan', pets: [], id: 2 });\n"
  },
  {
    "path": "examples/mvc/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar logger = require('morgan');\nvar path = require('node:path');\nvar session = require('express-session');\nvar methodOverride = require('method-override');\n\nvar app = module.exports = express();\n\n// set our default template engine to \"ejs\"\n// which prevents the need for using file extensions\napp.set('view engine', 'ejs');\n\n// set views for error and 404 pages\napp.set('views', path.join(__dirname, 'views'));\n\n// define a custom res.message() method\n// which stores messages in the session\napp.response.message = function(msg){\n  // reference `req.session` via the `this.req` reference\n  var sess = this.req.session;\n  // simply add the msg to an array for later\n  sess.messages = sess.messages || [];\n  sess.messages.push(msg);\n  return this;\n};\n\n// log\nif (!module.parent) app.use(logger('dev'));\n\n// serve static files\napp.use(express.static(path.join(__dirname, 'public')));\n\n// session support\napp.use(session({\n  resave: false, // don't save session if unmodified\n  saveUninitialized: false, // don't create session until something stored\n  secret: 'some secret here'\n}));\n\n// parse request bodies (req.body)\napp.use(express.urlencoded({ extended: true }))\n\n// allow overriding methods in query (?_method=put)\napp.use(methodOverride('_method'));\n\n// expose the \"messages\" local variable when views are rendered\napp.use(function(req, res, next){\n  var msgs = req.session.messages || [];\n\n  // expose \"messages\" local variable\n  res.locals.messages = msgs;\n\n  // expose \"hasMessages\"\n  res.locals.hasMessages = !! msgs.length;\n\n  /* This is equivalent:\n   res.locals({\n     messages: msgs,\n     hasMessages: !! msgs.length\n   });\n  */\n\n  next();\n  // empty or \"flush\" the messages so they\n  // don't build up\n  req.session.messages = [];\n});\n\n// load controllers\nrequire('./lib/boot')(app, { verbose: !module.parent });\n\napp.use(function(err, req, res, next){\n  // log it\n  if (!module.parent) console.error(err.stack);\n\n  // error page\n  res.status(500).render('5xx');\n});\n\n// assume 404 since no middleware responded\napp.use(function(req, res, next){\n  res.status(404).render('404', { url: req.originalUrl });\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/mvc/lib/boot.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../..');\nvar fs = require('node:fs');\nvar path = require('node:path');\n\nmodule.exports = function(parent, options){\n  var dir = path.join(__dirname, '..', 'controllers');\n  var verbose = options.verbose;\n  fs.readdirSync(dir).forEach(function(name){\n    var file = path.join(dir, name)\n    if (!fs.statSync(file).isDirectory()) return;\n    verbose && console.log('\\n   %s:', name);\n    var obj = require(file);\n    var name = obj.name || name;\n    var prefix = obj.prefix || '';\n    var app = express();\n    var handler;\n    var method;\n    var url;\n\n    // allow specifying the view engine\n    if (obj.engine) app.set('view engine', obj.engine);\n    app.set('views', path.join(__dirname, '..', 'controllers', name, 'views'));\n\n    // generate routes based\n    // on the exported methods\n    for (var key in obj) {\n      // \"reserved\" exports\n      if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue;\n      // route exports\n      switch (key) {\n        case 'show':\n          method = 'get';\n          url = '/' + name + '/:' + name + '_id';\n          break;\n        case 'list':\n          method = 'get';\n          url = '/' + name + 's';\n          break;\n        case 'edit':\n          method = 'get';\n          url = '/' + name + '/:' + name + '_id/edit';\n          break;\n        case 'update':\n          method = 'put';\n          url = '/' + name + '/:' + name + '_id';\n          break;\n        case 'create':\n          method = 'post';\n          url = '/' + name;\n          break;\n        case 'index':\n          method = 'get';\n          url = '/';\n          break;\n        default:\n          /* istanbul ignore next */\n          throw new Error('unrecognized route: ' + name + '.' + key);\n      }\n\n      // setup\n      handler = obj[key];\n      url = prefix + url;\n\n      // before middleware support\n      if (obj.before) {\n        app[method](url, obj.before, handler);\n        verbose && console.log('     %s %s -> before -> %s', method.toUpperCase(), url, key);\n      } else {\n        app[method](url, handler);\n        verbose && console.log('     %s %s -> %s', method.toUpperCase(), url, key);\n      }\n    }\n\n    // mount the app\n    parent.use(app);\n  });\n};\n"
  },
  {
    "path": "examples/mvc/public/style.css",
    "content": "body {\n  padding: 50px;\n  font: 16px \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\na {\n  color: #107aff;\n  text-decoration: none;\n}\na:hover {\n  text-decoration: underline;\n}\nh1 a {\n  font-size: 16px;\n}\n"
  },
  {
    "path": "examples/mvc/views/404.ejs",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>Not Found</title>\n    <link rel=\"stylesheet\" href=\"/style.css\">\n  </head>\n  <body>\n    <h1>404: Not Found</h1>\n    <p>Sorry we can't find <%= url %></p>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/mvc/views/5xx.ejs",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>Internal Server Error</title>\n    <link rel=\"stylesheet\" href=\"/style.css\">\n  </head>\n  <body>\n    <h1>500: Internal Server Error</h1>\n    <p>Looks like something blew up!</p>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/online/index.js",
    "content": "'use strict'\n\n// install redis first:\n// https://redis.io/\n\n// then:\n// $ npm install redis online\n// $ redis-server\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar online = require('online');\nvar redis = require('redis');\nvar db = redis.createClient();\n\n// online\n\nonline = online(db);\n\n// app\n\nvar app = express();\n\n// activity tracking, in this case using\n// the UA string, you would use req.user.id etc\n\napp.use(function(req, res, next){\n  // fire-and-forget\n  online.add(req.headers['user-agent']);\n  next();\n});\n\n/**\n * List helper.\n */\n\nfunction list(ids) {\n  return '<ul>' + ids.map(function(id){\n    return '<li>' + id + '</li>';\n  }).join('') + '</ul>';\n}\n\n/**\n * GET users online.\n */\n\napp.get('/', function(req, res, next){\n  online.last(5, function(err, ids){\n    if (err) return next(err);\n    res.send('<p>Users online: ' + ids.length + '</p>' + list(ids));\n  });\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/params/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar createError = require('http-errors')\nvar express = require('../../');\nvar app = module.exports = express();\n\n// Faux database\n\nvar users = [\n  { name: 'tj' }\n  , { name: 'tobi' }\n  , { name: 'loki' }\n  , { name: 'jane' }\n  , { name: 'bandit' }\n];\n\n// Convert :to and :from to integers\n\napp.param(['to', 'from'], function(req, res, next, num, name){\n  req.params[name] = parseInt(num, 10);\n  if( isNaN(req.params[name]) ){\n    next(createError(400, 'failed to parseInt '+num));\n  } else {\n    next();\n  }\n});\n\n// Load user by id\n\napp.param('user', function(req, res, next, id){\n  req.user = users[id]\n  if (req.user) {\n    next();\n  } else {\n    next(createError(404, 'failed to find user'));\n  }\n});\n\n/**\n * GET index.\n */\n\napp.get('/', function(req, res){\n  res.send('Visit /user/0 or /users/0-2');\n});\n\n/**\n * GET :user.\n */\n\napp.get('/user/:user', function (req, res) {\n  res.send('user ' + req.user.name);\n});\n\n/**\n * GET users :from - :to.\n */\n\napp.get('/users/:from-:to', function (req, res) {\n  var from = req.params.from;\n  var to = req.params.to;\n  var names = users.map(function(user){ return user.name; });\n  res.send('users ' + names.slice(from, to + 1).join(', '));\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/resource/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../');\n\nvar app = module.exports = express();\n\n// Ad-hoc example resource method\n\napp.resource = function(path, obj) {\n  this.get(path, obj.index);\n  this.get(path + '/:a..:b{.:format}', function(req, res){\n    var a = parseInt(req.params.a, 10);\n    var b = parseInt(req.params.b, 10);\n    var format = req.params.format;\n    obj.range(req, res, a, b, format);\n  });\n  this.get(path + '/:id', obj.show);\n  this.delete(path + '/:id', function(req, res){\n    var id = parseInt(req.params.id, 10);\n    obj.destroy(req, res, id);\n  });\n};\n\n// Fake records\n\nvar users = [\n  { name: 'tj' }\n  , { name: 'ciaran' }\n  , { name: 'aaron' }\n  , { name: 'guillermo' }\n  , { name: 'simon' }\n  , { name: 'tobi' }\n];\n\n// Fake controller.\n\nvar User = {\n  index: function(req, res){\n    res.send(users);\n  },\n  show: function(req, res){\n    res.send(users[req.params.id] || { error: 'Cannot find user' });\n  },\n  destroy: function(req, res, id){\n    var destroyed = id in users;\n    delete users[id];\n    res.send(destroyed ? 'destroyed' : 'Cannot find user');\n  },\n  range: function(req, res, a, b, format){\n    var range = users.slice(a, b + 1);\n    switch (format) {\n      case 'json':\n        res.send(range);\n        break;\n      case 'html':\n      default:\n        var html = '<ul>' + range.map(function(user){\n          return '<li>' + user.name + '</li>';\n        }).join('\\n') + '</ul>';\n        res.send(html);\n        break;\n    }\n  }\n};\n\n// curl http://localhost:3000/users     -- responds with all users\n// curl http://localhost:3000/users/1   -- responds with user 1\n// curl http://localhost:3000/users/4   -- responds with error\n// curl http://localhost:3000/users/1..3 -- responds with several users\n// curl -X DELETE http://localhost:3000/users/1  -- deletes the user\n\napp.resource('/users', User);\n\napp.get('/', function(req, res){\n  res.send([\n    '<h1>Examples:</h1> <ul>'\n    , '<li>GET /users</li>'\n    , '<li>GET /users/1</li>'\n    , '<li>GET /users/3</li>'\n    , '<li>GET /users/1..3</li>'\n    , '<li>GET /users/1..3.json</li>'\n    , '<li>DELETE /users/4</li>'\n    , '</ul>'\n  ].join('\\n'));\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/route-map/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar escapeHtml = require('escape-html')\nvar express = require('../../lib/express');\n\nvar verbose = process.env.NODE_ENV !== 'test'\n\nvar app = module.exports = express();\n\napp.map = function(a, route){\n  route = route || '';\n  for (var key in a) {\n    switch (typeof a[key]) {\n      // { '/path': { ... }}\n      case 'object':\n        app.map(a[key], route + key);\n        break;\n      // get: function(){ ... }\n      case 'function':\n        if (verbose) console.log('%s %s', key, route);\n        app[key](route, a[key]);\n        break;\n    }\n  }\n};\n\nvar users = {\n  list: function(req, res){\n    res.send('user list');\n  },\n\n  get: function(req, res){\n    res.send('user ' +  escapeHtml(req.params.uid))\n  },\n\n  delete: function(req, res){\n    res.send('delete users');\n  }\n};\n\nvar pets = {\n  list: function(req, res){\n    res.send('user ' + escapeHtml(req.params.uid) + '\\'s pets')\n  },\n\n  delete: function(req, res){\n    res.send('delete ' + escapeHtml(req.params.uid) + '\\'s pet ' + escapeHtml(req.params.pid))\n  }\n};\n\napp.map({\n  '/users': {\n    get: users.list,\n    delete: users.delete,\n    '/:uid': {\n      get: users.get,\n      '/pets': {\n        get: pets.list,\n        '/:pid': {\n          delete: pets.delete\n        }\n      }\n    }\n  }\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/route-middleware/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../lib/express');\n\nvar app = express();\n\n// Example requests:\n//     curl http://localhost:3000/user/0\n//     curl http://localhost:3000/user/0/edit\n//     curl http://localhost:3000/user/1\n//     curl http://localhost:3000/user/1/edit (unauthorized since this is not you)\n//     curl -X DELETE http://localhost:3000/user/0 (unauthorized since you are not an admin)\n\n// Placeholder users\nvar users = [\n  { id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' }\n  , { id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' }\n  , { id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' }\n];\n\nfunction loadUser(req, res, next) {\n  // You would fetch your user from the db\n  var user = users[req.params.id];\n  if (user) {\n    req.user = user;\n    next();\n  } else {\n    next(new Error('Failed to load user ' + req.params.id));\n  }\n}\n\nfunction andRestrictToSelf(req, res, next) {\n  // If our authenticated user is the user we are viewing\n  // then everything is fine :)\n  if (req.authenticatedUser.id === req.user.id) {\n    next();\n  } else {\n    // You may want to implement specific exceptions\n    // such as UnauthorizedError or similar so that you\n    // can handle these can be special-cased in an error handler\n    // (view ./examples/pages for this)\n    next(new Error('Unauthorized'));\n  }\n}\n\nfunction andRestrictTo(role) {\n  return function(req, res, next) {\n    if (req.authenticatedUser.role === role) {\n      next();\n    } else {\n      next(new Error('Unauthorized'));\n    }\n  }\n}\n\n// Middleware for faux authentication\n// you would of course implement something real,\n// but this illustrates how an authenticated user\n// may interact with middleware\n\napp.use(function(req, res, next){\n  req.authenticatedUser = users[0];\n  next();\n});\n\napp.get('/', function(req, res){\n  res.redirect('/user/0');\n});\n\napp.get('/user/:id', loadUser, function(req, res){\n  res.send('Viewing user ' + req.user.name);\n});\n\napp.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){\n  res.send('Editing user ' + req.user.name);\n});\n\napp.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){\n  res.send('Deleted user ' + req.user.name);\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/route-separation/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar path = require('node:path');\nvar app = express();\nvar logger = require('morgan');\nvar cookieParser = require('cookie-parser');\nvar methodOverride = require('method-override');\nvar site = require('./site');\nvar post = require('./post');\nvar user = require('./user');\n\nmodule.exports = app;\n\n// Config\n\napp.set('view engine', 'ejs');\napp.set('views', path.join(__dirname, 'views'));\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.use(logger('dev'));\n}\n\napp.use(methodOverride('_method'));\napp.use(cookieParser());\napp.use(express.urlencoded({ extended: true }))\napp.use(express.static(path.join(__dirname, 'public')));\n\n// General\n\napp.get('/', site.index);\n\n// User\n\napp.get('/users', user.list);\napp.all('/user/:id{/:op}', user.load);\napp.get('/user/:id', user.view);\napp.get('/user/:id/view', user.view);\napp.get('/user/:id/edit', user.edit);\napp.put('/user/:id/edit', user.update);\n\n// Posts\n\napp.get('/posts', post.list);\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/route-separation/post.js",
    "content": "'use strict'\n\n// Fake posts database\n\nvar posts = [\n  { title: 'Foo', body: 'some foo bar' },\n  { title: 'Foo bar', body: 'more foo bar' },\n  { title: 'Foo bar baz', body: 'more foo bar baz' }\n];\n\nexports.list = function(req, res){\n  res.render('posts', { title: 'Posts', posts: posts });\n};\n"
  },
  {
    "path": "examples/route-separation/public/style.css",
    "content": "body {\n  padding: 50px;\n  font: 14px \"Helvetica Neue\", Arial, sans-serif;\n}\na {\n  color: #00AEFF;\n  text-decoration: none;\n}\na.edit {\n  color: #000;\n  opacity: .3;\n}\na.edit::before {\n  content: ' [';\n}\na.edit::after {\n  content: ']';\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin: 15px;\n}"
  },
  {
    "path": "examples/route-separation/site.js",
    "content": "'use strict'\n\nexports.index = function(req, res){\n  res.render('index', { title: 'Route Separation Example' });\n};\n"
  },
  {
    "path": "examples/route-separation/user.js",
    "content": "'use strict'\n\n// Fake user database\n\nvar users = [\n  { name: 'TJ', email: 'tj@vision-media.ca' },\n  { name: 'Tobi', email: 'tobi@vision-media.ca' }\n];\n\nexports.list = function(req, res){\n  res.render('users', { title: 'Users', users: users });\n};\n\nexports.load = function(req, res, next){\n  var id = req.params.id;\n  req.user = users[id];\n  if (req.user) {\n    next();\n  } else {\n    var err = new Error('cannot find user ' + id);\n    err.status = 404;\n    next(err);\n  }\n};\n\nexports.view = function(req, res){\n  res.render('users/view', {\n    title: 'Viewing user ' + req.user.name,\n    user: req.user\n  });\n};\n\nexports.edit = function(req, res){\n  res.render('users/edit', {\n    title: 'Editing user ' + req.user.name,\n    user: req.user\n  });\n};\n\nexports.update = function(req, res){\n  // Normally you would handle all kinds of\n  // validation and save back to the db\n  var user = req.body.user;\n  req.user.name = user.name;\n  req.user.email = user.email;\n  res.redirect(req.get('Referrer') || '/');\n};\n"
  },
  {
    "path": "examples/route-separation/views/footer.ejs",
    "content": "</body>\n</html>\n"
  },
  {
    "path": "examples/route-separation/views/header.ejs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <title><%= title %></title>\n  <link rel=\"stylesheet\" href=\"/style.css\">\n</head>\n<body>\n"
  },
  {
    "path": "examples/route-separation/views/index.ejs",
    "content": "<%- include('header') -%>\n\n<h1><%= title %></h1>\n\n<ul>\n  <li>Visit the <a href=\"/users\">users</a> page.</li>\n  <li>Visit the <a href=\"/posts\">posts</a> page.</li>\n</ul>\n\n<%- include('footer') -%>\n"
  },
  {
    "path": "examples/route-separation/views/posts/index.ejs",
    "content": "<%- include('../header') -%>\n\n<h1>Posts</h1>\n\n<dl id=\"posts\">\n  <% posts.forEach(function(post) { %>\n    <dt><%= post.title %></dt>\n    <dd><%= post.body %></dd>\n  <% }) %>\n</dl>\n\n<%- include('../footer') -%>\n"
  },
  {
    "path": "examples/route-separation/views/users/edit.ejs",
    "content": "<%- include('../header') -%>\n\n<h1>Editing <%= user.name %></h1>\n\n<div id=\"user\">\n  <form action=\"?_method=put\" method=\"post\">\n    <p>\n      Name:\n      <input type=\"text\" value=\"<%= user.name %>\" name=\"user[name]\" />\n    </p>\n\n    <p>\n      Email:\n      <input type=\"email\" value=\"<%= user.email %>\" name=\"user[email]\" />\n    </p>\n\n    <p>\n      <input type=\"submit\" value=\"Save\" />\n    </p>\n  </form>\n</div>\n\n<%- include('../footer') -%>\n"
  },
  {
    "path": "examples/route-separation/views/users/index.ejs",
    "content": "<%- include('../header') -%>\n\n<h1><%= title %></h1>\n\n<div id=\"users\">\n  <% users.forEach(function(user, index) { %>\n    <li>\n      <a href=\"/user/<%= index %>\"><%= user.name %></a>\n      <a href=\"/user/<%= index %>/edit\">edit</a>\n    </li>\n  <% }) %>\n</div>\n\n<%- include('../footer') -%>\n"
  },
  {
    "path": "examples/route-separation/views/users/view.ejs",
    "content": "<%- include('../header') -%>\n\n<h1><%= user.name %></h1>\n\n<div id=\"user\">\n  <p>Email: <%= user.email %></p>\n</div>\n\n<%- include('../footer') -%>\n"
  },
  {
    "path": "examples/search/index.js",
    "content": "'use strict'\n\n// install redis first:\n// https://redis.io/\n\n// then:\n// $ npm install redis\n// $ redis-server\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar path = require('node:path');\nvar redis = require('redis');\n\nvar db = redis.createClient();\nvar app = express();\n\napp.use(express.static(path.join(__dirname, 'public')));\n\n// npm install redis\n\n/**\n * Redis Initialization\n */\n\nasync function initializeRedis() {\n  try {\n    // connect to Redis\n\n    await db.connect();\n\n    // populate search\n\n    await db.sAdd('ferret', 'tobi');\n    await db.sAdd('ferret', 'loki');\n    await db.sAdd('ferret', 'jane');\n    await db.sAdd('cat', 'manny');\n    await db.sAdd('cat', 'luna');\n  } catch (err) {\n    console.error('Error initializing Redis:', err);\n    process.exit(1);\n  }\n}\n\n/**\n * GET search for :query.\n */\n\napp.get('/search/{:query}', function (req, res, next) {\n  var query = req.params.query || '';\n  db.sMembers(query)\n    .then((vals) => res.send(vals))\n    .catch((err) => {\n      console.error(`Redis error for query \"${query}\":`, err);\n      next(err);\n    });\n});\n\n/**\n * GET client javascript. Here we use sendFile()\n * because serving __dirname with the static() middleware\n * would also mean serving our server \"index.js\" and the \"search.jade\"\n * template.\n */\n\napp.get('/client.js', function(req, res){\n  res.sendFile(path.join(__dirname, 'client.js'));\n});\n\n/**\n * Start the Server\n */\n\n(async () => {\n  await initializeRedis();\n  if (!module.parent) {\n    app.listen(3000);\n    console.log('Express started on port 3000');\n  }\n})();\n"
  },
  {
    "path": "examples/search/public/client.js",
    "content": "'use strict'\n\nvar search = document.querySelector('[type=search]');\nvar code = document.querySelector('pre');\n\nsearch.addEventListener('keyup', function(){\n  var xhr = new XMLHttpRequest;\n  xhr.open('GET', '/search/' + search.value, true);\n  xhr.onreadystatechange = function(){\n    if (xhr.readyState === 4) {\n      code.textContent = xhr.responseText;\n    }\n  };\n  xhr.send();\n}, false);\n"
  },
  {
    "path": "examples/search/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <title>Search example</title>\n  <style>\n    body {\n      font: 14px \"Helvetica Neue\", Helvetica;\n      padding: 50px;\n    }\n  </style>\n</head>\n<body>\n  <h2>Search</h2>\n  <p>Try searching for \"ferret\" or \"cat\".</p>\n  <input type=\"search\" name=\"search\" value=\"\" />\n  <pre></pre>\n  <script src=\"/client.js\" charset=\"utf-8\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/session/index.js",
    "content": "'use strict'\n\n// install redis first:\n// https://redis.io/\n\n// then:\n// $ npm install redis\n// $ redis-server\n\nvar express = require('../..');\nvar session = require('express-session');\n\nvar app = express();\n\n// Populates req.session\napp.use(session({\n  resave: false, // don't save session if unmodified\n  saveUninitialized: false, // don't create session until something stored\n  secret: 'keyboard cat'\n}));\n\napp.get('/', function(req, res){\n  var body = '';\n  if (req.session.views) {\n    ++req.session.views;\n  } else {\n    req.session.views = 1;\n    body += '<p>First time visiting? view this page in several browsers :)</p>';\n  }\n  res.send(body + '<p>viewed <strong>' + req.session.views + '</strong> times.</p>');\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/session/redis.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar logger = require('morgan');\nvar session = require('express-session');\n\n// pass the express to the connect redis module\n// allowing it to inherit from session.Store\nvar RedisStore = require('connect-redis')(session);\n\nvar app = express();\n\napp.use(logger('dev'));\n\n// Populates req.session\napp.use(session({\n  resave: false, // don't save session if unmodified\n  saveUninitialized: false, // don't create session until something stored\n  secret: 'keyboard cat',\n  store: new RedisStore\n}));\n\napp.get('/', function(req, res){\n  var body = '';\n  if (req.session.views) {\n    ++req.session.views;\n  } else {\n    req.session.views = 1;\n    body += '<p>First time visiting? view this page in several browsers :)</p>';\n  }\n  res.send(body + '<p>viewed <strong>' + req.session.views + '</strong> times.</p>');\n});\n\napp.listen(3000);\nconsole.log('Express app started on port 3000');\n"
  },
  {
    "path": "examples/static-files/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar logger = require('morgan');\nvar path = require('node:path');\nvar app = express();\n\n// log requests\napp.use(logger('dev'));\n\n// express on its own has no notion\n// of a \"file\". The express.static()\n// middleware checks for a file matching\n// the `req.path` within the directory\n// that you pass it. In this case \"GET /js/app.js\"\n// will look for \"./public/js/app.js\".\n\napp.use(express.static(path.join(__dirname, 'public')));\n\n// if you wanted to \"prefix\" you may use\n// the mounting feature of Connect, for example\n// \"GET /static/js/app.js\" instead of \"GET /js/app.js\".\n// The mount-path \"/static\" is simply removed before\n// passing control to the express.static() middleware,\n// thus it serves the file correctly by ignoring \"/static\"\napp.use('/static', express.static(path.join(__dirname, 'public')));\n\n// if for some reason you want to serve files from\n// several directories, you can use express.static()\n// multiple times! Here we're passing \"./public/css\",\n// this will allow \"GET /style.css\" instead of \"GET /css/style.css\":\napp.use(express.static(path.join(__dirname, 'public', 'css')));\n\napp.listen(3000);\nconsole.log('listening on port 3000');\nconsole.log('try:');\nconsole.log('  GET /hello.txt');\nconsole.log('  GET /js/app.js');\nconsole.log('  GET /css/style.css');\n"
  },
  {
    "path": "examples/static-files/public/css/style.css",
    "content": "body {\n  \n}"
  },
  {
    "path": "examples/static-files/public/hello.txt",
    "content": "hey"
  },
  {
    "path": "examples/static-files/public/js/app.js",
    "content": "// foo\n"
  },
  {
    "path": "examples/vhost/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar logger = require('morgan');\nvar vhost = require('vhost');\n\n/*\nedit /etc/hosts:\n\n127.0.0.1       foo.example.com\n127.0.0.1       bar.example.com\n127.0.0.1       example.com\n*/\n\n// Main server app\n\nvar main = express();\n\nif (!module.parent) main.use(logger('dev'));\n\nmain.get('/', function(req, res){\n  res.send('Hello from main app!');\n});\n\nmain.get('/:sub', function(req, res){\n  res.send('requested ' + req.params.sub);\n});\n\n// Redirect app\n\nvar redirect = express();\n\nredirect.use(function(req, res){\n  if (!module.parent) console.log(req.vhost);\n  res.redirect('http://example.com:3000/' + req.vhost[0]);\n});\n\n// Vhost app\n\nvar app = module.exports = express();\n\napp.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app\napp.use(vhost('example.com', main)); // Serves top level domain via Main server app\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/view-constructor/github-view.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar https = require('node:https');\nvar path = require('node:path');\nvar extname = path.extname;\n\n/**\n * Expose `GithubView`.\n */\n\nmodule.exports = GithubView;\n\n/**\n * Custom view that fetches and renders\n * remove github templates. You could\n * render templates from a database etc.\n */\n\nfunction GithubView(name, options){\n  this.name = name;\n  options = options || {};\n  this.engine = options.engines[extname(name)];\n  // \"root\" is the app.set('views') setting, however\n  // in your own implementation you could ignore this\n  this.path = '/' + options.root + '/master/' + name;\n}\n\n/**\n * Render the view.\n */\n\nGithubView.prototype.render = function(options, fn){\n  var self = this;\n  var opts = {\n    host: 'raw.githubusercontent.com',\n    port: 443,\n    path: this.path,\n    method: 'GET'\n  };\n\n  https.request(opts, function(res) {\n    var buf = '';\n    res.setEncoding('utf8');\n    res.on('data', function(str){ buf += str });\n    res.on('end', function(){\n      self.engine(buf, options, fn);\n    });\n  }).end();\n};\n"
  },
  {
    "path": "examples/view-constructor/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../');\nvar GithubView = require('./github-view');\nvar md = require('marked').parse;\n\nvar app = module.exports = express();\n\n// register .md as an engine in express view system\napp.engine('md', function(str, options, fn){\n  try {\n    var html = md(str);\n    html = html.replace(/\\{([^}]+)\\}/g, function(_, name){\n      return options[name] || '';\n    });\n    fn(null, html);\n  } catch(err) {\n    fn(err);\n  }\n});\n\n// pointing to a particular github repo to load files from it\napp.set('views', 'expressjs/express');\n\n// register a new view constructor\napp.set('view', GithubView);\n\napp.get('/', function(req, res){\n  // rendering a view relative to the repo.\n  // app.locals, res.locals, and locals passed\n  // work like they normally would\n  res.render('examples/markdown/views/index.md', { title: 'Example' });\n});\n\napp.get('/Readme.md', function(req, res){\n  // rendering a view from https://github.com/expressjs/express/blob/master/Readme.md\n  res.render('Readme.md');\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/view-locals/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../..');\nvar path = require('node:path');\nvar User = require('./user');\nvar app = express();\n\napp.set('views', path.join(__dirname, 'views'));\napp.set('view engine', 'ejs');\n\n// filter ferrets only\n\nfunction ferrets(user) {\n  return user.species === 'ferret'\n}\n\n// naive nesting approach,\n// delegating errors to next(err)\n// in order to expose the \"count\"\n// and \"users\" locals\n\napp.get('/', function(req, res, next){\n  User.count(function(err, count){\n    if (err) return next(err);\n    User.all(function(err, users){\n      if (err) return next(err);\n      res.render('index', {\n        title: 'Users',\n        count: count,\n        users: users.filter(ferrets)\n      });\n    })\n  })\n});\n\n\n\n\n// this approach is cleaner,\n// less nesting and we have\n// the variables available\n// on the request object\n\nfunction count(req, res, next) {\n  User.count(function(err, count){\n    if (err) return next(err);\n    req.count = count;\n    next();\n  })\n}\n\nfunction users(req, res, next) {\n  User.all(function(err, users){\n    if (err) return next(err);\n    req.users = users;\n    next();\n  })\n}\n\napp.get('/middleware', count, users, function (req, res) {\n  res.render('index', {\n    title: 'Users',\n    count: req.count,\n    users: req.users.filter(ferrets)\n  });\n});\n\n\n\n\n// this approach is much like the last\n// however we're explicitly exposing\n// the locals within each middleware\n//\n// note that this may not always work\n// well, for example here we filter\n// the users in the middleware, which\n// may not be ideal for our application.\n// so in that sense the previous example\n// is more flexible with `req.users`.\n\nfunction count2(req, res, next) {\n  User.count(function(err, count){\n    if (err) return next(err);\n    res.locals.count = count;\n    next();\n  })\n}\n\nfunction users2(req, res, next) {\n  User.all(function(err, users){\n    if (err) return next(err);\n    res.locals.users = users.filter(ferrets);\n    next();\n  })\n}\n\napp.get('/middleware-locals', count2, users2, function (req, res) {\n  // you can see now how we have much less\n  // to pass to res.render(). If we have\n  // several routes related to users this\n  // can be a great productivity booster\n  res.render('index', { title: 'Users' });\n});\n\n// keep in mind that middleware may be placed anywhere\n// and in various combinations, so if you have locals\n// that you wish to make available to all subsequent\n// middleware/routes you can do something like this:\n\n/*\n\napp.use(function(req, res, next){\n  res.locals.user = req.user;\n  res.locals.sess = req.session;\n  next();\n});\n\n*/\n\n// or suppose you have some /admin\n// \"global\" local variables:\n\n/*\n\napp.use('/api', function(req, res, next){\n  res.locals.user = req.user;\n  res.locals.sess = req.session;\n  next();\n});\n\n*/\n\n// the following is effectively the same,\n// but uses a route instead:\n\n/*\n\napp.all('/api/*', function(req, res, next){\n  res.locals.user = req.user;\n  res.locals.sess = req.session;\n  next();\n});\n\n*/\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "examples/view-locals/user.js",
    "content": "'use strict'\n\nmodule.exports = User;\n\n// faux model\n\nfunction User(name, age, species) {\n  this.name = name;\n  this.age = age;\n  this.species = species;\n}\n\nUser.all = function(fn){\n  // process.nextTick makes sure this function API\n  // behaves in an asynchronous manner, like if it\n  // was a real DB query to read all users.\n  process.nextTick(function(){\n    fn(null, users);\n  });\n};\n\nUser.count = function(fn){\n  process.nextTick(function(){\n    fn(null, users.length);\n  });\n};\n\n// faux database\n\nvar users = [];\n\nusers.push(new User('Tobi', 2, 'ferret'));\nusers.push(new User('Loki', 1, 'ferret'));\nusers.push(new User('Jane', 6, 'ferret'));\nusers.push(new User('Luna', 1, 'cat'));\nusers.push(new User('Manny', 1, 'cat'));\n"
  },
  {
    "path": "examples/view-locals/views/index.ejs",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title><%= title %></title>\n    <style media=\"screen\">\n      body {\n        padding: 50px;\n        font: 16px Helvetica, Arial;\n      }\n    </style>\n  </head>\n  <body>\n    <h2><%= title %></h2>\n    <% users.forEach(function(user) { %>\n      <li><strong><%= user.name %></strong> is a <% user.age %> year old <%= user.species %></li>\n    <% }); %>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/web-service/index.js",
    "content": "'use strict'\n\n/**\n * Module dependencies.\n */\n\nvar express = require('../../');\n\nvar app = module.exports = express();\n\n// create an error with .status. we\n// can then use the property in our\n// custom error handler (Connect respects this prop as well)\n\nfunction error(status, msg) {\n  var err = new Error(msg);\n  err.status = status;\n  return err;\n}\n\n// if we wanted to supply more than JSON, we could\n// use something similar to the content-negotiation\n// example.\n\n// here we validate the API key,\n// by mounting this middleware to /api\n// meaning only paths prefixed with \"/api\"\n// will cause this middleware to be invoked\n\napp.use('/api', function(req, res, next){\n  var key = req.query['api-key'];\n\n  // key isn't present\n  if (!key) return next(error(400, 'api key required'));\n\n  // key is invalid\n  if (apiKeys.indexOf(key) === -1) return next(error(401, 'invalid api key'))\n\n  // all good, store req.key for route access\n  req.key = key;\n  next();\n});\n\n// map of valid api keys, typically mapped to\n// account info with some sort of database like redis.\n// api keys do _not_ serve as authentication, merely to\n// track API usage or help prevent malicious behavior etc.\n\nvar apiKeys = ['foo', 'bar', 'baz'];\n\n// these two objects will serve as our faux database\n\nvar repos = [\n  { name: 'express', url: 'https://github.com/expressjs/express' },\n  { name: 'stylus', url: 'https://github.com/learnboost/stylus' },\n  { name: 'cluster', url: 'https://github.com/learnboost/cluster' }\n];\n\nvar users = [\n  { name: 'tobi' }\n  , { name: 'loki' }\n  , { name: 'jane' }\n];\n\nvar userRepos = {\n  tobi: [repos[0], repos[1]]\n  , loki: [repos[1]]\n  , jane: [repos[2]]\n};\n\n// we now can assume the api key is valid,\n// and simply expose the data\n\n// example: http://localhost:3000/api/users/?api-key=foo\napp.get('/api/users', function (req, res) {\n  res.send(users);\n});\n\n// example: http://localhost:3000/api/repos/?api-key=foo\napp.get('/api/repos', function (req, res) {\n  res.send(repos);\n});\n\n// example: http://localhost:3000/api/user/tobi/repos/?api-key=foo\napp.get('/api/user/:name/repos', function(req, res, next){\n  var name = req.params.name;\n  var user = userRepos[name];\n\n  if (user) res.send(user);\n  else next();\n});\n\n// middleware with an arity of 4 are considered\n// error handling middleware. When you next(err)\n// it will be passed through the defined middleware\n// in order, but ONLY those with an arity of 4, ignoring\n// regular middleware.\napp.use(function(err, req, res, next){\n  // whatever you want here, feel free to populate\n  // properties on `err` to treat it differently in here.\n  res.status(err.status || 500);\n  res.send({ error: err.message });\n});\n\n// our custom JSON 404 middleware. Since it's placed last\n// it will be the last middleware called, if all others\n// invoke next() and do not respond.\napp.use(function(req, res){\n  res.status(404);\n  res.send({ error: \"Sorry, can't find that\" })\n});\n\n/* istanbul ignore next */\nif (!module.parent) {\n  app.listen(3000);\n  console.log('Express started on port 3000');\n}\n"
  },
  {
    "path": "index.js",
    "content": "/*!\n * express\n * Copyright(c) 2009-2013 TJ Holowaychuk\n * Copyright(c) 2013 Roman Shtylman\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\nmodule.exports = require('./lib/express');\n"
  },
  {
    "path": "lib/application.js",
    "content": "/*!\n * express\n * Copyright(c) 2009-2013 TJ Holowaychuk\n * Copyright(c) 2013 Roman Shtylman\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module dependencies.\n * @private\n */\n\nvar finalhandler = require('finalhandler');\nvar debug = require('debug')('express:application');\nvar View = require('./view');\nvar http = require('node:http');\nvar methods = require('./utils').methods;\nvar compileETag = require('./utils').compileETag;\nvar compileQueryParser = require('./utils').compileQueryParser;\nvar compileTrust = require('./utils').compileTrust;\nvar resolve = require('node:path').resolve;\nvar once = require('once')\nvar Router = require('router');\n\n/**\n * Module variables.\n * @private\n */\n\nvar slice = Array.prototype.slice;\nvar flatten = Array.prototype.flat;\n\n/**\n * Application prototype.\n */\n\nvar app = exports = module.exports = {};\n\n/**\n * Variable for trust proxy inheritance back-compat\n * @private\n */\n\nvar trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';\n\n/**\n * Initialize the server.\n *\n *   - setup default configuration\n *   - setup default middleware\n *   - setup route reflection methods\n *\n * @private\n */\n\napp.init = function init() {\n  var router = null;\n\n  this.cache = Object.create(null);\n  this.engines = Object.create(null);\n  this.settings = Object.create(null);\n\n  this.defaultConfiguration();\n\n  // Setup getting to lazily add base router\n  Object.defineProperty(this, 'router', {\n    configurable: true,\n    enumerable: true,\n    get: function getrouter() {\n      if (router === null) {\n        router = new Router({\n          caseSensitive: this.enabled('case sensitive routing'),\n          strict: this.enabled('strict routing')\n        });\n      }\n\n      return router;\n    }\n  });\n};\n\n/**\n * Initialize application configuration.\n * @private\n */\n\napp.defaultConfiguration = function defaultConfiguration() {\n  var env = process.env.NODE_ENV || 'development';\n\n  // default settings\n  this.enable('x-powered-by');\n  this.set('etag', 'weak');\n  this.set('env', env);\n  this.set('query parser', 'simple')\n  this.set('subdomain offset', 2);\n  this.set('trust proxy', false);\n\n  // trust proxy inherit back-compat\n  Object.defineProperty(this.settings, trustProxyDefaultSymbol, {\n    configurable: true,\n    value: true\n  });\n\n  debug('booting in %s mode', env);\n\n  this.on('mount', function onmount(parent) {\n    // inherit trust proxy\n    if (this.settings[trustProxyDefaultSymbol] === true\n      && typeof parent.settings['trust proxy fn'] === 'function') {\n      delete this.settings['trust proxy'];\n      delete this.settings['trust proxy fn'];\n    }\n\n    // inherit protos\n    Object.setPrototypeOf(this.request, parent.request)\n    Object.setPrototypeOf(this.response, parent.response)\n    Object.setPrototypeOf(this.engines, parent.engines)\n    Object.setPrototypeOf(this.settings, parent.settings)\n  });\n\n  // setup locals\n  this.locals = Object.create(null);\n\n  // top-most app is mounted at /\n  this.mountpath = '/';\n\n  // default locals\n  this.locals.settings = this.settings;\n\n  // default configuration\n  this.set('view', View);\n  this.set('views', resolve('views'));\n  this.set('jsonp callback name', 'callback');\n\n  if (env === 'production') {\n    this.enable('view cache');\n  }\n};\n\n/**\n * Dispatch a req, res pair into the application. Starts pipeline processing.\n *\n * If no callback is provided, then default error handlers will respond\n * in the event of an error bubbling through the stack.\n *\n * @private\n */\n\napp.handle = function handle(req, res, callback) {\n  // final handler\n  var done = callback || finalhandler(req, res, {\n    env: this.get('env'),\n    onerror: logerror.bind(this)\n  });\n\n  // set powered by header\n  if (this.enabled('x-powered-by')) {\n    res.setHeader('X-Powered-By', 'Express');\n  }\n\n  // set circular references\n  req.res = res;\n  res.req = req;\n\n  // alter the prototypes\n  Object.setPrototypeOf(req, this.request)\n  Object.setPrototypeOf(res, this.response)\n\n  // setup locals\n  if (!res.locals) {\n    res.locals = Object.create(null);\n  }\n\n  this.router.handle(req, res, done);\n};\n\n/**\n * Proxy `Router#use()` to add middleware to the app router.\n * See Router#use() documentation for details.\n *\n * If the _fn_ parameter is an express app, then it will be\n * mounted at the _route_ specified.\n *\n * @public\n */\n\napp.use = function use(fn) {\n  var offset = 0;\n  var path = '/';\n\n  // default path to '/'\n  // disambiguate app.use([fn])\n  if (typeof fn !== 'function') {\n    var arg = fn;\n\n    while (Array.isArray(arg) && arg.length !== 0) {\n      arg = arg[0];\n    }\n\n    // first arg is the path\n    if (typeof arg !== 'function') {\n      offset = 1;\n      path = fn;\n    }\n  }\n\n  var fns = flatten.call(slice.call(arguments, offset), Infinity);\n\n  if (fns.length === 0) {\n    throw new TypeError('app.use() requires a middleware function')\n  }\n\n  // get router\n  var router = this.router;\n\n  fns.forEach(function (fn) {\n    // non-express app\n    if (!fn || !fn.handle || !fn.set) {\n      return router.use(path, fn);\n    }\n\n    debug('.use app under %s', path);\n    fn.mountpath = path;\n    fn.parent = this;\n\n    // restore .app property on req and res\n    router.use(path, function mounted_app(req, res, next) {\n      var orig = req.app;\n      fn.handle(req, res, function (err) {\n        Object.setPrototypeOf(req, orig.request)\n        Object.setPrototypeOf(res, orig.response)\n        next(err);\n      });\n    });\n\n    // mounted an app\n    fn.emit('mount', this);\n  }, this);\n\n  return this;\n};\n\n/**\n * Proxy to the app `Router#route()`\n * Returns a new `Route` instance for the _path_.\n *\n * Routes are isolated middleware stacks for specific paths.\n * See the Route api docs for details.\n *\n * @public\n */\n\napp.route = function route(path) {\n  return this.router.route(path);\n};\n\n/**\n * Register the given template engine callback `fn`\n * as `ext`.\n *\n * By default will `require()` the engine based on the\n * file extension. For example if you try to render\n * a \"foo.ejs\" file Express will invoke the following internally:\n *\n *     app.engine('ejs', require('ejs').__express);\n *\n * For engines that do not provide `.__express` out of the box,\n * or if you wish to \"map\" a different extension to the template engine\n * you may use this method. For example mapping the EJS template engine to\n * \".html\" files:\n *\n *     app.engine('html', require('ejs').renderFile);\n *\n * In this case EJS provides a `.renderFile()` method with\n * the same signature that Express expects: `(path, options, callback)`,\n * though note that it aliases this method as `ejs.__express` internally\n * so if you're using \".ejs\" extensions you don't need to do anything.\n *\n * Some template engines do not follow this convention, the\n * [Consolidate.js](https://github.com/tj/consolidate.js)\n * library was created to map all of node's popular template\n * engines to follow this convention, thus allowing them to\n * work seamlessly within Express.\n *\n * @param {String} ext\n * @param {Function} fn\n * @return {app} for chaining\n * @public\n */\n\napp.engine = function engine(ext, fn) {\n  if (typeof fn !== 'function') {\n    throw new Error('callback function required');\n  }\n\n  // get file extension\n  var extension = ext[0] !== '.'\n    ? '.' + ext\n    : ext;\n\n  // store engine\n  this.engines[extension] = fn;\n\n  return this;\n};\n\n/**\n * Proxy to `Router#param()` with one added api feature. The _name_ parameter\n * can be an array of names.\n *\n * See the Router#param() docs for more details.\n *\n * @param {String|Array} name\n * @param {Function} fn\n * @return {app} for chaining\n * @public\n */\n\napp.param = function param(name, fn) {\n  if (Array.isArray(name)) {\n    for (var i = 0; i < name.length; i++) {\n      this.param(name[i], fn);\n    }\n\n    return this;\n  }\n\n  this.router.param(name, fn);\n\n  return this;\n};\n\n/**\n * Assign `setting` to `val`, or return `setting`'s value.\n *\n *    app.set('foo', 'bar');\n *    app.set('foo');\n *    // => \"bar\"\n *\n * Mounted servers inherit their parent server's settings.\n *\n * @param {String} setting\n * @param {*} [val]\n * @return {Server} for chaining\n * @public\n */\n\napp.set = function set(setting, val) {\n  if (arguments.length === 1) {\n    // app.get(setting)\n    return this.settings[setting];\n  }\n\n  debug('set \"%s\" to %o', setting, val);\n\n  // set value\n  this.settings[setting] = val;\n\n  // trigger matched settings\n  switch (setting) {\n    case 'etag':\n      this.set('etag fn', compileETag(val));\n      break;\n    case 'query parser':\n      this.set('query parser fn', compileQueryParser(val));\n      break;\n    case 'trust proxy':\n      this.set('trust proxy fn', compileTrust(val));\n\n      // trust proxy inherit back-compat\n      Object.defineProperty(this.settings, trustProxyDefaultSymbol, {\n        configurable: true,\n        value: false\n      });\n\n      break;\n  }\n\n  return this;\n};\n\n/**\n * Return the app's absolute pathname\n * based on the parent(s) that have\n * mounted it.\n *\n * For example if the application was\n * mounted as \"/admin\", which itself\n * was mounted as \"/blog\" then the\n * return value would be \"/blog/admin\".\n *\n * @return {String}\n * @private\n */\n\napp.path = function path() {\n  return this.parent\n    ? this.parent.path() + this.mountpath\n    : '';\n};\n\n/**\n * Check if `setting` is enabled (truthy).\n *\n *    app.enabled('foo')\n *    // => false\n *\n *    app.enable('foo')\n *    app.enabled('foo')\n *    // => true\n *\n * @param {String} setting\n * @return {Boolean}\n * @public\n */\n\napp.enabled = function enabled(setting) {\n  return Boolean(this.set(setting));\n};\n\n/**\n * Check if `setting` is disabled.\n *\n *    app.disabled('foo')\n *    // => true\n *\n *    app.enable('foo')\n *    app.disabled('foo')\n *    // => false\n *\n * @param {String} setting\n * @return {Boolean}\n * @public\n */\n\napp.disabled = function disabled(setting) {\n  return !this.set(setting);\n};\n\n/**\n * Enable `setting`.\n *\n * @param {String} setting\n * @return {app} for chaining\n * @public\n */\n\napp.enable = function enable(setting) {\n  return this.set(setting, true);\n};\n\n/**\n * Disable `setting`.\n *\n * @param {String} setting\n * @return {app} for chaining\n * @public\n */\n\napp.disable = function disable(setting) {\n  return this.set(setting, false);\n};\n\n/**\n * Delegate `.VERB(...)` calls to `router.VERB(...)`.\n */\n\nmethods.forEach(function (method) {\n  app[method] = function (path) {\n    if (method === 'get' && arguments.length === 1) {\n      // app.get(setting)\n      return this.set(path);\n    }\n\n    var route = this.route(path);\n    route[method].apply(route, slice.call(arguments, 1));\n    return this;\n  };\n});\n\n/**\n * Special-cased \"all\" method, applying the given route `path`,\n * middleware, and callback to _every_ HTTP method.\n *\n * @param {String} path\n * @param {Function} ...\n * @return {app} for chaining\n * @public\n */\n\napp.all = function all(path) {\n  var route = this.route(path);\n  var args = slice.call(arguments, 1);\n\n  for (var i = 0; i < methods.length; i++) {\n    route[methods[i]].apply(route, args);\n  }\n\n  return this;\n};\n\n/**\n * Render the given view `name` name with `options`\n * and a callback accepting an error and the\n * rendered template string.\n *\n * Example:\n *\n *    app.render('email', { name: 'Tobi' }, function(err, html){\n *      // ...\n *    })\n *\n * @param {String} name\n * @param {Object|Function} options or fn\n * @param {Function} callback\n * @public\n */\n\napp.render = function render(name, options, callback) {\n  var cache = this.cache;\n  var done = callback;\n  var engines = this.engines;\n  var opts = options || {};\n  var view;\n\n  // support callback function as second arg\n  if (typeof options === 'function') {\n    done = options;\n    opts = {};\n  }\n\n  // merge options\n  var renderOptions = { ...this.locals, ...opts._locals, ...opts };\n\n  // set .cache unless explicitly provided\n  if (renderOptions.cache == null) {\n    renderOptions.cache = this.enabled('view cache');\n  }\n\n  // primed cache\n  if (renderOptions.cache) {\n    view = cache[name];\n  }\n\n  // view\n  if (!view) {\n    var View = this.get('view');\n\n    view = new View(name, {\n      defaultEngine: this.get('view engine'),\n      root: this.get('views'),\n      engines: engines\n    });\n\n    if (!view.path) {\n      var dirs = Array.isArray(view.root) && view.root.length > 1\n        ? 'directories \"' + view.root.slice(0, -1).join('\", \"') + '\" or \"' + view.root[view.root.length - 1] + '\"'\n        : 'directory \"' + view.root + '\"'\n      var err = new Error('Failed to lookup view \"' + name + '\" in views ' + dirs);\n      err.view = view;\n      return done(err);\n    }\n\n    // prime the cache\n    if (renderOptions.cache) {\n      cache[name] = view;\n    }\n  }\n\n  // render\n  tryRender(view, renderOptions, done);\n};\n\n/**\n * Listen for connections.\n *\n * A node `http.Server` is returned, with this\n * application (which is a `Function`) as its\n * callback. If you wish to create both an HTTP\n * and HTTPS server you may do so with the \"http\"\n * and \"https\" modules as shown here:\n *\n *    var http = require('node:http')\n *      , https = require('node:https')\n *      , express = require('express')\n *      , app = express();\n *\n *    http.createServer(app).listen(80);\n *    https.createServer({ ... }, app).listen(443);\n *\n * @return {http.Server}\n * @public\n */\n\napp.listen = function listen() {\n  var server = http.createServer(this)\n  var args = slice.call(arguments)\n  if (typeof args[args.length - 1] === 'function') {\n    var done = args[args.length - 1] = once(args[args.length - 1])\n    server.once('error', done)\n  }\n  return server.listen.apply(server, args)\n}\n\n/**\n * Log error using console.error.\n *\n * @param {Error} err\n * @private\n */\n\nfunction logerror(err) {\n  /* istanbul ignore next */\n  if (this.get('env') !== 'test') console.error(err.stack || err.toString());\n}\n\n/**\n * Try rendering a view.\n * @private\n */\n\nfunction tryRender(view, options, callback) {\n  try {\n    view.render(options, callback);\n  } catch (err) {\n    callback(err);\n  }\n}\n"
  },
  {
    "path": "lib/express.js",
    "content": "/*!\n * express\n * Copyright(c) 2009-2013 TJ Holowaychuk\n * Copyright(c) 2013 Roman Shtylman\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module dependencies.\n */\n\nvar bodyParser = require('body-parser')\nvar EventEmitter = require('node:events').EventEmitter;\nvar mixin = require('merge-descriptors');\nvar proto = require('./application');\nvar Router = require('router');\nvar req = require('./request');\nvar res = require('./response');\n\n/**\n * Expose `createApplication()`.\n */\n\nexports = module.exports = createApplication;\n\n/**\n * Create an express application.\n *\n * @return {Function}\n * @api public\n */\n\nfunction createApplication() {\n  var app = function(req, res, next) {\n    app.handle(req, res, next);\n  };\n\n  mixin(app, EventEmitter.prototype, false);\n  mixin(app, proto, false);\n\n  // expose the prototype that will get set on requests\n  app.request = Object.create(req, {\n    app: { configurable: true, enumerable: true, writable: true, value: app }\n  })\n\n  // expose the prototype that will get set on responses\n  app.response = Object.create(res, {\n    app: { configurable: true, enumerable: true, writable: true, value: app }\n  })\n\n  app.init();\n  return app;\n}\n\n/**\n * Expose the prototypes.\n */\n\nexports.application = proto;\nexports.request = req;\nexports.response = res;\n\n/**\n * Expose constructors.\n */\n\nexports.Route = Router.Route;\nexports.Router = Router;\n\n/**\n * Expose middleware\n */\n\nexports.json = bodyParser.json\nexports.raw = bodyParser.raw\nexports.static = require('serve-static');\nexports.text = bodyParser.text\nexports.urlencoded = bodyParser.urlencoded\n"
  },
  {
    "path": "lib/request.js",
    "content": "/*!\n * express\n * Copyright(c) 2009-2013 TJ Holowaychuk\n * Copyright(c) 2013 Roman Shtylman\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module dependencies.\n * @private\n */\n\nvar accepts = require('accepts');\nvar isIP = require('node:net').isIP;\nvar typeis = require('type-is');\nvar http = require('node:http');\nvar fresh = require('fresh');\nvar parseRange = require('range-parser');\nvar parse = require('parseurl');\nvar proxyaddr = require('proxy-addr');\n\n/**\n * Request prototype.\n * @public\n */\n\nvar req = Object.create(http.IncomingMessage.prototype)\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = req\n\n/**\n * Return request header.\n *\n * The `Referrer` header field is special-cased,\n * both `Referrer` and `Referer` are interchangeable.\n *\n * Examples:\n *\n *     req.get('Content-Type');\n *     // => \"text/plain\"\n *\n *     req.get('content-type');\n *     // => \"text/plain\"\n *\n *     req.get('Something');\n *     // => undefined\n *\n * Aliased as `req.header()`.\n *\n * @param {String} name\n * @return {String}\n * @public\n */\n\nreq.get =\nreq.header = function header(name) {\n  if (!name) {\n    throw new TypeError('name argument is required to req.get');\n  }\n\n  if (typeof name !== 'string') {\n    throw new TypeError('name must be a string to req.get');\n  }\n\n  var lc = name.toLowerCase();\n\n  switch (lc) {\n    case 'referer':\n    case 'referrer':\n      return this.headers.referrer\n        || this.headers.referer;\n    default:\n      return this.headers[lc];\n  }\n};\n\n/**\n * Check if the given `type(s)` is acceptable, returning\n * the best match when true, otherwise `false`, in which\n * case you should respond with 406 \"Not Acceptable\".\n *\n * The `type` value may be a single MIME type string\n * such as \"application/json\", an extension name\n * such as \"json\", an argument list such as `\"json\", \"html\", \"text/plain\"`,\n * or an array `[\"json\", \"html\", \"text/plain\"]`. When a list\n * or array is given, the _best_ match, if any is returned.\n *\n * Examples:\n *\n *     // Accept: text/html\n *     req.accepts('html');\n *     // => \"html\"\n *\n *     // Accept: text/*, application/json\n *     req.accepts('html');\n *     // => \"html\"\n *     req.accepts('text/html');\n *     // => \"text/html\"\n *     req.accepts('json', 'text');\n *     // => \"json\"\n *     req.accepts('application/json');\n *     // => \"application/json\"\n *\n *     // Accept: text/*, application/json\n *     req.accepts('image/png');\n *     req.accepts('png');\n *     // => false\n *\n *     // Accept: text/*;q=.5, application/json\n *     req.accepts(['html', 'json']);\n *     req.accepts('html', 'json');\n *     // => \"json\"\n *\n * @param {String|Array} type(s)\n * @return {String|Array|Boolean}\n * @public\n */\n\nreq.accepts = function(){\n  var accept = accepts(this);\n  return accept.types.apply(accept, arguments);\n};\n\n/**\n * Check if the given `encoding`s are accepted.\n *\n * @param {String} ...encoding\n * @return {String|Array}\n * @public\n */\n\nreq.acceptsEncodings = function(){\n  var accept = accepts(this);\n  return accept.encodings.apply(accept, arguments);\n};\n\n/**\n * Checks if the specified `charset`s are acceptable based on the request's `Accept-Charset` header.\n * Returns the best matching charset or an array of acceptable charsets.\n *\n * The `charset` argument(s) can be:\n * - A single charset string (e.g., \"utf-8\")\n * - Multiple charset strings as arguments (e.g., `\"utf-8\", \"iso-8859-1\"`)\n * - A comma-delimited list of charsets (e.g., `\"utf-8, iso-8859-1\"`)\n *\n * Examples:\n *\n *     // Accept-Charset: utf-8, iso-8859-1\n *     req.acceptsCharsets('utf-8');\n *     // => \"utf-8\"\n *\n *     req.acceptsCharsets('utf-8', 'iso-8859-1');\n *     // => \"utf-8\"\n *\n *     req.acceptsCharsets('utf-8, utf-16');\n *     // => \"utf-8\"\n *\n * @param {...String} charsets - The charset(s) to check against the `Accept-Charset` header.\n * @return {String|Array} - The best matching charset, or an array of acceptable charsets.\n * @public\n */\n\nreq.acceptsCharsets = function(...charsets) {\n  const accept = accepts(this);\n  return accept.charsets(...charsets);\n};\n\n/**\n * Check if the given `lang`s are acceptable,\n * otherwise you should respond with 406 \"Not Acceptable\".\n *\n * @param {String} ...lang\n * @return {String|Array}\n * @public\n */\n\nreq.acceptsLanguages = function(...languages) {\n  return accepts(this).languages(...languages);\n};\n\n/**\n * Parse Range header field, capping to the given `size`.\n *\n * Unspecified ranges such as \"0-\" require knowledge of your resource length. In\n * the case of a byte range this is of course the total number of bytes. If the\n * Range header field is not given `undefined` is returned, `-1` when unsatisfiable,\n * and `-2` when syntactically invalid.\n *\n * When ranges are returned, the array has a \"type\" property which is the type of\n * range that is required (most commonly, \"bytes\"). Each array element is an object\n * with a \"start\" and \"end\" property for the portion of the range.\n *\n * The \"combine\" option can be set to `true` and overlapping & adjacent ranges\n * will be combined into a single range.\n *\n * NOTE: remember that ranges are inclusive, so for example \"Range: users=0-3\"\n * should respond with 4 users when available, not 3.\n *\n * @param {number} size\n * @param {object} [options]\n * @param {boolean} [options.combine=false]\n * @return {number|array}\n * @public\n */\n\nreq.range = function range(size, options) {\n  var range = this.get('Range');\n  if (!range) return;\n  return parseRange(size, range, options);\n};\n\n/**\n * Parse the query string of `req.url`.\n *\n * This uses the \"query parser\" setting to parse the raw\n * string into an object.\n *\n * @return {String}\n * @api public\n */\n\ndefineGetter(req, 'query', function query(){\n  var queryparse = this.app.get('query parser fn');\n\n  if (!queryparse) {\n    // parsing is disabled\n    return Object.create(null);\n  }\n\n  var querystring = parse(this).query;\n\n  return queryparse(querystring);\n});\n\n/**\n * Check if the incoming request contains the \"Content-Type\"\n * header field, and it contains the given mime `type`.\n *\n * Examples:\n *\n *      // With Content-Type: text/html; charset=utf-8\n *      req.is('html');\n *      req.is('text/html');\n *      req.is('text/*');\n *      // => true\n *\n *      // When Content-Type is application/json\n *      req.is('json');\n *      req.is('application/json');\n *      req.is('application/*');\n *      // => true\n *\n *      req.is('html');\n *      // => false\n *\n * @param {String|Array} types...\n * @return {String|false|null}\n * @public\n */\n\nreq.is = function is(types) {\n  var arr = types;\n\n  // support flattened arguments\n  if (!Array.isArray(types)) {\n    arr = new Array(arguments.length);\n    for (var i = 0; i < arr.length; i++) {\n      arr[i] = arguments[i];\n    }\n  }\n\n  return typeis(this, arr);\n};\n\n/**\n * Return the protocol string \"http\" or \"https\"\n * when requested with TLS. When the \"trust proxy\"\n * setting trusts the socket address, the\n * \"X-Forwarded-Proto\" header field will be trusted\n * and used if present.\n *\n * If you're running behind a reverse proxy that\n * supplies https for you this may be enabled.\n *\n * @return {String}\n * @public\n */\n\ndefineGetter(req, 'protocol', function protocol(){\n  var proto = this.socket.encrypted\n    ? 'https'\n    : 'http';\n  var trust = this.app.get('trust proxy fn');\n\n  if (!trust(this.socket.remoteAddress, 0)) {\n    return proto;\n  }\n\n  // Note: X-Forwarded-Proto is normally only ever a\n  //       single value, but this is to be safe.\n  var header = this.get('X-Forwarded-Proto') || proto\n  var index = header.indexOf(',')\n\n  return index !== -1\n    ? header.substring(0, index).trim()\n    : header.trim()\n});\n\n/**\n * Short-hand for:\n *\n *    req.protocol === 'https'\n *\n * @return {Boolean}\n * @public\n */\n\ndefineGetter(req, 'secure', function secure(){\n  return this.protocol === 'https';\n});\n\n/**\n * Return the remote address from the trusted proxy.\n *\n * The is the remote address on the socket unless\n * \"trust proxy\" is set.\n *\n * @return {String}\n * @public\n */\n\ndefineGetter(req, 'ip', function ip(){\n  var trust = this.app.get('trust proxy fn');\n  return proxyaddr(this, trust);\n});\n\n/**\n * When \"trust proxy\" is set, trusted proxy addresses + client.\n *\n * For example if the value were \"client, proxy1, proxy2\"\n * you would receive the array `[\"client\", \"proxy1\", \"proxy2\"]`\n * where \"proxy2\" is the furthest down-stream and \"proxy1\" and\n * \"proxy2\" were trusted.\n *\n * @return {Array}\n * @public\n */\n\ndefineGetter(req, 'ips', function ips() {\n  var trust = this.app.get('trust proxy fn');\n  var addrs = proxyaddr.all(this, trust);\n\n  // reverse the order (to farthest -> closest)\n  // and remove socket address\n  addrs.reverse().pop()\n\n  return addrs\n});\n\n/**\n * Return subdomains as an array.\n *\n * Subdomains are the dot-separated parts of the host before the main domain of\n * the app. By default, the domain of the app is assumed to be the last two\n * parts of the host. This can be changed by setting \"subdomain offset\".\n *\n * For example, if the domain is \"tobi.ferrets.example.com\":\n * If \"subdomain offset\" is not set, req.subdomains is `[\"ferrets\", \"tobi\"]`.\n * If \"subdomain offset\" is 3, req.subdomains is `[\"tobi\"]`.\n *\n * @return {Array}\n * @public\n */\n\ndefineGetter(req, 'subdomains', function subdomains() {\n  var hostname = this.hostname;\n\n  if (!hostname) return [];\n\n  var offset = this.app.get('subdomain offset');\n  var subdomains = !isIP(hostname)\n    ? hostname.split('.').reverse()\n    : [hostname];\n\n  return subdomains.slice(offset);\n});\n\n/**\n * Short-hand for `url.parse(req.url).pathname`.\n *\n * @return {String}\n * @public\n */\n\ndefineGetter(req, 'path', function path() {\n  return parse(this).pathname;\n});\n\n/**\n * Parse the \"Host\" header field to a host.\n *\n * When the \"trust proxy\" setting trusts the socket\n * address, the \"X-Forwarded-Host\" header field will\n * be trusted.\n *\n * @return {String}\n * @public\n */\n\ndefineGetter(req, 'host', function host(){\n  var trust = this.app.get('trust proxy fn');\n  var val = this.get('X-Forwarded-Host');\n\n  if (!val || !trust(this.socket.remoteAddress, 0)) {\n    val = this.get('Host');\n  } else if (val.indexOf(',') !== -1) {\n    // Note: X-Forwarded-Host is normally only ever a\n    //       single value, but this is to be safe.\n    val = val.substring(0, val.indexOf(',')).trimRight()\n  }\n\n  return val || undefined;\n});\n\n/**\n * Parse the \"Host\" header field to a hostname.\n *\n * When the \"trust proxy\" setting trusts the socket\n * address, the \"X-Forwarded-Host\" header field will\n * be trusted.\n *\n * @return {String}\n * @api public\n */\n\ndefineGetter(req, 'hostname', function hostname(){\n  var host = this.host;\n\n  if (!host) return;\n\n  // IPv6 literal support\n  var offset = host[0] === '['\n    ? host.indexOf(']') + 1\n    : 0;\n  var index = host.indexOf(':', offset);\n\n  return index !== -1\n    ? host.substring(0, index)\n    : host;\n});\n\n/**\n * Check if the request is fresh, aka\n * Last-Modified or the ETag\n * still match.\n *\n * @return {Boolean}\n * @public\n */\n\ndefineGetter(req, 'fresh', function(){\n  var method = this.method;\n  var res = this.res\n  var status = res.statusCode\n\n  // GET or HEAD for weak freshness validation only\n  if ('GET' !== method && 'HEAD' !== method) return false;\n\n  // 2xx or 304 as per rfc2616 14.26\n  if ((status >= 200 && status < 300) || 304 === status) {\n    return fresh(this.headers, {\n      'etag': res.get('ETag'),\n      'last-modified': res.get('Last-Modified')\n    })\n  }\n\n  return false;\n});\n\n/**\n * Check if the request is stale, aka\n * \"Last-Modified\" and / or the \"ETag\" for the\n * resource has changed.\n *\n * @return {Boolean}\n * @public\n */\n\ndefineGetter(req, 'stale', function stale(){\n  return !this.fresh;\n});\n\n/**\n * Check if the request was an _XMLHttpRequest_.\n *\n * @return {Boolean}\n * @public\n */\n\ndefineGetter(req, 'xhr', function xhr(){\n  var val = this.get('X-Requested-With') || '';\n  return val.toLowerCase() === 'xmlhttprequest';\n});\n\n/**\n * Helper function for creating a getter on an object.\n *\n * @param {Object} obj\n * @param {String} name\n * @param {Function} getter\n * @private\n */\nfunction defineGetter(obj, name, getter) {\n  Object.defineProperty(obj, name, {\n    configurable: true,\n    enumerable: true,\n    get: getter\n  });\n}\n"
  },
  {
    "path": "lib/response.js",
    "content": "/*!\n * express\n * Copyright(c) 2009-2013 TJ Holowaychuk\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module dependencies.\n * @private\n */\n\nvar contentDisposition = require('content-disposition');\nvar createError = require('http-errors')\nvar deprecate = require('depd')('express');\nvar encodeUrl = require('encodeurl');\nvar escapeHtml = require('escape-html');\nvar http = require('node:http');\nvar onFinished = require('on-finished');\nvar mime = require('mime-types')\nvar path = require('node:path');\nvar pathIsAbsolute = require('node:path').isAbsolute;\nvar statuses = require('statuses')\nvar sign = require('cookie-signature').sign;\nvar normalizeType = require('./utils').normalizeType;\nvar normalizeTypes = require('./utils').normalizeTypes;\nvar setCharset = require('./utils').setCharset;\nvar cookie = require('cookie');\nvar send = require('send');\nvar extname = path.extname;\nvar resolve = path.resolve;\nvar vary = require('vary');\nconst { Buffer } = require('node:buffer');\n\n/**\n * Response prototype.\n * @public\n */\n\nvar res = Object.create(http.ServerResponse.prototype)\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = res\n\n/**\n * Set the HTTP status code for the response.\n *\n * Expects an integer value between 100 and 999 inclusive.\n * Throws an error if the provided status code is not an integer or if it's outside the allowable range.\n *\n * @param {number} code - The HTTP status code to set.\n * @return {ServerResponse} - Returns itself for chaining methods.\n * @throws {TypeError} If `code` is not an integer.\n * @throws {RangeError} If `code` is outside the range 100 to 999.\n * @public\n */\n\nres.status = function status(code) {\n  // Check if the status code is not an integer\n  if (!Number.isInteger(code)) {\n    throw new TypeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be an integer.`);\n  }\n  // Check if the status code is outside of Node's valid range\n  if (code < 100 || code > 999) {\n    throw new RangeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be greater than 99 and less than 1000.`);\n  }\n\n  this.statusCode = code;\n  return this;\n};\n\n/**\n * Set Link header field with the given `links`.\n *\n * Examples:\n *\n *    res.links({\n *      next: 'http://api.example.com/users?page=2',\n *      last: 'http://api.example.com/users?page=5',\n *      pages: [\n *        'http://api.example.com/users?page=1',\n *        'http://api.example.com/users?page=2'\n *      ]\n *    });\n *\n * @param {Object} links\n * @return {ServerResponse}\n * @public\n */\n\nres.links = function(links) {\n  var link = this.get('Link') || '';\n  if (link) link += ', ';\n  return this.set('Link', link + Object.keys(links).map(function(rel) {\n    // Allow multiple links if links[rel] is an array\n    if (Array.isArray(links[rel])) {\n      return links[rel].map(function (singleLink) {\n        return `<${singleLink}>; rel=\"${rel}\"`;\n      }).join(', ');\n    } else {\n      return `<${links[rel]}>; rel=\"${rel}\"`;\n    }\n  }).join(', '));\n};\n\n/**\n * Send a response.\n *\n * Examples:\n *\n *     res.send(Buffer.from('wahoo'));\n *     res.send({ some: 'json' });\n *     res.send('<p>some html</p>');\n *\n * @param {string|number|boolean|object|Buffer} body\n * @public\n */\n\nres.send = function send(body) {\n  var chunk = body;\n  var encoding;\n  var req = this.req;\n\n  // settings\n  var app = this.app;\n\n  switch (typeof chunk) {\n    // string defaulting to html\n    case 'string':\n      encoding = 'utf8';\n      const type = this.get('Content-Type');\n\n      if (typeof type === 'string') {\n        this.set('Content-Type', setCharset(type, 'utf-8'));\n      } else {\n        this.type('html');\n      }\n      break;\n    case 'boolean':\n    case 'number':\n    case 'object':\n      if (chunk === null) {\n        chunk = '';\n      } else if (ArrayBuffer.isView(chunk)) {\n        if (!this.get('Content-Type')) {\n          this.type('bin');\n        }\n      } else {\n        return this.json(chunk);\n      }\n      break;\n  }\n\n  // determine if ETag should be generated\n  var etagFn = app.get('etag fn')\n  var generateETag = !this.get('ETag') && typeof etagFn === 'function'\n\n  // populate Content-Length\n  var len\n  if (chunk !== undefined) {\n    if (Buffer.isBuffer(chunk)) {\n      // get length of Buffer\n      len = chunk.length\n    } else if (!generateETag && chunk.length < 1000) {\n      // just calculate length when no ETag + small chunk\n      len = Buffer.byteLength(chunk, encoding)\n    } else {\n      // convert chunk to Buffer and calculate\n      chunk = Buffer.from(chunk, encoding)\n      encoding = undefined;\n      len = chunk.length\n    }\n\n    this.set('Content-Length', len);\n  }\n\n  // populate ETag\n  var etag;\n  if (generateETag && len !== undefined) {\n    if ((etag = etagFn(chunk, encoding))) {\n      this.set('ETag', etag);\n    }\n  }\n\n  // freshness\n  if (req.fresh) this.status(304);\n\n  // strip irrelevant headers\n  if (204 === this.statusCode || 304 === this.statusCode) {\n    this.removeHeader('Content-Type');\n    this.removeHeader('Content-Length');\n    this.removeHeader('Transfer-Encoding');\n    chunk = '';\n  }\n\n  // alter headers for 205\n  if (this.statusCode === 205) {\n    this.set('Content-Length', '0')\n    this.removeHeader('Transfer-Encoding')\n    chunk = ''\n  }\n\n  if (req.method === 'HEAD') {\n    // skip body for HEAD\n    this.end();\n  } else {\n    // respond\n    this.end(chunk, encoding);\n  }\n\n  return this;\n};\n\n/**\n * Send JSON response.\n *\n * Examples:\n *\n *     res.json(null);\n *     res.json({ user: 'tj' });\n *\n * @param {string|number|boolean|object} obj\n * @public\n */\n\nres.json = function json(obj) {\n  // settings\n  var app = this.app;\n  var escape = app.get('json escape')\n  var replacer = app.get('json replacer');\n  var spaces = app.get('json spaces');\n  var body = stringify(obj, replacer, spaces, escape)\n\n  // content-type\n  if (!this.get('Content-Type')) {\n    this.set('Content-Type', 'application/json');\n  }\n\n  return this.send(body);\n};\n\n/**\n * Send JSON response with JSONP callback support.\n *\n * Examples:\n *\n *     res.jsonp(null);\n *     res.jsonp({ user: 'tj' });\n *\n * @param {string|number|boolean|object} obj\n * @public\n */\n\nres.jsonp = function jsonp(obj) {\n  // settings\n  var app = this.app;\n  var escape = app.get('json escape')\n  var replacer = app.get('json replacer');\n  var spaces = app.get('json spaces');\n  var body = stringify(obj, replacer, spaces, escape)\n  var callback = this.req.query[app.get('jsonp callback name')];\n\n  // content-type\n  if (!this.get('Content-Type')) {\n    this.set('X-Content-Type-Options', 'nosniff');\n    this.set('Content-Type', 'application/json');\n  }\n\n  // fixup callback\n  if (Array.isArray(callback)) {\n    callback = callback[0];\n  }\n\n  // jsonp\n  if (typeof callback === 'string' && callback.length !== 0) {\n    this.set('X-Content-Type-Options', 'nosniff');\n    this.set('Content-Type', 'text/javascript');\n\n    // restrict callback charset\n    callback = callback.replace(/[^\\[\\]\\w$.]/g, '');\n\n    if (body === undefined) {\n      // empty argument\n      body = ''\n    } else if (typeof body === 'string') {\n      // replace chars not allowed in JavaScript that are in JSON\n      body = body\n        .replace(/\\u2028/g, '\\\\u2028')\n        .replace(/\\u2029/g, '\\\\u2029')\n    }\n\n    // the /**/ is a specific security mitigation for \"Rosetta Flash JSONP abuse\"\n    // the typeof check is just to reduce client error noise\n    body = '/**/ typeof ' + callback + ' === \\'function\\' && ' + callback + '(' + body + ');';\n  }\n\n  return this.send(body);\n};\n\n/**\n * Send given HTTP status code.\n *\n * Sets the response status to `statusCode` and the body of the\n * response to the standard description from node's http.STATUS_CODES\n * or the statusCode number if no description.\n *\n * Examples:\n *\n *     res.sendStatus(200);\n *\n * @param {number} statusCode\n * @public\n */\n\nres.sendStatus = function sendStatus(statusCode) {\n  var body = statuses.message[statusCode] || String(statusCode)\n\n  this.status(statusCode);\n  this.type('txt');\n\n  return this.send(body);\n};\n\n/**\n * Transfer the file at the given `path`.\n *\n * Automatically sets the _Content-Type_ response header field.\n * The callback `callback(err)` is invoked when the transfer is complete\n * or when an error occurs. Be sure to check `res.headersSent`\n * if you wish to attempt responding, as the header and some data\n * may have already been transferred.\n *\n * Options:\n *\n *   - `maxAge`   defaulting to 0 (can be string converted by `ms`)\n *   - `root`     root directory for relative filenames\n *   - `headers`  object of headers to serve with file\n *   - `dotfiles` serve dotfiles, defaulting to false; can be `\"allow\"` to send them\n *\n * Other options are passed along to `send`.\n *\n * Examples:\n *\n *  The following example illustrates how `res.sendFile()` may\n *  be used as an alternative for the `static()` middleware for\n *  dynamic situations. The code backing `res.sendFile()` is actually\n *  the same code, so HTTP cache support etc is identical.\n *\n *     app.get('/user/:uid/photos/:file', function(req, res){\n *       var uid = req.params.uid\n *         , file = req.params.file;\n *\n *       req.user.mayViewFilesFrom(uid, function(yes){\n *         if (yes) {\n *           res.sendFile('/uploads/' + uid + '/' + file);\n *         } else {\n *           res.send(403, 'Sorry! you cant see that.');\n *         }\n *       });\n *     });\n *\n * @public\n */\n\nres.sendFile = function sendFile(path, options, callback) {\n  var done = callback;\n  var req = this.req;\n  var res = this;\n  var next = req.next;\n  var opts = options || {};\n\n  if (!path) {\n    throw new TypeError('path argument is required to res.sendFile');\n  }\n\n  if (typeof path !== 'string') {\n    throw new TypeError('path must be a string to res.sendFile')\n  }\n\n  // support function as second arg\n  if (typeof options === 'function') {\n    done = options;\n    opts = {};\n  }\n\n  if (!opts.root && !pathIsAbsolute(path)) {\n    throw new TypeError('path must be absolute or specify root to res.sendFile');\n  }\n\n  // create file stream\n  var pathname = encodeURI(path);\n\n  // wire application etag option to send\n  opts.etag = this.app.enabled('etag');\n  var file = send(req, pathname, opts);\n\n  // transfer\n  sendfile(res, file, opts, function (err) {\n    if (done) return done(err);\n    if (err && err.code === 'EISDIR') return next();\n\n    // next() all but write errors\n    if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {\n      next(err);\n    }\n  });\n};\n\n/**\n * Transfer the file at the given `path` as an attachment.\n *\n * Optionally providing an alternate attachment `filename`,\n * and optional callback `callback(err)`. The callback is invoked\n * when the data transfer is complete, or when an error has\n * occurred. Be sure to check `res.headersSent` if you plan to respond.\n *\n * Optionally providing an `options` object to use with `res.sendFile()`.\n * This function will set the `Content-Disposition` header, overriding\n * any `Content-Disposition` header passed as header options in order\n * to set the attachment and filename.\n *\n * This method uses `res.sendFile()`.\n *\n * @public\n */\n\nres.download = function download (path, filename, options, callback) {\n  var done = callback;\n  var name = filename;\n  var opts = options || null\n\n  // support function as second or third arg\n  if (typeof filename === 'function') {\n    done = filename;\n    name = null;\n    opts = null\n  } else if (typeof options === 'function') {\n    done = options\n    opts = null\n  }\n\n  // support optional filename, where options may be in it's place\n  if (typeof filename === 'object' &&\n    (typeof options === 'function' || options === undefined)) {\n    name = null\n    opts = filename\n  }\n\n  // set Content-Disposition when file is sent\n  var headers = {\n    'Content-Disposition': contentDisposition(name || path)\n  };\n\n  // merge user-provided headers\n  if (opts && opts.headers) {\n    var keys = Object.keys(opts.headers)\n    for (var i = 0; i < keys.length; i++) {\n      var key = keys[i]\n      if (key.toLowerCase() !== 'content-disposition') {\n        headers[key] = opts.headers[key]\n      }\n    }\n  }\n\n  // merge user-provided options\n  opts = Object.create(opts)\n  opts.headers = headers\n\n  // Resolve the full path for sendFile\n  var fullPath = !opts.root\n    ? resolve(path)\n    : path\n\n  // send file\n  return this.sendFile(fullPath, opts, done)\n};\n\n/**\n * Set _Content-Type_ response header with `type` through `mime.contentType()`\n * when it does not contain \"/\", or set the Content-Type to `type` otherwise.\n * When no mapping is found though `mime.contentType()`, the type is set to\n * \"application/octet-stream\".\n *\n * Examples:\n *\n *     res.type('.html');\n *     res.type('html');\n *     res.type('json');\n *     res.type('application/json');\n *     res.type('png');\n *\n * @param {String} type\n * @return {ServerResponse} for chaining\n * @public\n */\n\nres.contentType =\nres.type = function contentType(type) {\n  var ct = type.indexOf('/') === -1\n    ? (mime.contentType(type) || 'application/octet-stream')\n    : type;\n\n  return this.set('Content-Type', ct);\n};\n\n/**\n * Respond to the Acceptable formats using an `obj`\n * of mime-type callbacks.\n *\n * This method uses `req.accepted`, an array of\n * acceptable types ordered by their quality values.\n * When \"Accept\" is not present the _first_ callback\n * is invoked, otherwise the first match is used. When\n * no match is performed the server responds with\n * 406 \"Not Acceptable\".\n *\n * Content-Type is set for you, however if you choose\n * you may alter this within the callback using `res.type()`\n * or `res.set('Content-Type', ...)`.\n *\n *    res.format({\n *      'text/plain': function(){\n *        res.send('hey');\n *      },\n *\n *      'text/html': function(){\n *        res.send('<p>hey</p>');\n *      },\n *\n *      'application/json': function () {\n *        res.send({ message: 'hey' });\n *      }\n *    });\n *\n * In addition to canonicalized MIME types you may\n * also use extnames mapped to these types:\n *\n *    res.format({\n *      text: function(){\n *        res.send('hey');\n *      },\n *\n *      html: function(){\n *        res.send('<p>hey</p>');\n *      },\n *\n *      json: function(){\n *        res.send({ message: 'hey' });\n *      }\n *    });\n *\n * By default Express passes an `Error`\n * with a `.status` of 406 to `next(err)`\n * if a match is not made. If you provide\n * a `.default` callback it will be invoked\n * instead.\n *\n * @param {Object} obj\n * @return {ServerResponse} for chaining\n * @public\n */\n\nres.format = function(obj){\n  var req = this.req;\n  var next = req.next;\n\n  var keys = Object.keys(obj)\n    .filter(function (v) { return v !== 'default' })\n\n  var key = keys.length > 0\n    ? req.accepts(keys)\n    : false;\n\n  this.vary(\"Accept\");\n\n  if (key) {\n    this.set('Content-Type', normalizeType(key).value);\n    obj[key](req, this, next);\n  } else if (obj.default) {\n    obj.default(req, this, next)\n  } else {\n    next(createError(406, {\n      types: normalizeTypes(keys).map(function (o) { return o.value })\n    }))\n  }\n\n  return this;\n};\n\n/**\n * Set _Content-Disposition_ header to _attachment_ with optional `filename`.\n *\n * @param {String} filename\n * @return {ServerResponse}\n * @public\n */\n\nres.attachment = function attachment(filename) {\n  if (filename) {\n    this.type(extname(filename));\n  }\n\n  this.set('Content-Disposition', contentDisposition(filename));\n\n  return this;\n};\n\n/**\n * Append additional header `field` with value `val`.\n *\n * Example:\n *\n *    res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);\n *    res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');\n *    res.append('Warning', '199 Miscellaneous warning');\n *\n * @param {String} field\n * @param {String|Array} val\n * @return {ServerResponse} for chaining\n * @public\n */\n\nres.append = function append(field, val) {\n  var prev = this.get(field);\n  var value = val;\n\n  if (prev) {\n    // concat the new and prev vals\n    value = Array.isArray(prev) ? prev.concat(val)\n      : Array.isArray(val) ? [prev].concat(val)\n        : [prev, val]\n  }\n\n  return this.set(field, value);\n};\n\n/**\n * Set header `field` to `val`, or pass\n * an object of header fields.\n *\n * Examples:\n *\n *    res.set('Foo', ['bar', 'baz']);\n *    res.set('Accept', 'application/json');\n *    res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });\n *\n * Aliased as `res.header()`.\n *\n * When the set header is \"Content-Type\", the type is expanded to include\n * the charset if not present using `mime.contentType()`.\n *\n * @param {String|Object} field\n * @param {String|Array} val\n * @return {ServerResponse} for chaining\n * @public\n */\n\nres.set =\nres.header = function header(field, val) {\n  if (arguments.length === 2) {\n    var value = Array.isArray(val)\n      ? val.map(String)\n      : String(val);\n\n    // add charset to content-type\n    if (field.toLowerCase() === 'content-type') {\n      if (Array.isArray(value)) {\n        throw new TypeError('Content-Type cannot be set to an Array');\n      }\n      value = mime.contentType(value)\n    }\n\n    this.setHeader(field, value);\n  } else {\n    for (var key in field) {\n      this.set(key, field[key]);\n    }\n  }\n  return this;\n};\n\n/**\n * Get value for header `field`.\n *\n * @param {String} field\n * @return {String}\n * @public\n */\n\nres.get = function(field){\n  return this.getHeader(field);\n};\n\n/**\n * Clear cookie `name`.\n *\n * @param {String} name\n * @param {Object} [options]\n * @return {ServerResponse} for chaining\n * @public\n */\n\nres.clearCookie = function clearCookie(name, options) {\n  // Force cookie expiration by setting expires to the past\n  const opts = { path: '/', ...options, expires: new Date(1)};\n  // ensure maxAge is not passed\n  delete opts.maxAge\n\n  return this.cookie(name, '', opts);\n};\n\n/**\n * Set cookie `name` to `value`, with the given `options`.\n *\n * Options:\n *\n *    - `maxAge`   max-age in milliseconds, converted to `expires`\n *    - `signed`   sign the cookie\n *    - `path`     defaults to \"/\"\n *\n * Examples:\n *\n *    // \"Remember Me\" for 15 minutes\n *    res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });\n *\n *    // same as above\n *    res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })\n *\n * @param {String} name\n * @param {String|Object} value\n * @param {Object} [options]\n * @return {ServerResponse} for chaining\n * @public\n */\n\nres.cookie = function (name, value, options) {\n  var opts = { ...options };\n  var secret = this.req.secret;\n  var signed = opts.signed;\n\n  if (signed && !secret) {\n    throw new Error('cookieParser(\"secret\") required for signed cookies');\n  }\n\n  var val = typeof value === 'object'\n    ? 'j:' + JSON.stringify(value)\n    : String(value);\n\n  if (signed) {\n    val = 's:' + sign(val, secret);\n  }\n\n  if (opts.maxAge != null) {\n    var maxAge = opts.maxAge - 0\n\n    if (!isNaN(maxAge)) {\n      opts.expires = new Date(Date.now() + maxAge)\n      opts.maxAge = Math.floor(maxAge / 1000)\n    }\n  }\n\n  if (opts.path == null) {\n    opts.path = '/';\n  }\n\n  this.append('Set-Cookie', cookie.serialize(name, String(val), opts));\n\n  return this;\n};\n\n/**\n * Set the location header to `url`.\n *\n * The given `url` can also be \"back\", which redirects\n * to the _Referrer_ or _Referer_ headers or \"/\".\n *\n * Examples:\n *\n *    res.location('/foo/bar').;\n *    res.location('http://example.com');\n *    res.location('../login');\n *\n * @param {String} url\n * @return {ServerResponse} for chaining\n * @public\n */\n\nres.location = function location(url) {\n  return this.set('Location', encodeUrl(url));\n};\n\n/**\n * Redirect to the given `url` with optional response `status`\n * defaulting to 302.\n *\n * Examples:\n *\n *    res.redirect('/foo/bar');\n *    res.redirect('http://example.com');\n *    res.redirect(301, 'http://example.com');\n *    res.redirect('../login'); // /blog/post/1 -> /blog/login\n *\n * @public\n */\n\nres.redirect = function redirect(url) {\n  var address = url;\n  var body;\n  var status = 302;\n\n  // allow status / url\n  if (arguments.length === 2) {\n    status = arguments[0]\n    address = arguments[1]\n  }\n\n  if (!address) {\n    deprecate('Provide a url argument');\n  }\n\n  if (typeof address !== 'string') {\n    deprecate('Url must be a string');\n  }\n\n  if (typeof status !== 'number') {\n    deprecate('Status must be a number');\n  }\n\n  // Set location header\n  address = this.location(address).get('Location');\n\n  // Support text/{plain,html} by default\n  this.format({\n    text: function(){\n      body = statuses.message[status] + '. Redirecting to ' + address\n    },\n\n    html: function(){\n      var u = escapeHtml(address);\n      body = '<!DOCTYPE html><head><title>' + statuses.message[status] + '</title></head>'\n       + '<body><p>' + statuses.message[status] + '. Redirecting to ' + u + '</p></body>'\n    },\n\n    default: function(){\n      body = '';\n    }\n  });\n\n  // Respond\n  this.status(status);\n  this.set('Content-Length', Buffer.byteLength(body));\n\n  if (this.req.method === 'HEAD') {\n    this.end();\n  } else {\n    this.end(body);\n  }\n};\n\n/**\n * Add `field` to Vary. If already present in the Vary set, then\n * this call is simply ignored.\n *\n * @param {Array|String} field\n * @return {ServerResponse} for chaining\n * @public\n */\n\nres.vary = function(field){\n  vary(this, field);\n\n  return this;\n};\n\n/**\n * Render `view` with the given `options` and optional callback `fn`.\n * When a callback function is given a response will _not_ be made\n * automatically, otherwise a response of _200_ and _text/html_ is given.\n *\n * Options:\n *\n *  - `cache`     boolean hinting to the engine it should cache\n *  - `filename`  filename of the view being rendered\n *\n * @public\n */\n\nres.render = function render(view, options, callback) {\n  var app = this.req.app;\n  var done = callback;\n  var opts = options || {};\n  var req = this.req;\n  var self = this;\n\n  // support callback function as second arg\n  if (typeof options === 'function') {\n    done = options;\n    opts = {};\n  }\n\n  // merge res.locals\n  opts._locals = self.locals;\n\n  // default callback to respond\n  done = done || function (err, str) {\n    if (err) return req.next(err);\n    self.send(str);\n  };\n\n  // render\n  app.render(view, opts, done);\n};\n\n// pipe the send file stream\nfunction sendfile(res, file, options, callback) {\n  var done = false;\n  var streaming;\n\n  // request aborted\n  function onaborted() {\n    if (done) return;\n    done = true;\n\n    var err = new Error('Request aborted');\n    err.code = 'ECONNABORTED';\n    callback(err);\n  }\n\n  // directory\n  function ondirectory() {\n    if (done) return;\n    done = true;\n\n    var err = new Error('EISDIR, read');\n    err.code = 'EISDIR';\n    callback(err);\n  }\n\n  // errors\n  function onerror(err) {\n    if (done) return;\n    done = true;\n    callback(err);\n  }\n\n  // ended\n  function onend() {\n    if (done) return;\n    done = true;\n    callback();\n  }\n\n  // file\n  function onfile() {\n    streaming = false;\n  }\n\n  // finished\n  function onfinish(err) {\n    if (err && err.code === 'ECONNRESET') return onaborted();\n    if (err) return onerror(err);\n    if (done) return;\n\n    setImmediate(function () {\n      if (streaming !== false && !done) {\n        onaborted();\n        return;\n      }\n\n      if (done) return;\n      done = true;\n      callback();\n    });\n  }\n\n  // streaming\n  function onstream() {\n    streaming = true;\n  }\n\n  file.on('directory', ondirectory);\n  file.on('end', onend);\n  file.on('error', onerror);\n  file.on('file', onfile);\n  file.on('stream', onstream);\n  onFinished(res, onfinish);\n\n  if (options.headers) {\n    // set headers on successful transfer\n    file.on('headers', function headers(res) {\n      var obj = options.headers;\n      var keys = Object.keys(obj);\n\n      for (var i = 0; i < keys.length; i++) {\n        var k = keys[i];\n        res.setHeader(k, obj[k]);\n      }\n    });\n  }\n\n  // pipe\n  file.pipe(res);\n}\n\n/**\n * Stringify JSON, like JSON.stringify, but v8 optimized, with the\n * ability to escape characters that can trigger HTML sniffing.\n *\n * @param {*} value\n * @param {function} replacer\n * @param {number} spaces\n * @param {boolean} escape\n * @returns {string}\n * @private\n */\n\nfunction stringify (value, replacer, spaces, escape) {\n  // v8 checks arguments.length for optimizing simple call\n  // https://bugs.chromium.org/p/v8/issues/detail?id=4730\n  var json = replacer || spaces\n    ? JSON.stringify(value, replacer, spaces)\n    : JSON.stringify(value);\n\n  if (escape && typeof json === 'string') {\n    json = json.replace(/[<>&]/g, function (c) {\n      switch (c.charCodeAt(0)) {\n        case 0x3c:\n          return '\\\\u003c'\n        case 0x3e:\n          return '\\\\u003e'\n        case 0x26:\n          return '\\\\u0026'\n        /* istanbul ignore next: unreachable default */\n        default:\n          return c\n      }\n    })\n  }\n\n  return json\n}\n"
  },
  {
    "path": "lib/utils.js",
    "content": "/*!\n * express\n * Copyright(c) 2009-2013 TJ Holowaychuk\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module dependencies.\n * @api private\n */\n\nvar { METHODS } = require('node:http');\nvar contentType = require('content-type');\nvar etag = require('etag');\nvar mime = require('mime-types')\nvar proxyaddr = require('proxy-addr');\nvar qs = require('qs');\nvar querystring = require('node:querystring');\nconst { Buffer } = require('node:buffer');\n\n\n/**\n * A list of lowercased HTTP methods that are supported by Node.js.\n * @api private\n */\nexports.methods = METHODS.map((method) => method.toLowerCase());\n\n/**\n * Return strong ETag for `body`.\n *\n * @param {String|Buffer} body\n * @param {String} [encoding]\n * @return {String}\n * @api private\n */\n\nexports.etag = createETagGenerator({ weak: false })\n\n/**\n * Return weak ETag for `body`.\n *\n * @param {String|Buffer} body\n * @param {String} [encoding]\n * @return {String}\n * @api private\n */\n\nexports.wetag = createETagGenerator({ weak: true })\n\n/**\n * Normalize the given `type`, for example \"html\" becomes \"text/html\".\n *\n * @param {String} type\n * @return {Object}\n * @api private\n */\n\nexports.normalizeType = function(type){\n  return ~type.indexOf('/')\n    ? acceptParams(type)\n    : { value: (mime.lookup(type) || 'application/octet-stream'), params: {} }\n};\n\n/**\n * Normalize `types`, for example \"html\" becomes \"text/html\".\n *\n * @param {Array} types\n * @return {Array}\n * @api private\n */\n\nexports.normalizeTypes = function(types) {\n  return types.map(exports.normalizeType);\n};\n\n\n/**\n * Parse accept params `str` returning an\n * object with `.value`, `.quality` and `.params`.\n *\n * @param {String} str\n * @return {Object}\n * @api private\n */\n\nfunction acceptParams (str) {\n  var length = str.length;\n  var colonIndex = str.indexOf(';');\n  var index = colonIndex === -1 ? length : colonIndex;\n  var ret = { value: str.slice(0, index).trim(), quality: 1, params: {} };\n\n  while (index < length) {\n    var splitIndex = str.indexOf('=', index);\n    if (splitIndex === -1) break;\n\n    var colonIndex = str.indexOf(';', index);\n    var endIndex = colonIndex === -1 ? length : colonIndex;\n\n    if (splitIndex > endIndex) {\n      index = str.lastIndexOf(';', splitIndex - 1) + 1;\n      continue;\n    }\n\n    var key = str.slice(index, splitIndex).trim();\n    var value = str.slice(splitIndex + 1, endIndex).trim();\n\n    if (key === 'q') {\n      ret.quality = parseFloat(value);\n    } else {\n      ret.params[key] = value;\n    }\n\n    index = endIndex + 1;\n  }\n\n  return ret;\n}\n\n/**\n * Compile \"etag\" value to function.\n *\n * @param  {Boolean|String|Function} val\n * @return {Function}\n * @api private\n */\n\nexports.compileETag = function(val) {\n  var fn;\n\n  if (typeof val === 'function') {\n    return val;\n  }\n\n  switch (val) {\n    case true:\n    case 'weak':\n      fn = exports.wetag;\n      break;\n    case false:\n      break;\n    case 'strong':\n      fn = exports.etag;\n      break;\n    default:\n      throw new TypeError('unknown value for etag function: ' + val);\n  }\n\n  return fn;\n}\n\n/**\n * Compile \"query parser\" value to function.\n *\n * @param  {String|Function} val\n * @return {Function}\n * @api private\n */\n\nexports.compileQueryParser = function compileQueryParser(val) {\n  var fn;\n\n  if (typeof val === 'function') {\n    return val;\n  }\n\n  switch (val) {\n    case true:\n    case 'simple':\n      fn = querystring.parse;\n      break;\n    case false:\n      break;\n    case 'extended':\n      fn = parseExtendedQueryString;\n      break;\n    default:\n      throw new TypeError('unknown value for query parser function: ' + val);\n  }\n\n  return fn;\n}\n\n/**\n * Compile \"proxy trust\" value to function.\n *\n * @param  {Boolean|String|Number|Array|Function} val\n * @return {Function}\n * @api private\n */\n\nexports.compileTrust = function(val) {\n  if (typeof val === 'function') return val;\n\n  if (val === true) {\n    // Support plain true/false\n    return function(){ return true };\n  }\n\n  if (typeof val === 'number') {\n    // Support trusting hop count\n    return function(a, i){ return i < val };\n  }\n\n  if (typeof val === 'string') {\n    // Support comma-separated values\n    val = val.split(',')\n      .map(function (v) { return v.trim() })\n  }\n\n  return proxyaddr.compile(val || []);\n}\n\n/**\n * Set the charset in a given Content-Type string.\n *\n * @param {String} type\n * @param {String} charset\n * @return {String}\n * @api private\n */\n\nexports.setCharset = function setCharset(type, charset) {\n  if (!type || !charset) {\n    return type;\n  }\n\n  // parse type\n  var parsed = contentType.parse(type);\n\n  // set charset\n  parsed.parameters.charset = charset;\n\n  // format type\n  return contentType.format(parsed);\n};\n\n/**\n * Create an ETag generator function, generating ETags with\n * the given options.\n *\n * @param {object} options\n * @return {function}\n * @private\n */\n\nfunction createETagGenerator (options) {\n  return function generateETag (body, encoding) {\n    var buf = !Buffer.isBuffer(body)\n      ? Buffer.from(body, encoding)\n      : body\n\n    return etag(buf, options)\n  }\n}\n\n/**\n * Parse an extended query string with qs.\n *\n * @param {String} str\n * @return {Object}\n * @private\n */\n\nfunction parseExtendedQueryString(str) {\n  return qs.parse(str, {\n    allowPrototypes: true\n  });\n}\n"
  },
  {
    "path": "lib/view.js",
    "content": "/*!\n * express\n * Copyright(c) 2009-2013 TJ Holowaychuk\n * Copyright(c) 2013 Roman Shtylman\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module dependencies.\n * @private\n */\n\nvar debug = require('debug')('express:view');\nvar path = require('node:path');\nvar fs = require('node:fs');\n\n/**\n * Module variables.\n * @private\n */\n\nvar dirname = path.dirname;\nvar basename = path.basename;\nvar extname = path.extname;\nvar join = path.join;\nvar resolve = path.resolve;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = View;\n\n/**\n * Initialize a new `View` with the given `name`.\n *\n * Options:\n *\n *   - `defaultEngine` the default template engine name\n *   - `engines` template engine require() cache\n *   - `root` root path for view lookup\n *\n * @param {string} name\n * @param {object} options\n * @public\n */\n\nfunction View(name, options) {\n  var opts = options || {};\n\n  this.defaultEngine = opts.defaultEngine;\n  this.ext = extname(name);\n  this.name = name;\n  this.root = opts.root;\n\n  if (!this.ext && !this.defaultEngine) {\n    throw new Error('No default engine was specified and no extension was provided.');\n  }\n\n  var fileName = name;\n\n  if (!this.ext) {\n    // get extension from default engine name\n    this.ext = this.defaultEngine[0] !== '.'\n      ? '.' + this.defaultEngine\n      : this.defaultEngine;\n\n    fileName += this.ext;\n  }\n\n  if (!opts.engines[this.ext]) {\n    // load engine\n    var mod = this.ext.slice(1)\n    debug('require \"%s\"', mod)\n\n    // default engine export\n    var fn = require(mod).__express\n\n    if (typeof fn !== 'function') {\n      throw new Error('Module \"' + mod + '\" does not provide a view engine.')\n    }\n\n    opts.engines[this.ext] = fn\n  }\n\n  // store loaded engine\n  this.engine = opts.engines[this.ext];\n\n  // lookup path\n  this.path = this.lookup(fileName);\n}\n\n/**\n * Lookup view by the given `name`\n *\n * @param {string} name\n * @private\n */\n\nView.prototype.lookup = function lookup(name) {\n  var path;\n  var roots = [].concat(this.root);\n\n  debug('lookup \"%s\"', name);\n\n  for (var i = 0; i < roots.length && !path; i++) {\n    var root = roots[i];\n\n    // resolve the path\n    var loc = resolve(root, name);\n    var dir = dirname(loc);\n    var file = basename(loc);\n\n    // resolve the file\n    path = this.resolve(dir, file);\n  }\n\n  return path;\n};\n\n/**\n * Render with the given options.\n *\n * @param {object} options\n * @param {function} callback\n * @private\n */\n\nView.prototype.render = function render(options, callback) {\n  var sync = true;\n\n  debug('render \"%s\"', this.path);\n\n  // render, normalizing sync callbacks\n  this.engine(this.path, options, function onRender() {\n    if (!sync) {\n      return callback.apply(this, arguments);\n    }\n\n    // copy arguments\n    var args = new Array(arguments.length);\n    var cntx = this;\n\n    for (var i = 0; i < arguments.length; i++) {\n      args[i] = arguments[i];\n    }\n\n    // force callback to be async\n    return process.nextTick(function renderTick() {\n      return callback.apply(cntx, args);\n    });\n  });\n\n  sync = false;\n};\n\n/**\n * Resolve the file within the given directory.\n *\n * @param {string} dir\n * @param {string} file\n * @private\n */\n\nView.prototype.resolve = function resolve(dir, file) {\n  var ext = this.ext;\n\n  // <path>.<ext>\n  var path = join(dir, file);\n  var stat = tryStat(path);\n\n  if (stat && stat.isFile()) {\n    return path;\n  }\n\n  // <path>/index.<ext>\n  path = join(dir, basename(file, ext), 'index' + ext);\n  stat = tryStat(path);\n\n  if (stat && stat.isFile()) {\n    return path;\n  }\n};\n\n/**\n * Return a stat, maybe.\n *\n * @param {string} path\n * @return {fs.Stats}\n * @private\n */\n\nfunction tryStat(path) {\n  debug('stat \"%s\"', path);\n\n  try {\n    return fs.statSync(path);\n  } catch (e) {\n    return undefined;\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"express\",\n  \"description\": \"Fast, unopinionated, minimalist web framework\",\n  \"version\": \"5.2.1\",\n  \"author\": \"TJ Holowaychuk <tj@vision-media.ca>\",\n  \"contributors\": [\n    \"Aaron Heckmann <aaron.heckmann+github@gmail.com>\",\n    \"Ciaran Jessup <ciaranj@gmail.com>\",\n    \"Douglas Christopher Wilson <doug@somethingdoug.com>\",\n    \"Guillermo Rauch <rauchg@gmail.com>\",\n    \"Jonathan Ong <me@jongleberry.com>\",\n    \"Roman Shtylman <shtylman+expressjs@gmail.com>\",\n    \"Young Jae Sim <hanul@hanul.me>\"\n  ],\n  \"license\": \"MIT\",\n  \"repository\": \"expressjs/express\",\n  \"homepage\": \"https://expressjs.com/\",\n  \"funding\": {\n    \"type\": \"opencollective\",\n    \"url\": \"https://opencollective.com/express\"\n  },\n  \"keywords\": [\n    \"express\",\n    \"framework\",\n    \"sinatra\",\n    \"web\",\n    \"http\",\n    \"rest\",\n    \"restful\",\n    \"router\",\n    \"app\",\n    \"api\"\n  ],\n  \"dependencies\": {\n    \"accepts\": \"^2.0.0\",\n    \"body-parser\": \"^2.2.1\",\n    \"content-disposition\": \"^1.0.0\",\n    \"content-type\": \"^1.0.5\",\n    \"cookie\": \"^0.7.1\",\n    \"cookie-signature\": \"^1.2.1\",\n    \"debug\": \"^4.4.0\",\n    \"depd\": \"^2.0.0\",\n    \"encodeurl\": \"^2.0.0\",\n    \"escape-html\": \"^1.0.3\",\n    \"etag\": \"^1.8.1\",\n    \"finalhandler\": \"^2.1.0\",\n    \"fresh\": \"^2.0.0\",\n    \"http-errors\": \"^2.0.0\",\n    \"merge-descriptors\": \"^2.0.0\",\n    \"mime-types\": \"^3.0.0\",\n    \"on-finished\": \"^2.4.1\",\n    \"once\": \"^1.4.0\",\n    \"parseurl\": \"^1.3.3\",\n    \"proxy-addr\": \"^2.0.7\",\n    \"qs\": \"^6.14.2\",\n    \"range-parser\": \"^1.2.1\",\n    \"router\": \"^2.2.0\",\n    \"send\": \"^1.1.0\",\n    \"serve-static\": \"^2.2.0\",\n    \"statuses\": \"^2.0.1\",\n    \"type-is\": \"^2.0.1\",\n    \"vary\": \"^1.1.2\"\n  },\n  \"devDependencies\": {\n    \"after\": \"0.8.2\",\n    \"connect-redis\": \"^8.0.1\",\n    \"cookie-parser\": \"1.4.7\",\n    \"cookie-session\": \"2.1.1\",\n    \"ejs\": \"^3.1.10\",\n    \"eslint\": \"8.47.0\",\n    \"express-session\": \"^1.18.1\",\n    \"hbs\": \"4.2.0\",\n    \"marked\": \"^15.0.3\",\n    \"method-override\": \"3.0.0\",\n    \"mocha\": \"^10.7.3\",\n    \"morgan\": \"1.10.1\",\n    \"nyc\": \"^17.1.0\",\n    \"pbkdf2-password\": \"1.2.1\",\n    \"supertest\": \"^6.3.0\",\n    \"vhost\": \"~3.0.2\"\n  },\n  \"engines\": {\n    \"node\": \">= 18\"\n  },\n  \"files\": [\n    \"LICENSE\",\n    \"Readme.md\",\n    \"index.js\",\n    \"lib/\"\n  ],\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"eslint . --fix\",\n    \"test\": \"mocha --require test/support/env --reporter spec --check-leaks test/ test/acceptance/\",\n    \"test-ci\": \"nyc --exclude examples --exclude test --exclude benchmarks --reporter=lcovonly --reporter=text npm test\",\n    \"test-cov\": \"nyc --exclude examples --exclude test --exclude benchmarks --reporter=html --reporter=text npm test\",\n    \"test-tap\": \"mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/\"\n  }\n}\n"
  },
  {
    "path": "test/Route.js",
    "content": "'use strict'\n\nvar after = require('after');\nvar assert = require('node:assert')\nvar express = require('../')\n  , Route = express.Route\n  , methods = require('../lib/utils').methods\n\ndescribe('Route', function(){\n  it('should work without handlers', function(done) {\n    var req = { method: 'GET', url: '/' }\n    var route = new Route('/foo')\n    route.dispatch(req, {}, done)\n  })\n\n  it('should not stack overflow with a large sync stack', function (done) {\n    this.timeout(5000) // long-running test\n\n    var req = { method: 'GET', url: '/' }\n    var route = new Route('/foo')\n\n    route.get(function (req, res, next) {\n      req.counter = 0\n      next()\n    })\n\n    for (var i = 0; i < 6000; i++) {\n      route.all(function (req, res, next) {\n        req.counter++\n        next()\n      })\n    }\n\n    route.get(function (req, res, next) {\n      req.called = true\n      next()\n    })\n\n    route.dispatch(req, {}, function (err) {\n      if (err) return done(err)\n      assert.ok(req.called)\n      assert.strictEqual(req.counter, 6000)\n      done()\n    })\n  })\n\n  describe('.all', function(){\n    it('should add handler', function(done){\n      var req = { method: 'GET', url: '/' };\n      var route = new Route('/foo');\n\n      route.all(function(req, res, next) {\n        req.called = true;\n        next();\n      });\n\n      route.dispatch(req, {}, function (err) {\n        if (err) return done(err);\n        assert.ok(req.called)\n        done();\n      });\n    })\n\n    it('should handle VERBS', function(done) {\n      var count = 0;\n      var route = new Route('/foo');\n      var cb = after(methods.length, function (err) {\n        if (err) return done(err);\n        assert.strictEqual(count, methods.length)\n        done();\n      });\n\n      route.all(function(req, res, next) {\n        count++;\n        next();\n      });\n\n      methods.forEach(function testMethod(method) {\n        var req = { method: method, url: '/' };\n        route.dispatch(req, {}, cb);\n      });\n    })\n\n    it('should stack', function(done) {\n      var req = { count: 0, method: 'GET', url: '/' };\n      var route = new Route('/foo');\n\n      route.all(function(req, res, next) {\n        req.count++;\n        next();\n      });\n\n      route.all(function(req, res, next) {\n        req.count++;\n        next();\n      });\n\n      route.dispatch(req, {}, function (err) {\n        if (err) return done(err);\n        assert.strictEqual(req.count, 2)\n        done();\n      });\n    })\n  })\n\n  describe('.VERB', function(){\n    it('should support .get', function(done){\n      var req = { method: 'GET', url: '/' };\n      var route = new Route('');\n\n      route.get(function(req, res, next) {\n        req.called = true;\n        next();\n      })\n\n      route.dispatch(req, {}, function (err) {\n        if (err) return done(err);\n        assert.ok(req.called)\n        done();\n      });\n    })\n\n    it('should limit to just .VERB', function(done){\n      var req = { method: 'POST', url: '/' };\n      var route = new Route('');\n\n      route.get(function () {\n        throw new Error('not me!');\n      })\n\n      route.post(function(req, res, next) {\n        req.called = true;\n        next();\n      })\n\n      route.dispatch(req, {}, function (err) {\n        if (err) return done(err);\n        assert.ok(req.called)\n        done();\n      });\n    })\n\n    it('should allow fallthrough', function(done){\n      var req = { order: '', method: 'GET', url: '/' };\n      var route = new Route('');\n\n      route.get(function(req, res, next) {\n        req.order += 'a';\n        next();\n      })\n\n      route.all(function(req, res, next) {\n        req.order += 'b';\n        next();\n      });\n\n      route.get(function(req, res, next) {\n        req.order += 'c';\n        next();\n      })\n\n      route.dispatch(req, {}, function (err) {\n        if (err) return done(err);\n        assert.strictEqual(req.order, 'abc')\n        done();\n      });\n    })\n  })\n\n  describe('errors', function(){\n    it('should handle errors via arity 4 functions', function(done){\n      var req = { order: '', method: 'GET', url: '/' };\n      var route = new Route('');\n\n      route.all(function(req, res, next){\n        next(new Error('foobar'));\n      });\n\n      route.all(function(req, res, next){\n        req.order += '0';\n        next();\n      });\n\n      route.all(function(err, req, res, next){\n        req.order += 'a';\n        next(err);\n      });\n\n      route.dispatch(req, {}, function (err) {\n        assert.ok(err)\n        assert.strictEqual(err.message, 'foobar')\n        assert.strictEqual(req.order, 'a')\n        done();\n      });\n    })\n\n    it('should handle throw', function(done) {\n      var req = { order: '', method: 'GET', url: '/' };\n      var route = new Route('');\n\n      route.all(function () {\n        throw new Error('foobar');\n      });\n\n      route.all(function(req, res, next){\n        req.order += '0';\n        next();\n      });\n\n      route.all(function(err, req, res, next){\n        req.order += 'a';\n        next(err);\n      });\n\n      route.dispatch(req, {}, function (err) {\n        assert.ok(err)\n        assert.strictEqual(err.message, 'foobar')\n        assert.strictEqual(req.order, 'a')\n        done();\n      });\n    });\n\n    it('should handle throwing inside error handlers', function(done) {\n      var req = { method: 'GET', url: '/' };\n      var route = new Route('');\n\n      route.get(function () {\n        throw new Error('boom!');\n      });\n\n      route.get(function(err, req, res, next){\n        throw new Error('oops');\n      });\n\n      route.get(function(err, req, res, next){\n        req.message = err.message;\n        next();\n      });\n\n      route.dispatch(req, {}, function (err) {\n        if (err) return done(err);\n        assert.strictEqual(req.message, 'oops')\n        done();\n      });\n    });\n\n    it('should handle throw in .all', function(done) {\n      var req = { method: 'GET', url: '/' };\n      var route = new Route('');\n\n      route.all(function(req, res, next){\n        throw new Error('boom!');\n      });\n\n      route.dispatch(req, {}, function(err){\n        assert.ok(err)\n        assert.strictEqual(err.message, 'boom!')\n        done();\n      });\n    });\n\n    it('should handle single error handler', function(done) {\n      var req = { method: 'GET', url: '/' };\n      var route = new Route('');\n\n      route.all(function(err, req, res, next){\n        // this should not execute\n        throw new Error('should not be called')\n      });\n\n      route.dispatch(req, {}, done);\n    });\n  })\n})\n"
  },
  {
    "path": "test/Router.js",
    "content": "'use strict'\n\nvar after = require('after');\nvar express = require('../')\n  , Router = express.Router\n  , methods = require('../lib/utils').methods\n  , assert = require('node:assert');\n\ndescribe('Router', function () {\n  it('should return a function with router methods', function () {\n    var router = new Router();\n    assert(typeof router === 'function')\n\n    assert(typeof router.get === 'function')\n    assert(typeof router.handle === 'function')\n    assert(typeof router.use === 'function')\n  });\n\n  it('should support .use of other routers', function (done) {\n    var router = new Router();\n    var another = new Router();\n\n    another.get('/bar', function (req, res) {\n      res.end();\n    });\n    router.use('/foo', another);\n\n    router.handle({ url: '/foo/bar', method: 'GET' }, { end: done }, function () { });\n  });\n\n  it('should support dynamic routes', function (done) {\n    var router = new Router();\n    var another = new Router();\n\n    another.get('/:bar', function (req, res) {\n      assert.strictEqual(req.params.bar, 'route')\n      res.end();\n    });\n    router.use('/:foo', another);\n\n    router.handle({ url: '/test/route', method: 'GET' }, { end: done }, function () { });\n  });\n\n  it('should handle blank URL', function (done) {\n    var router = new Router();\n\n    router.use(function (req, res) {\n      throw new Error('should not be called')\n    });\n\n    router.handle({ url: '', method: 'GET' }, {}, done);\n  });\n\n  it('should handle missing URL', function (done) {\n    var router = new Router()\n\n    router.use(function (req, res) {\n      throw new Error('should not be called')\n    })\n\n    router.handle({ method: 'GET' }, {}, done)\n  })\n\n  it('handle missing method', function (done) {\n    var all = false\n    var router = new Router()\n    var route = router.route('/foo')\n    var use = false\n\n    route.post(function (req, res, next) { next(new Error('should not run')) })\n    route.all(function (req, res, next) {\n      all = true\n      next()\n    })\n    route.get(function (req, res, next) { next(new Error('should not run')) })\n\n    router.get('/foo', function (req, res, next) { next(new Error('should not run')) })\n    router.use(function (req, res, next) {\n      use = true\n      next()\n    })\n\n    router.handle({ url: '/foo' }, {}, function (err) {\n      if (err) return done(err)\n      assert.ok(all)\n      assert.ok(use)\n      done()\n    })\n  })\n\n  it('should not stack overflow with many registered routes', function (done) {\n    this.timeout(5000) // long-running test\n\n    var handler = function (req, res) { res.end(new Error('wrong handler')) };\n    var router = new Router();\n\n    for (var i = 0; i < 6000; i++) {\n      router.get('/thing' + i, handler)\n    }\n\n    router.get('/', function (req, res) {\n      res.end();\n    });\n\n    router.handle({ url: '/', method: 'GET' }, { end: done }, function () { });\n  });\n\n  it('should not stack overflow with a large sync route stack', function (done) {\n    this.timeout(5000) // long-running test\n\n    var router = new Router()\n\n    router.get('/foo', function (req, res, next) {\n      req.counter = 0\n      next()\n    })\n\n    for (var i = 0; i < 6000; i++) {\n      router.get('/foo', function (req, res, next) {\n        req.counter++\n        next()\n      })\n    }\n\n    router.get('/foo', function (req, res) {\n      assert.strictEqual(req.counter, 6000)\n      res.end()\n    })\n\n    router.handle({ url: '/foo', method: 'GET' }, { end: done }, function (err) {\n      assert(!err, err);\n    });\n  })\n\n  it('should not stack overflow with a large sync middleware stack', function (done) {\n    this.timeout(5000) // long-running test\n\n    var router = new Router()\n\n    router.use(function (req, res, next) {\n      req.counter = 0\n      next()\n    })\n\n    for (var i = 0; i < 6000; i++) {\n      router.use(function (req, res, next) {\n        req.counter++\n        next()\n      })\n    }\n\n    router.use(function (req, res) {\n      assert.strictEqual(req.counter, 6000)\n      res.end()\n    })\n\n    router.handle({ url: '/', method: 'GET' }, { end: done }, function (err) {\n      assert(!err, err);\n    })\n  })\n\n  describe('.handle', function () {\n    it('should dispatch', function (done) {\n      var router = new Router();\n\n      router.route('/foo').get(function (req, res) {\n        res.send('foo');\n      });\n\n      var res = {\n        send: function (val) {\n          assert.strictEqual(val, 'foo')\n          done();\n        }\n      }\n      router.handle({ url: '/foo', method: 'GET' }, res, function () { });\n    })\n  })\n\n  describe('.multiple callbacks', function () {\n    it('should throw if a callback is null', function () {\n      assert.throws(function () {\n        var router = new Router();\n        router.route('/foo').all(null);\n      })\n    })\n\n    it('should throw if a callback is undefined', function () {\n      assert.throws(function () {\n        var router = new Router();\n        router.route('/foo').all(undefined);\n      })\n    })\n\n    it('should throw if a callback is not a function', function () {\n      assert.throws(function () {\n        var router = new Router();\n        router.route('/foo').all('not a function');\n      })\n    })\n\n    it('should not throw if all callbacks are functions', function () {\n      var router = new Router();\n      router.route('/foo').all(function () { }).all(function () { });\n    })\n  })\n\n  describe('error', function () {\n    it('should skip non error middleware', function (done) {\n      var router = new Router();\n\n      router.get('/foo', function (req, res, next) {\n        next(new Error('foo'));\n      });\n\n      router.get('/bar', function (req, res, next) {\n        next(new Error('bar'));\n      });\n\n      router.use(function (req, res, next) {\n        assert(false);\n      });\n\n      router.use(function (err, req, res, next) {\n        assert.equal(err.message, 'foo');\n        done();\n      });\n\n      router.handle({ url: '/foo', method: 'GET' }, {}, done);\n    });\n\n    it('should handle throwing inside routes with params', function (done) {\n      var router = new Router();\n\n      router.get('/foo/:id', function () {\n        throw new Error('foo');\n      });\n\n      router.use(function (req, res, next) {\n        assert(false);\n      });\n\n      router.use(function (err, req, res, next) {\n        assert.equal(err.message, 'foo');\n        done();\n      });\n\n      router.handle({ url: '/foo/2', method: 'GET' }, {}, function () { });\n    });\n\n    it('should handle throwing in handler after async param', function (done) {\n      var router = new Router();\n\n      router.param('user', function (req, res, next, val) {\n        process.nextTick(function () {\n          req.user = val;\n          next();\n        });\n      });\n\n      router.use('/:user', function (req, res, next) {\n        throw new Error('oh no!');\n      });\n\n      router.use(function (err, req, res, next) {\n        assert.equal(err.message, 'oh no!');\n        done();\n      });\n\n      router.handle({ url: '/bob', method: 'GET' }, {}, function () { });\n    });\n\n    it('should handle throwing inside error handlers', function (done) {\n      var router = new Router();\n\n      router.use(function (req, res, next) {\n        throw new Error('boom!');\n      });\n\n      router.use(function (err, req, res, next) {\n        throw new Error('oops');\n      });\n\n      router.use(function (err, req, res, next) {\n        assert.equal(err.message, 'oops');\n        done();\n      });\n\n      router.handle({ url: '/', method: 'GET' }, {}, done);\n    });\n  })\n\n  describe('FQDN', function () {\n    it('should not obscure FQDNs', function (done) {\n      var request = { hit: 0, url: 'http://example.com/foo', method: 'GET' };\n      var router = new Router();\n\n      router.use(function (req, res, next) {\n        assert.equal(req.hit++, 0);\n        assert.equal(req.url, 'http://example.com/foo');\n        next();\n      });\n\n      router.handle(request, {}, function (err) {\n        if (err) return done(err);\n        assert.equal(request.hit, 1);\n        done();\n      });\n    });\n\n    it('should ignore FQDN in search', function (done) {\n      var request = { hit: 0, url: '/proxy?url=http://example.com/blog/post/1', method: 'GET' };\n      var router = new Router();\n\n      router.use('/proxy', function (req, res, next) {\n        assert.equal(req.hit++, 0);\n        assert.equal(req.url, '/?url=http://example.com/blog/post/1');\n        next();\n      });\n\n      router.handle(request, {}, function (err) {\n        if (err) return done(err);\n        assert.equal(request.hit, 1);\n        done();\n      });\n    });\n\n    it('should ignore FQDN in path', function (done) {\n      var request = { hit: 0, url: '/proxy/http://example.com/blog/post/1', method: 'GET' };\n      var router = new Router();\n\n      router.use('/proxy', function (req, res, next) {\n        assert.equal(req.hit++, 0);\n        assert.equal(req.url, '/http://example.com/blog/post/1');\n        next();\n      });\n\n      router.handle(request, {}, function (err) {\n        if (err) return done(err);\n        assert.equal(request.hit, 1);\n        done();\n      });\n    });\n\n    it('should adjust FQDN req.url', function (done) {\n      var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };\n      var router = new Router();\n\n      router.use('/blog', function (req, res, next) {\n        assert.equal(req.hit++, 0);\n        assert.equal(req.url, 'http://example.com/post/1');\n        next();\n      });\n\n      router.handle(request, {}, function (err) {\n        if (err) return done(err);\n        assert.equal(request.hit, 1);\n        done();\n      });\n    });\n\n    it('should adjust FQDN req.url with multiple handlers', function (done) {\n      var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };\n      var router = new Router();\n\n      router.use(function (req, res, next) {\n        assert.equal(req.hit++, 0);\n        assert.equal(req.url, 'http://example.com/blog/post/1');\n        next();\n      });\n\n      router.use('/blog', function (req, res, next) {\n        assert.equal(req.hit++, 1);\n        assert.equal(req.url, 'http://example.com/post/1');\n        next();\n      });\n\n      router.handle(request, {}, function (err) {\n        if (err) return done(err);\n        assert.equal(request.hit, 2);\n        done();\n      });\n    });\n\n    it('should adjust FQDN req.url with multiple routed handlers', function (done) {\n      var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };\n      var router = new Router();\n\n      router.use('/blog', function (req, res, next) {\n        assert.equal(req.hit++, 0);\n        assert.equal(req.url, 'http://example.com/post/1');\n        next();\n      });\n\n      router.use('/blog', function (req, res, next) {\n        assert.equal(req.hit++, 1);\n        assert.equal(req.url, 'http://example.com/post/1');\n        next();\n      });\n\n      router.use(function (req, res, next) {\n        assert.equal(req.hit++, 2);\n        assert.equal(req.url, 'http://example.com/blog/post/1');\n        next();\n      });\n\n      router.handle(request, {}, function (err) {\n        if (err) return done(err);\n        assert.equal(request.hit, 3);\n        done();\n      });\n    });\n  })\n\n  describe('.all', function () {\n    it('should support using .all to capture all http verbs', function (done) {\n      var router = new Router();\n\n      var count = 0;\n      router.all('/foo', function () { count++; });\n\n      var url = '/foo?bar=baz';\n\n      methods.forEach(function testMethod(method) {\n        router.handle({ url: url, method: method }, {}, function () { });\n      });\n\n      assert.equal(count, methods.length);\n      done();\n    })\n  })\n\n  describe('.use', function () {\n    it('should require middleware', function () {\n      var router = new Router()\n      assert.throws(function () { router.use('/') }, /argument handler is required/)\n    })\n\n    it('should reject string as middleware', function () {\n      var router = new Router()\n      assert.throws(function () { router.use('/', 'foo') }, /argument handler must be a function/)\n    })\n\n    it('should reject number as middleware', function () {\n      var router = new Router()\n      assert.throws(function () { router.use('/', 42) }, /argument handler must be a function/)\n    })\n\n    it('should reject null as middleware', function () {\n      var router = new Router()\n      assert.throws(function () { router.use('/', null) }, /argument handler must be a function/)\n    })\n\n    it('should reject Date as middleware', function () {\n      var router = new Router()\n      assert.throws(function () { router.use('/', new Date()) }, /argument handler must be a function/)\n    })\n\n    it('should be called for any URL', function (done) {\n      var cb = after(4, done)\n      var router = new Router()\n\n      function no() {\n        throw new Error('should not be called')\n      }\n\n      router.use(function (req, res) {\n        res.end()\n      })\n\n      router.handle({ url: '/', method: 'GET' }, { end: cb }, no)\n      router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no)\n      router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no)\n      router.handle({ url: '*', method: 'GET' }, { end: cb }, no)\n    })\n\n    it('should accept array of middleware', function (done) {\n      var count = 0;\n      var router = new Router();\n\n      function fn1(req, res, next) {\n        assert.equal(++count, 1);\n        next();\n      }\n\n      function fn2(req, res, next) {\n        assert.equal(++count, 2);\n        next();\n      }\n\n      router.use([fn1, fn2], function (req, res) {\n        assert.equal(++count, 3);\n        done();\n      });\n\n      router.handle({ url: '/foo', method: 'GET' }, {}, function () { });\n    })\n  })\n\n  describe('.param', function () {\n    it('should require function', function () {\n      var router = new Router();\n      assert.throws(router.param.bind(router, 'id'), /argument fn is required/);\n    });\n\n    it('should reject non-function', function () {\n      var router = new Router();\n      assert.throws(router.param.bind(router, 'id', 42), /argument fn must be a function/);\n    });\n\n    it('should call param function when routing VERBS', function (done) {\n      var router = new Router();\n\n      router.param('id', function (req, res, next, id) {\n        assert.equal(id, '123');\n        next();\n      });\n\n      router.get('/foo/:id/bar', function (req, res, next) {\n        assert.equal(req.params.id, '123');\n        next();\n      });\n\n      router.handle({ url: '/foo/123/bar', method: 'get' }, {}, done);\n    });\n\n    it('should call param function when routing middleware', function (done) {\n      var router = new Router();\n\n      router.param('id', function (req, res, next, id) {\n        assert.equal(id, '123');\n        next();\n      });\n\n      router.use('/foo/:id/bar', function (req, res, next) {\n        assert.equal(req.params.id, '123');\n        assert.equal(req.url, '/baz');\n        next();\n      });\n\n      router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done);\n    });\n\n    it('should only call once per request', function (done) {\n      var count = 0;\n      var req = { url: '/foo/bob/bar', method: 'get' };\n      var router = new Router();\n      var sub = new Router();\n\n      sub.get('/bar', function (req, res, next) {\n        next();\n      });\n\n      router.param('user', function (req, res, next, user) {\n        count++;\n        req.user = user;\n        next();\n      });\n\n      router.use('/foo/:user/', new Router());\n      router.use('/foo/:user/', sub);\n\n      router.handle(req, {}, function (err) {\n        if (err) return done(err);\n        assert.equal(count, 1);\n        assert.equal(req.user, 'bob');\n        done();\n      });\n    });\n\n    it('should call when values differ', function (done) {\n      var count = 0;\n      var req = { url: '/foo/bob/bar', method: 'get' };\n      var router = new Router();\n      var sub = new Router();\n\n      sub.get('/bar', function (req, res, next) {\n        next();\n      });\n\n      router.param('user', function (req, res, next, user) {\n        count++;\n        req.user = user;\n        next();\n      });\n\n      router.use('/foo/:user/', new Router());\n      router.use('/:user/bob/', sub);\n\n      router.handle(req, {}, function (err) {\n        if (err) return done(err);\n        assert.equal(count, 2);\n        assert.equal(req.user, 'foo');\n        done();\n      });\n    });\n  });\n\n  describe('parallel requests', function () {\n    it('should not mix requests', function (done) {\n      var req1 = { url: '/foo/50/bar', method: 'get' };\n      var req2 = { url: '/foo/10/bar', method: 'get' };\n      var router = new Router();\n      var sub = new Router();\n      var cb = after(2, done)\n\n\n      sub.get('/bar', function (req, res, next) {\n        next();\n      });\n\n      router.param('ms', function (req, res, next, ms) {\n        ms = parseInt(ms, 10);\n        req.ms = ms;\n        setTimeout(next, ms);\n      });\n\n      router.use('/foo/:ms/', new Router());\n      router.use('/foo/:ms/', sub);\n\n      router.handle(req1, {}, function (err) {\n        assert.ifError(err);\n        assert.equal(req1.ms, 50);\n        assert.equal(req1.originalUrl, '/foo/50/bar');\n        cb()\n      });\n\n      router.handle(req2, {}, function (err) {\n        assert.ifError(err);\n        assert.equal(req2.ms, 10);\n        assert.equal(req2.originalUrl, '/foo/10/bar');\n        cb()\n      });\n    });\n  });\n})\n"
  },
  {
    "path": "test/acceptance/auth.js",
    "content": "var app = require('../../examples/auth')\nvar request = require('supertest')\n\nfunction getCookie(res) {\n  return res.headers['set-cookie'][0].split(';')[0];\n}\n\ndescribe('auth', function(){\n  describe('GET /',function(){\n    it('should redirect to /login', function(done){\n      request(app)\n      .get('/')\n      .expect('Location', '/login')\n      .expect(302, done)\n    })\n  })\n\n  describe('GET /login',function(){\n    it('should render login form', function(done){\n      request(app)\n      .get('/login')\n      .expect(200, /<form/, done)\n    })\n\n    it('should display login error for bad user', function (done) {\n      request(app)\n      .post('/login')\n      .type('urlencoded')\n      .send('username=not-tj&password=foobar')\n      .expect('Location', '/login')\n      .expect(302, function(err, res){\n        if (err) return done(err)\n        request(app)\n        .get('/login')\n        .set('Cookie', getCookie(res))\n        .expect(200, /Authentication failed/, done)\n      })\n    })\n\n    it('should display login error for bad password', function (done) {\n      request(app)\n        .post('/login')\n        .type('urlencoded')\n        .send('username=tj&password=nogood')\n        .expect('Location', '/login')\n        .expect(302, function (err, res) {\n          if (err) return done(err)\n          request(app)\n            .get('/login')\n            .set('Cookie', getCookie(res))\n            .expect(200, /Authentication failed/, done)\n        })\n    })\n  })\n\n  describe('GET /logout',function(){\n    it('should redirect to /', function(done){\n      request(app)\n      .get('/logout')\n      .expect('Location', '/')\n      .expect(302, done)\n    })\n  })\n\n  describe('GET /restricted',function(){\n    it('should redirect to /login without cookie', function(done){\n      request(app)\n      .get('/restricted')\n      .expect('Location', '/login')\n      .expect(302, done)\n    })\n\n    it('should succeed with proper cookie', function(done){\n      request(app)\n      .post('/login')\n      .type('urlencoded')\n      .send('username=tj&password=foobar')\n      .expect('Location', '/')\n      .expect(302, function(err, res){\n        if (err) return done(err)\n        request(app)\n        .get('/restricted')\n        .set('Cookie', getCookie(res))\n        .expect(200, done)\n      })\n    })\n  })\n\n  describe('POST /login', function(){\n    it('should fail without proper username', function(done){\n      request(app)\n      .post('/login')\n      .type('urlencoded')\n      .send('username=not-tj&password=foobar')\n      .expect('Location', '/login')\n      .expect(302, done)\n    })\n\n    it('should fail without proper password', function(done){\n      request(app)\n      .post('/login')\n      .type('urlencoded')\n      .send('username=tj&password=baz')\n      .expect('Location', '/login')\n      .expect(302, done)\n    })\n\n    it('should succeed with proper credentials', function(done){\n      request(app)\n      .post('/login')\n      .type('urlencoded')\n      .send('username=tj&password=foobar')\n      .expect('Location', '/')\n      .expect(302, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/content-negotiation.js",
    "content": "\nvar request = require('supertest')\n  , app = require('../../examples/content-negotiation');\n\ndescribe('content-negotiation', function(){\n  describe('GET /', function(){\n    it('should default to text/html', function(done){\n      request(app)\n      .get('/')\n      .expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)\n    })\n\n    it('should accept to text/plain', function(done){\n      request(app)\n      .get('/')\n      .set('Accept', 'text/plain')\n      .expect(200, ' - Tobi\\n - Loki\\n - Jane\\n', done)\n    })\n\n    it('should accept to application/json', function(done){\n      request(app)\n      .get('/')\n      .set('Accept', 'application/json')\n      .expect(200, '[{\"name\":\"Tobi\"},{\"name\":\"Loki\"},{\"name\":\"Jane\"}]', done)\n    })\n  })\n\n  describe('GET /users', function(){\n    it('should default to text/html', function(done){\n      request(app)\n      .get('/users')\n      .expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)\n    })\n\n    it('should accept to text/plain', function(done){\n      request(app)\n      .get('/users')\n      .set('Accept', 'text/plain')\n      .expect(200, ' - Tobi\\n - Loki\\n - Jane\\n', done)\n    })\n\n    it('should accept to application/json', function(done){\n      request(app)\n      .get('/users')\n      .set('Accept', 'application/json')\n      .expect(200, '[{\"name\":\"Tobi\"},{\"name\":\"Loki\"},{\"name\":\"Jane\"}]', done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/cookie-sessions.js",
    "content": "\nvar app = require('../../examples/cookie-sessions')\nvar request = require('supertest')\n\ndescribe('cookie-sessions', function () {\n  describe('GET /', function () {\n    it('should display no views', function (done) {\n      request(app)\n      .get('/')\n      .expect(200, 'viewed 1 times\\n', done)\n    })\n\n    it('should set a session cookie', function (done) {\n      request(app)\n      .get('/')\n      .expect('Set-Cookie', /session=/)\n      .expect(200, done)\n    })\n\n    it('should display 1 view on revisit', function (done) {\n      request(app)\n      .get('/')\n      .expect(200, 'viewed 1 times\\n', function (err, res) {\n        if (err) return done(err)\n        request(app)\n        .get('/')\n        .set('Cookie', getCookies(res))\n        .expect(200, 'viewed 2 times\\n', done)\n      })\n    })\n  })\n})\n\nfunction getCookies(res) {\n  return res.headers['set-cookie'].map(function (val) {\n    return val.split(';')[0]\n  }).join('; ');\n}\n"
  },
  {
    "path": "test/acceptance/cookies.js",
    "content": "\nvar app = require('../../examples/cookies')\n  , request = require('supertest');\nvar utils = require('../support/utils');\n\ndescribe('cookies', function(){\n  describe('GET /', function(){\n    it('should have a form', function(done){\n      request(app)\n      .get('/')\n      .expect(/<form/, done);\n    })\n\n    it('should respond with no cookies', function(done){\n      request(app)\n      .get('/')\n      .expect(utils.shouldNotHaveHeader('Set-Cookie'))\n      .expect(200, done)\n    })\n\n    it('should respond to cookie', function(done){\n      request(app)\n      .post('/')\n      .type('urlencoded')\n      .send({ remember: 1 })\n      .expect(302, function(err, res){\n        if (err) return done(err)\n        request(app)\n        .get('/')\n        .set('Cookie', res.headers['set-cookie'][0])\n        .expect(200, /Remembered/, done)\n      })\n    })\n  })\n\n  describe('GET /forget', function(){\n    it('should clear cookie', function(done){\n      request(app)\n      .post('/')\n      .type('urlencoded')\n      .send({ remember: 1 })\n      .expect(302, function(err, res){\n        if (err) return done(err)\n        request(app)\n        .get('/forget')\n        .set('Cookie', res.headers['set-cookie'][0])\n        .expect('Set-Cookie', /remember=;/)\n        .expect(302, done)\n      })\n    })\n  })\n\n  describe('POST /', function(){\n    it('should set a cookie', function(done){\n      request(app)\n      .post('/')\n      .type('urlencoded')\n      .send({ remember: 1 })\n      .expect('Set-Cookie', /remember=1/)\n      .expect(302, done)\n    })\n\n    it('should no set cookie w/o reminder', function(done){\n      request(app)\n      .post('/')\n      .send({})\n      .expect(utils.shouldNotHaveHeader('Set-Cookie'))\n      .expect(302, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/downloads.js",
    "content": "\nvar app = require('../../examples/downloads')\n  , request = require('supertest');\n\ndescribe('downloads', function(){\n  describe('GET /', function(){\n    it('should have a link to amazing.txt', function(done){\n      request(app)\n      .get('/')\n      .expect(/href=\"\\/files\\/amazing.txt\"/, done)\n    })\n  })\n\n  describe('GET /files/notes/groceries.txt', function () {\n    it('should have a download header', function (done) {\n      request(app)\n        .get('/files/notes/groceries.txt')\n        .expect('Content-Disposition', 'attachment; filename=\"groceries.txt\"')\n        .expect(200, done)\n    })\n  })\n\n  describe('GET /files/amazing.txt', function(){\n    it('should have a download header', function(done){\n      request(app)\n      .get('/files/amazing.txt')\n      .expect('Content-Disposition', 'attachment; filename=\"amazing.txt\"')\n      .expect(200, done)\n    })\n  })\n\n  describe('GET /files/missing.txt', function(){\n    it('should respond with 404', function(done){\n      request(app)\n      .get('/files/missing.txt')\n      .expect(404, done)\n    })\n  })\n\n  describe('GET /files/../index.js', function () {\n    it('should respond with 403', function (done) {\n      request(app)\n        .get('/files/../index.js')\n        .expect(403, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/ejs.js",
    "content": "\nvar request = require('supertest')\n  , app = require('../../examples/ejs');\n\ndescribe('ejs', function(){\n  describe('GET /', function(){\n    it('should respond with html', function(done){\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/html; charset=utf-8')\n      .expect(/<li>tobi &lt;tobi@learnboost\\.com&gt;<\\/li>/)\n      .expect(/<li>loki &lt;loki@learnboost\\.com&gt;<\\/li>/)\n      .expect(/<li>jane &lt;jane@learnboost\\.com&gt;<\\/li>/)\n      .expect(200, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/error-pages.js",
    "content": "\nvar app = require('../../examples/error-pages')\n  , request = require('supertest');\n\ndescribe('error-pages', function(){\n  describe('GET /', function(){\n    it('should respond with page list', function(done){\n      request(app)\n      .get('/')\n      .expect(/Pages Example/, done)\n    })\n  })\n\n  describe('Accept: text/html',function(){\n    describe('GET /403', function(){\n      it('should respond with 403', function(done){\n        request(app)\n        .get('/403')\n        .expect(403, done)\n      })\n    })\n\n    describe('GET /404', function(){\n      it('should respond with 404', function(done){\n        request(app)\n        .get('/404')\n        .expect(404, done)\n      })\n    })\n\n    describe('GET /500', function(){\n      it('should respond with 500', function(done){\n        request(app)\n        .get('/500')\n        .expect(500, done)\n      })\n    })\n  })\n\n  describe('Accept: application/json',function(){\n    describe('GET /403', function(){\n      it('should respond with 403', function(done){\n        request(app)\n        .get('/403')\n        .set('Accept','application/json')\n        .expect(403, done)\n      })\n    })\n\n    describe('GET /404', function(){\n      it('should respond with 404', function(done){\n        request(app)\n        .get('/404')\n        .set('Accept','application/json')\n        .expect(404, { error: 'Not found' }, done)\n      })\n    })\n\n    describe('GET /500', function(){\n      it('should respond with 500', function(done){\n        request(app)\n        .get('/500')\n        .set('Accept', 'application/json')\n        .expect(500, done)\n      })\n    })\n  })\n\n\n  describe('Accept: text/plain',function(){\n    describe('GET /403', function(){\n      it('should respond with 403', function(done){\n        request(app)\n        .get('/403')\n        .set('Accept','text/plain')\n        .expect(403, done)\n      })\n    })\n\n    describe('GET /404', function(){\n      it('should respond with 404', function(done){\n        request(app)\n        .get('/404')\n        .set('Accept', 'text/plain')\n        .expect(404)\n        .expect('Not found', done);\n      })\n    })\n\n    describe('GET /500', function(){\n      it('should respond with 500', function(done){\n        request(app)\n        .get('/500')\n        .set('Accept','text/plain')\n        .expect(500, done)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/error.js",
    "content": "\nvar app = require('../../examples/error')\n  , request = require('supertest');\n\ndescribe('error', function(){\n  describe('GET /', function(){\n    it('should respond with 500', function(done){\n      request(app)\n        .get('/')\n        .expect(500,done)\n    })\n  })\n\n  describe('GET /next', function(){\n    it('should respond with 500', function(done){\n      request(app)\n        .get('/next')\n        .expect(500,done)\n    })\n  })\n\n  describe('GET /missing', function(){\n    it('should respond with 404', function(done){\n      request(app)\n        .get('/missing')\n        .expect(404,done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/hello-world.js",
    "content": "\nvar app = require('../../examples/hello-world')\nvar request = require('supertest')\n\ndescribe('hello-world', function () {\n  describe('GET /', function () {\n    it('should respond with hello world', function (done) {\n      request(app)\n        .get('/')\n        .expect(200, 'Hello World', done)\n    })\n  })\n\n  describe('GET /missing', function () {\n    it('should respond with 404', function (done) {\n      request(app)\n        .get('/missing')\n        .expect(404, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/markdown.js",
    "content": "\nvar app = require('../../examples/markdown')\nvar request = require('supertest')\n\ndescribe('markdown', function(){\n  describe('GET /', function(){\n    it('should respond with html', function(done){\n      request(app)\n        .get('/')\n        .expect(/<h1[^>]*>Markdown Example<\\/h1>/,done)\n    })\n  })\n\n  describe('GET /fail',function(){\n    it('should respond with an error', function(done){\n      request(app)\n        .get('/fail')\n        .expect(500,done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/multi-router.js",
    "content": "var app = require('../../examples/multi-router')\nvar request = require('supertest')\n\ndescribe('multi-router', function(){\n  describe('GET /',function(){\n    it('should respond with root handler', function(done){\n      request(app)\n      .get('/')\n      .expect(200, 'Hello from root route.', done)\n    })\n  })\n\n  describe('GET /api/v1/',function(){\n    it('should respond with APIv1 root handler', function(done){\n      request(app)\n      .get('/api/v1/')\n      .expect(200, 'Hello from APIv1 root route.', done)\n    })\n  })\n\n  describe('GET /api/v1/users',function(){\n    it('should respond with users from APIv1', function(done){\n      request(app)\n      .get('/api/v1/users')\n      .expect(200, 'List of APIv1 users.', done)\n    })\n  })\n\n  describe('GET /api/v2/',function(){\n    it('should respond with APIv2 root handler', function(done){\n      request(app)\n      .get('/api/v2/')\n      .expect(200, 'Hello from APIv2 root route.', done)\n    })\n  })\n\n  describe('GET /api/v2/users',function(){\n    it('should respond with users from APIv2', function(done){\n      request(app)\n      .get('/api/v2/users')\n      .expect(200, 'List of APIv2 users.', done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/mvc.js",
    "content": "\nvar request = require('supertest')\n  , app = require('../../examples/mvc');\n\ndescribe('mvc', function(){\n  describe('GET /', function(){\n    it('should redirect to /users', function(done){\n      request(app)\n      .get('/')\n      .expect('Location', '/users')\n      .expect(302, done)\n    })\n  })\n\n  describe('GET /pet/0', function(){\n    it('should get pet', function(done){\n      request(app)\n      .get('/pet/0')\n      .expect(200, /Tobi/, done)\n    })\n  })\n\n  describe('GET /pet/0/edit', function(){\n    it('should get pet edit page', function(done){\n      request(app)\n      .get('/pet/0/edit')\n      .expect(/<form/)\n      .expect(200, /Tobi/, done)\n    })\n  })\n\n  describe('PUT /pet/2', function(){\n    it('should update the pet', function(done){\n      request(app)\n      .put('/pet/3')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send({ pet: { name: 'Boots' } })\n      .expect(302, function (err, res) {\n        if (err) return done(err);\n        request(app)\n        .get('/pet/3/edit')\n        .expect(200, /Boots/, done)\n      })\n    })\n  })\n\n  describe('GET /users', function(){\n    it('should display a list of users', function(done){\n      request(app)\n      .get('/users')\n      .expect(/<h1>Users<\\/h1>/)\n      .expect(/>TJ</)\n      .expect(/>Guillermo</)\n      .expect(/>Nathan</)\n      .expect(200, done)\n    })\n  })\n\n  describe('GET /user/:id', function(){\n    describe('when present', function(){\n      it('should display the user', function(done){\n        request(app)\n        .get('/user/0')\n        .expect(200, /<h1>TJ <a href=\"\\/user\\/0\\/edit\">edit/, done)\n      })\n\n      it('should display the users pets', function(done){\n        request(app)\n        .get('/user/0')\n        .expect(/\\/pet\\/0\">Tobi/)\n        .expect(/\\/pet\\/1\">Loki/)\n        .expect(/\\/pet\\/2\">Jane/)\n        .expect(200, done)\n      })\n    })\n\n    describe('when not present', function(){\n      it('should 404', function(done){\n        request(app)\n        .get('/user/123')\n        .expect(404, done);\n      })\n    })\n  })\n\n  describe('GET /user/:id/edit', function(){\n    it('should display the edit form', function(done){\n      request(app)\n      .get('/user/1/edit')\n      .expect(/Guillermo/)\n      .expect(200, /<form/, done)\n    })\n  })\n\n  describe('PUT /user/:id', function(){\n    it('should 500 on error', function(done){\n      request(app)\n      .put('/user/1')\n      .send({})\n      .expect(500, done)\n    })\n\n    it('should update the user', function(done){\n      request(app)\n      .put('/user/1')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send({ user: { name: 'Tobo' }})\n      .expect(302, function (err, res) {\n        if (err) return done(err);\n        request(app)\n        .get('/user/1/edit')\n        .expect(200, /Tobo/, done)\n      })\n    })\n  })\n\n  describe('POST /user/:id/pet', function(){\n    it('should create a pet for user', function(done){\n      request(app)\n      .post('/user/2/pet')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send({ pet: { name: 'Snickers' }})\n      .expect('Location', '/user/2')\n      .expect(302, function(err, res){\n        if (err) return done(err)\n        request(app)\n        .get('/user/2')\n        .expect(200, /Snickers/, done)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/params.js",
    "content": "var app = require('../../examples/params')\nvar request = require('supertest')\n\ndescribe('params', function(){\n  describe('GET /', function(){\n    it('should respond with instructions', function(done){\n      request(app)\n        .get('/')\n        .expect(/Visit/,done)\n    })\n  })\n\n  describe('GET /user/0', function(){\n    it('should respond with a user', function(done){\n      request(app)\n        .get('/user/0')\n        .expect(/user tj/,done)\n    })\n  })\n\n  describe('GET /user/9', function(){\n    it('should fail to find user', function(done){\n      request(app)\n      .get('/user/9')\n      .expect(404, /failed to find user/, done)\n    })\n  })\n\n  describe('GET /users/0-2', function(){\n    it('should respond with three users', function(done){\n      request(app)\n      .get('/users/0-2')\n      .expect(/users tj, tobi, loki/, done)\n    })\n  })\n\n  describe('GET /users/foo-bar', function(){\n    it('should fail integer parsing', function(done){\n      request(app)\n      .get('/users/foo-bar')\n      .expect(400, /failed to parseInt foo/, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/resource.js",
    "content": "var app = require('../../examples/resource')\nvar request = require('supertest')\n\ndescribe('resource', function(){\n  describe('GET /', function(){\n    it('should respond with instructions', function(done){\n      request(app)\n        .get('/')\n        .expect(/^<h1>Examples:<\\/h1>/,done)\n    })\n  })\n\n  describe('GET /users', function(){\n    it('should respond with all users', function(done){\n      request(app)\n        .get('/users')\n        .expect(/^\\[{\"name\":\"tj\"},{\"name\":\"ciaran\"},{\"name\":\"aaron\"},{\"name\":\"guillermo\"},{\"name\":\"simon\"},{\"name\":\"tobi\"}\\]/,done)\n    })\n  })\n\n  describe('GET /users/1', function(){\n    it('should respond with user 1', function(done){\n      request(app)\n        .get('/users/1')\n        .expect(/^{\"name\":\"ciaran\"}/,done)\n    })\n  })\n\n  describe('GET /users/9', function(){\n    it('should respond with error', function(done){\n      request(app)\n        .get('/users/9')\n        .expect('{\"error\":\"Cannot find user\"}', done)\n    })\n  })\n\n  describe('GET /users/1..3', function(){\n    it('should respond with users 1 through 3', function(done){\n      request(app)\n        .get('/users/1..3')\n        .expect(/^<ul><li>ciaran<\\/li>\\n<li>aaron<\\/li>\\n<li>guillermo<\\/li><\\/ul>/,done)\n    })\n  })\n\n  describe('DELETE /users/1', function(){\n    it('should delete user 1', function(done){\n      request(app)\n        .del('/users/1')\n        .expect(/^destroyed/,done)\n    })\n  })\n\n  describe('DELETE /users/9', function(){\n    it('should fail', function(done){\n      request(app)\n        .del('/users/9')\n        .expect('Cannot find user', done)\n    })\n  })\n\n  describe('GET /users/1..3.json', function(){\n    it('should respond with users 2 and 3 as json', function(done){\n      request(app)\n        .get('/users/1..3.json')\n        .expect(/^\\[null,{\"name\":\"aaron\"},{\"name\":\"guillermo\"}\\]/,done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/route-map.js",
    "content": "\nvar request = require('supertest')\n  , app = require('../../examples/route-map');\n\ndescribe('route-map', function(){\n  describe('GET /users', function(){\n    it('should respond with users', function(done){\n      request(app)\n      .get('/users')\n      .expect('user list', done);\n    })\n  })\n\n  describe('DELETE /users', function(){\n    it('should delete users', function(done){\n      request(app)\n      .del('/users')\n      .expect('delete users', done);\n    })\n  })\n\n  describe('GET /users/:id', function(){\n    it('should get a user', function(done){\n      request(app)\n      .get('/users/12')\n      .expect('user 12', done);\n    })\n  })\n\n  describe('GET /users/:id/pets', function(){\n    it('should get a users pets', function(done){\n      request(app)\n      .get('/users/12/pets')\n      .expect('user 12\\'s pets', done);\n    })\n  })\n\n  describe('GET /users/:id/pets/:pid', function(){\n    it('should get a users pet', function(done){\n      request(app)\n      .del('/users/12/pets/2')\n      .expect('delete 12\\'s pet 2', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/route-separation.js",
    "content": "\nvar app = require('../../examples/route-separation')\nvar request = require('supertest')\n\ndescribe('route-separation', function () {\n  describe('GET /', function () {\n    it('should respond with index', function (done) {\n      request(app)\n      .get('/')\n      .expect(200, /Route Separation Example/, done)\n    })\n  })\n\n  describe('GET /users', function () {\n    it('should list users', function (done) {\n      request(app)\n      .get('/users')\n      .expect(/TJ/)\n      .expect(/Tobi/)\n      .expect(200, done)\n    })\n  })\n\n  describe('GET /user/:id', function () {\n    it('should get a user', function (done) {\n      request(app)\n      .get('/user/0')\n      .expect(200, /Viewing user TJ/, done)\n    })\n\n    it('should 404 on missing user', function (done) {\n      request(app)\n      .get('/user/10')\n      .expect(404, done)\n    })\n  })\n\n  describe('GET /user/:id/view', function () {\n    it('should get a user', function (done) {\n      request(app)\n      .get('/user/0/view')\n      .expect(200, /Viewing user TJ/, done)\n    })\n\n    it('should 404 on missing user', function (done) {\n      request(app)\n      .get('/user/10/view')\n      .expect(404, done)\n    })\n  })\n\n  describe('GET /user/:id/edit', function () {\n    it('should get a user to edit', function (done) {\n      request(app)\n      .get('/user/0/edit')\n      .expect(200, /Editing user TJ/, done)\n    })\n  })\n\n  describe('PUT /user/:id/edit', function () {\n    it('should edit a user', function (done) {\n      request(app)\n      .put('/user/0/edit')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send({ user: { name: 'TJ', email: 'tj-invalid@vision-media.ca' } })\n      .expect(302, function (err) {\n        if (err) return done(err)\n        request(app)\n        .get('/user/0')\n        .expect(200, /tj-invalid@vision-media\\.ca/, done)\n      })\n    })\n  })\n\n  describe('POST /user/:id/edit?_method=PUT', function () {\n    it('should edit a user', function (done) {\n      request(app)\n      .post('/user/1/edit?_method=PUT')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send({ user: { name: 'Tobi', email: 'tobi-invalid@vision-media.ca' } })\n      .expect(302, function (err) {\n        if (err) return done(err)\n        request(app)\n        .get('/user/1')\n        .expect(200, /tobi-invalid@vision-media\\.ca/, done)\n      })\n    })\n  })\n\n  describe('GET /posts', function () {\n    it('should get a list of posts', function (done) {\n      request(app)\n      .get('/posts')\n      .expect(200, /Posts/, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/vhost.js",
    "content": "var app = require('../../examples/vhost')\nvar request = require('supertest')\n\ndescribe('vhost', function(){\n  describe('example.com', function(){\n    describe('GET /', function(){\n      it('should say hello', function(done){\n        request(app)\n        .get('/')\n        .set('Host', 'example.com')\n        .expect(200, /hello/i, done)\n      })\n    })\n\n    describe('GET /foo', function(){\n      it('should say foo', function(done){\n        request(app)\n        .get('/foo')\n        .set('Host', 'example.com')\n        .expect(200, 'requested foo', done)\n      })\n    })\n  })\n\n  describe('foo.example.com', function(){\n    describe('GET /', function(){\n      it('should redirect to /foo', function(done){\n        request(app)\n        .get('/')\n        .set('Host', 'foo.example.com')\n        .expect(302, /Redirecting to http:\\/\\/example.com:3000\\/foo/, done)\n      })\n    })\n  })\n\n  describe('bar.example.com', function(){\n    describe('GET /', function(){\n      it('should redirect to /bar', function(done){\n        request(app)\n        .get('/')\n        .set('Host', 'bar.example.com')\n        .expect(302, /Redirecting to http:\\/\\/example.com:3000\\/bar/, done)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/acceptance/web-service.js",
    "content": "\nvar request = require('supertest')\n  , app = require('../../examples/web-service');\n\ndescribe('web-service', function(){\n  describe('GET /api/users', function(){\n    describe('without an api key', function(){\n      it('should respond with 400 bad request', function(done){\n        request(app)\n        .get('/api/users')\n        .expect(400, done);\n      })\n    })\n\n    describe('with an invalid api key', function(){\n      it('should respond with 401 unauthorized', function(done){\n        request(app)\n        .get('/api/users?api-key=rawr')\n        .expect(401, done);\n      })\n    })\n\n    describe('with a valid api key', function(){\n      it('should respond users json', function(done){\n        request(app)\n        .get('/api/users?api-key=foo')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '[{\"name\":\"tobi\"},{\"name\":\"loki\"},{\"name\":\"jane\"}]', done)\n      })\n    })\n  })\n\n  describe('GET /api/repos', function(){\n    describe('without an api key', function(){\n      it('should respond with 400 bad request', function(done){\n        request(app)\n        .get('/api/repos')\n        .expect(400, done);\n      })\n    })\n\n    describe('with an invalid api key', function(){\n      it('should respond with 401 unauthorized', function(done){\n        request(app)\n        .get('/api/repos?api-key=rawr')\n        .expect(401, done);\n      })\n    })\n\n    describe('with a valid api key', function(){\n      it('should respond repos json', function(done){\n        request(app)\n        .get('/api/repos?api-key=foo')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(/\"name\":\"express\"/)\n        .expect(/\"url\":\"https:\\/\\/github.com\\/expressjs\\/express\"/)\n        .expect(200, done)\n      })\n    })\n  })\n\n  describe('GET /api/user/:name/repos', function(){\n    describe('without an api key', function(){\n      it('should respond with 400 bad request', function(done){\n        request(app)\n        .get('/api/user/loki/repos')\n        .expect(400, done);\n      })\n    })\n\n    describe('with an invalid api key', function(){\n      it('should respond with 401 unauthorized', function(done){\n        request(app)\n        .get('/api/user/loki/repos?api-key=rawr')\n        .expect(401, done);\n      })\n    })\n\n    describe('with a valid api key', function(){\n      it('should respond user repos json', function(done){\n        request(app)\n        .get('/api/user/loki/repos?api-key=foo')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(/\"name\":\"stylus\"/)\n        .expect(/\"url\":\"https:\\/\\/github.com\\/learnboost\\/stylus\"/)\n        .expect(200, done)\n      })\n\n      it('should 404 with unknown user', function(done){\n        request(app)\n        .get('/api/user/bob/repos?api-key=foo')\n        .expect(404, done)\n      })\n    })\n  })\n\n  describe('when requesting an invalid route', function(){\n    it('should respond with 404 json', function(done){\n      request(app)\n        .get('/api/something?api-key=bar')\n        .expect('Content-Type', /json/)\n        .expect(404, '{\"error\":\"Sorry, can\\'t find that\"}', done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/app.all.js",
    "content": "'use strict'\n\nvar after = require('after')\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('app.all()', function(){\n  it('should add a router per method', function(done){\n    var app = express();\n    var cb = after(2, done)\n\n    app.all('/tobi', function(req, res){\n      res.end(req.method);\n    });\n\n    request(app)\n      .put('/tobi')\n      .expect(200, 'PUT', cb)\n\n    request(app)\n      .get('/tobi')\n      .expect(200, 'GET', cb)\n  })\n\n  it('should run the callback for a method just once', function(done){\n    var app = express()\n      , n = 0;\n\n    app.all('/*splat', function(req, res, next){\n      if (n++) return done(new Error('DELETE called several times'));\n      next();\n    });\n\n    request(app)\n    .del('/tobi')\n    .expect(404, done);\n  })\n})\n"
  },
  {
    "path": "test/app.engine.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('../')\n  , fs = require('node:fs');\nvar path = require('node:path')\n\nfunction render(path, options, fn) {\n  fs.readFile(path, 'utf8', function(err, str){\n    if (err) return fn(err);\n    str = str.replace('{{user.name}}', options.user.name);\n    fn(null, str);\n  });\n}\n\ndescribe('app', function(){\n  describe('.engine(ext, fn)', function(){\n    it('should map a template engine', function(done){\n      var app = express();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.engine('.html', render);\n      app.locals.user = { name: 'tobi' };\n\n      app.render('user.html', function(err, str){\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n\n    it('should throw when the callback is missing', function(){\n      var app = express();\n      assert.throws(function () {\n        app.engine('.html', null);\n      }, /callback function required/)\n    })\n\n    it('should work without leading \".\"', function(done){\n      var app = express();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.engine('html', render);\n      app.locals.user = { name: 'tobi' };\n\n      app.render('user.html', function(err, str){\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n\n    it('should work \"view engine\" setting', function(done){\n      var app = express();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.engine('html', render);\n      app.set('view engine', 'html');\n      app.locals.user = { name: 'tobi' };\n\n      app.render('user', function(err, str){\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n\n    it('should work \"view engine\" with leading \".\"', function(done){\n      var app = express();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.engine('.html', render);\n      app.set('view engine', '.html');\n      app.locals.user = { name: 'tobi' };\n\n      app.render('user', function(err, str){\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/app.head.js",
    "content": "'use strict'\n\nvar express = require('../');\nvar request = require('supertest');\nvar assert = require('node:assert');\n\ndescribe('HEAD', function(){\n  it('should default to GET', function(done){\n    var app = express();\n\n    app.get('/tobi', function(req, res){\n      // send() detects HEAD\n      res.send('tobi');\n    });\n\n    request(app)\n    .head('/tobi')\n    .expect(200, done);\n  })\n\n  it('should output the same headers as GET requests', function(done){\n    var app = express();\n\n    app.get('/tobi', function(req, res){\n      // send() detects HEAD\n      res.send('tobi');\n    });\n\n    request(app)\n    .head('/tobi')\n    .expect(200, function(err, res){\n      if (err) return done(err);\n      var headers = res.headers;\n      request(app)\n      .get('/tobi')\n      .expect(200, function(err, res){\n        if (err) return done(err);\n        delete headers.date;\n        delete res.headers.date;\n        assert.deepEqual(res.headers, headers);\n        done();\n      });\n    });\n  })\n})\n\ndescribe('app.head()', function(){\n  it('should override', function(done){\n    var app = express()\n\n    app.head('/tobi', function(req, res){\n      res.header('x-method', 'head')\n      res.end()\n    });\n\n    app.get('/tobi', function(req, res){\n      res.header('x-method', 'get')\n      res.send('tobi');\n    });\n\n    request(app)\n      .head('/tobi')\n      .expect('x-method', 'head')\n      .expect(200, done)\n  })\n})\n"
  },
  {
    "path": "test/app.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('..')\nvar request = require('supertest')\n\ndescribe('app', function(){\n  it('should inherit from event emitter', function(done){\n    var app = express();\n    app.on('foo', done);\n    app.emit('foo');\n  })\n\n  it('should be callable', function(){\n    var app = express();\n    assert.equal(typeof app, 'function');\n  })\n\n  it('should 404 without routes', function(done){\n    request(express())\n    .get('/')\n    .expect(404, done);\n  })\n})\n\ndescribe('app.parent', function(){\n  it('should return the parent when mounted', function(){\n    var app = express()\n      , blog = express()\n      , blogAdmin = express();\n\n    app.use('/blog', blog);\n    blog.use('/admin', blogAdmin);\n\n    assert(!app.parent, 'app.parent');\n    assert.strictEqual(blog.parent, app)\n    assert.strictEqual(blogAdmin.parent, blog)\n  })\n})\n\ndescribe('app.mountpath', function(){\n  it('should return the mounted path', function(){\n    var admin = express();\n    var app = express();\n    var blog = express();\n    var fallback = express();\n\n    app.use('/blog', blog);\n    app.use(fallback);\n    blog.use('/admin', admin);\n\n    assert.strictEqual(admin.mountpath, '/admin')\n    assert.strictEqual(app.mountpath, '/')\n    assert.strictEqual(blog.mountpath, '/blog')\n    assert.strictEqual(fallback.mountpath, '/')\n  })\n})\n\ndescribe('app.path()', function(){\n  it('should return the canonical', function(){\n    var app = express()\n      , blog = express()\n      , blogAdmin = express();\n\n    app.use('/blog', blog);\n    blog.use('/admin', blogAdmin);\n\n    assert.strictEqual(app.path(), '')\n    assert.strictEqual(blog.path(), '/blog')\n    assert.strictEqual(blogAdmin.path(), '/blog/admin')\n  })\n})\n\ndescribe('in development', function(){\n  before(function () {\n    this.env = process.env.NODE_ENV\n    process.env.NODE_ENV = 'development'\n  })\n\n  after(function () {\n    process.env.NODE_ENV = this.env\n  })\n\n  it('should disable \"view cache\"', function(){\n    var app = express();\n    assert.ok(!app.enabled('view cache'))\n  })\n})\n\ndescribe('in production', function(){\n  before(function () {\n    this.env = process.env.NODE_ENV\n    process.env.NODE_ENV = 'production'\n  })\n\n  after(function () {\n    process.env.NODE_ENV = this.env\n  })\n\n  it('should enable \"view cache\"', function(){\n    var app = express();\n    assert.ok(app.enabled('view cache'))\n  })\n})\n\ndescribe('without NODE_ENV', function(){\n  before(function () {\n    this.env = process.env.NODE_ENV\n    process.env.NODE_ENV = ''\n  })\n\n  after(function () {\n    process.env.NODE_ENV = this.env\n  })\n\n  it('should default to development', function(){\n    var app = express();\n    assert.strictEqual(app.get('env'), 'development')\n  })\n})\n"
  },
  {
    "path": "test/app.listen.js",
    "content": "'use strict'\n\nvar express = require('../')\nvar assert = require('node:assert')\n\ndescribe('app.listen()', function(){\n  it('should wrap with an HTTP server', function(done){\n    var app = express();\n\n    var server = app.listen(0, function () {\n      server.close(done)\n    });\n  })\n  it('should callback on HTTP server errors', function (done) {\n    var app1 = express()\n    var app2 = express()\n\n    var server1 = app1.listen(0, function (err) {\n      assert(!err)\n      app2.listen(server1.address().port, function (err) {\n        assert(err.code === 'EADDRINUSE')\n        server1.close()\n        done()\n      })\n    })\n  })\n  it('accepts port + hostname + backlog + callback', function (done) {\n    const app = express();\n    const server = app.listen(0, '127.0.0.1', 5, function () {\n      const { address, port } = server.address();\n      assert.strictEqual(address, '127.0.0.1');\n      assert(Number.isInteger(port) && port > 0);\n      // backlog isn’t directly inspectable, but if no error was thrown\n      // we know it was accepted.\n      server.close(done);\n    });\n  });\n  it('accepts just a callback (no args)', function (done) {\n    const app = express();\n    // same as app.listen(0, done)\n    const server = app.listen();\n    server.close(done);\n  });\n  it('server.address() gives a { address, port, family } object', function (done) {\n    const app = express();\n    const server = app.listen(0, () => {\n      const addr = server.address();\n      assert(addr && typeof addr === 'object');\n      assert.strictEqual(typeof addr.address, 'string');\n      assert(Number.isInteger(addr.port) && addr.port > 0);\n      assert(typeof addr.family === 'string');\n      server.close(done);\n    });\n  });\n})\n"
  },
  {
    "path": "test/app.locals.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('../')\n\ndescribe('app', function(){\n  describe('.locals', function () {\n    it('should default object with null prototype', function () {\n      var app = express()\n      assert.ok(app.locals)\n      assert.strictEqual(typeof app.locals, 'object')\n      assert.strictEqual(Object.getPrototypeOf(app.locals), null)\n    })\n\n    describe('.settings', function () {\n      it('should contain app settings ', function () {\n        var app = express()\n        app.set('title', 'Express')\n        assert.ok(app.locals.settings)\n        assert.strictEqual(typeof app.locals.settings, 'object')\n        assert.strictEqual(app.locals.settings, app.settings)\n        assert.strictEqual(app.locals.settings.title, 'Express')\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/app.options.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('OPTIONS', function(){\n  it('should default to the routes defined', function(done){\n    var app = express();\n\n    app.post('/', function(){});\n    app.get('/users', function(req, res){});\n    app.put('/users', function(req, res){});\n\n    request(app)\n    .options('/users')\n    .expect('Allow', 'GET, HEAD, PUT')\n    .expect(200, 'GET, HEAD, PUT', done);\n  })\n\n  it('should only include each method once', function(done){\n    var app = express();\n\n    app.delete('/', function(){});\n    app.get('/users', function(req, res){});\n    app.put('/users', function(req, res){});\n    app.get('/users', function(req, res){});\n\n    request(app)\n    .options('/users')\n    .expect('Allow', 'GET, HEAD, PUT')\n    .expect(200, 'GET, HEAD, PUT', done);\n  })\n\n  it('should not be affected by app.all', function(done){\n    var app = express();\n\n    app.get('/', function(){});\n    app.get('/users', function(req, res){});\n    app.put('/users', function(req, res){});\n    app.all('/users', function(req, res, next){\n      res.setHeader('x-hit', '1');\n      next();\n    });\n\n    request(app)\n    .options('/users')\n    .expect('x-hit', '1')\n    .expect('Allow', 'GET, HEAD, PUT')\n    .expect(200, 'GET, HEAD, PUT', done);\n  })\n\n  it('should not respond if the path is not defined', function(done){\n    var app = express();\n\n    app.get('/users', function(req, res){});\n\n    request(app)\n    .options('/other')\n    .expect(404, done);\n  })\n\n  it('should forward requests down the middleware chain', function(done){\n    var app = express();\n    var router = new express.Router();\n\n    router.get('/users', function(req, res){});\n    app.use(router);\n    app.get('/other', function(req, res){});\n\n    request(app)\n    .options('/other')\n    .expect('Allow', 'GET, HEAD')\n    .expect(200, 'GET, HEAD', done);\n  })\n\n  describe('when error occurs in response handler', function () {\n    it('should pass error to callback', function (done) {\n      var app = express();\n      var router = express.Router();\n\n      router.get('/users', function(req, res){});\n\n      app.use(function (req, res, next) {\n        res.writeHead(200);\n        next();\n      });\n      app.use(router);\n      app.use(function (err, req, res, next) {\n        res.end('true');\n      });\n\n      request(app)\n      .options('/users')\n      .expect(200, 'true', done)\n    })\n  })\n})\n\ndescribe('app.options()', function(){\n  it('should override the default behavior', function(done){\n    var app = express();\n\n    app.options('/users', function(req, res){\n      res.set('Allow', 'GET');\n      res.send('GET');\n    });\n\n    app.get('/users', function(req, res){});\n    app.put('/users', function(req, res){});\n\n    request(app)\n    .options('/users')\n    .expect('GET')\n    .expect('Allow', 'GET', done);\n  })\n})\n"
  },
  {
    "path": "test/app.param.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('app', function(){\n  describe('.param(names, fn)', function(){\n    it('should map the array', function(done){\n      var app = express();\n\n      app.param(['id', 'uid'], function(req, res, next, id){\n        id = Number(id);\n        if (isNaN(id)) return next('route');\n        req.params.id = id;\n        next();\n      });\n\n      app.get('/post/:id', function(req, res){\n        var id = req.params.id;\n        res.send((typeof id) + ':' + id)\n      });\n\n      app.get('/user/:uid', function(req, res){\n        var id = req.params.id;\n        res.send((typeof id) + ':' + id)\n      });\n\n      request(app)\n        .get('/user/123')\n        .expect(200, 'number:123', function (err) {\n          if (err) return done(err)\n          request(app)\n            .get('/post/123')\n            .expect('number:123', done)\n        })\n    })\n  })\n\n  describe('.param(name, fn)', function(){\n    it('should map logic for a single param', function(done){\n      var app = express();\n\n      app.param('id', function(req, res, next, id){\n        id = Number(id);\n        if (isNaN(id)) return next('route');\n        req.params.id = id;\n        next();\n      });\n\n      app.get('/user/:id', function(req, res){\n        var id = req.params.id;\n        res.send((typeof id) + ':' + id)\n      });\n\n      request(app)\n        .get('/user/123')\n        .expect(200, 'number:123', done)\n    })\n\n    it('should only call once per request', function(done) {\n      var app = express();\n      var called = 0;\n      var count = 0;\n\n      app.param('user', function(req, res, next, user) {\n        called++;\n        req.user = user;\n        next();\n      });\n\n      app.get('/foo/:user', function(req, res, next) {\n        count++;\n        next();\n      });\n      app.get('/foo/:user', function(req, res, next) {\n        count++;\n        next();\n      });\n      app.use(function(req, res) {\n        res.end([count, called, req.user].join(' '));\n      });\n\n      request(app)\n      .get('/foo/bob')\n      .expect('2 1 bob', done);\n    })\n\n    it('should call when values differ', function(done) {\n      var app = express();\n      var called = 0;\n      var count = 0;\n\n      app.param('user', function(req, res, next, user) {\n        called++;\n        req.users = (req.users || []).concat(user);\n        next();\n      });\n\n      app.get('/:user/bob', function(req, res, next) {\n        count++;\n        next();\n      });\n      app.get('/foo/:user', function(req, res, next) {\n        count++;\n        next();\n      });\n      app.use(function(req, res) {\n        res.end([count, called, req.users.join(',')].join(' '));\n      });\n\n      request(app)\n      .get('/foo/bob')\n      .expect('2 2 foo,bob', done);\n    })\n\n    it('should support altering req.params across routes', function(done) {\n      var app = express();\n\n      app.param('user', function(req, res, next, user) {\n        req.params.user = 'loki';\n        next();\n      });\n\n      app.get('/:user', function(req, res, next) {\n        next('route');\n      });\n      app.get('/:user', function (req, res) {\n        res.send(req.params.user);\n      });\n\n      request(app)\n      .get('/bob')\n      .expect('loki', done);\n    })\n\n    it('should not invoke without route handler', function(done) {\n      var app = express();\n\n      app.param('thing', function(req, res, next, thing) {\n        req.thing = thing;\n        next();\n      });\n\n      app.param('user', function(req, res, next, user) {\n        next(new Error('invalid invocation'))\n      });\n\n      app.post('/:user', function (req, res) {\n        res.send(req.params.user);\n      });\n\n      app.get('/:thing', function (req, res) {\n        res.send(req.thing);\n      });\n\n      request(app)\n      .get('/bob')\n      .expect(200, 'bob', done);\n    })\n\n    it('should work with encoded values', function(done){\n      var app = express();\n\n      app.param('name', function(req, res, next, name){\n        req.params.name = name;\n        next();\n      });\n\n      app.get('/user/:name', function(req, res){\n        var name = req.params.name;\n        res.send('' + name);\n      });\n\n      request(app)\n      .get('/user/foo%25bar')\n      .expect('foo%bar', done);\n    })\n\n    it('should catch thrown error', function(done){\n      var app = express();\n\n      app.param('id', function(req, res, next, id){\n        throw new Error('err!');\n      });\n\n      app.get('/user/:id', function(req, res){\n        var id = req.params.id;\n        res.send('' + id);\n      });\n\n      request(app)\n      .get('/user/123')\n      .expect(500, done);\n    })\n\n    it('should catch thrown secondary error', function(done){\n      var app = express();\n\n      app.param('id', function(req, res, next, val){\n        process.nextTick(next);\n      });\n\n      app.param('id', function(req, res, next, id){\n        throw new Error('err!');\n      });\n\n      app.get('/user/:id', function(req, res){\n        var id = req.params.id;\n        res.send('' + id);\n      });\n\n      request(app)\n      .get('/user/123')\n      .expect(500, done);\n    })\n\n    it('should defer to next route', function(done){\n      var app = express();\n\n      app.param('id', function(req, res, next, id){\n        next('route');\n      });\n\n      app.get('/user/:id', function(req, res){\n        var id = req.params.id;\n        res.send('' + id);\n      });\n\n      app.get('/:name/123', function(req, res){\n        res.send('name');\n      });\n\n      request(app)\n      .get('/user/123')\n      .expect('name', done);\n    })\n\n    it('should defer all the param routes', function(done){\n      var app = express();\n\n      app.param('id', function(req, res, next, val){\n        if (val === 'new') return next('route');\n        return next();\n      });\n\n      app.all('/user/:id', function(req, res){\n        res.send('all.id');\n      });\n\n      app.get('/user/:id', function(req, res){\n        res.send('get.id');\n      });\n\n      app.get('/user/new', function(req, res){\n        res.send('get.new');\n      });\n\n      request(app)\n      .get('/user/new')\n      .expect('get.new', done);\n    })\n\n    it('should not call when values differ on error', function(done) {\n      var app = express();\n      var called = 0;\n      var count = 0;\n\n      app.param('user', function(req, res, next, user) {\n        called++;\n        if (user === 'foo') throw new Error('err!');\n        req.user = user;\n        next();\n      });\n\n      app.get('/:user/bob', function(req, res, next) {\n        count++;\n        next();\n      });\n      app.get('/foo/:user', function(req, res, next) {\n        count++;\n        next();\n      });\n\n      app.use(function(err, req, res, next) {\n        res.status(500);\n        res.send([count, called, err.message].join(' '));\n      });\n\n      request(app)\n      .get('/foo/bob')\n      .expect(500, '0 1 err!', done)\n    });\n\n    it('should call when values differ when using \"next\"', function(done) {\n      var app = express();\n      var called = 0;\n      var count = 0;\n\n      app.param('user', function(req, res, next, user) {\n        called++;\n        if (user === 'foo') return next('route');\n        req.user = user;\n        next();\n      });\n\n      app.get('/:user/bob', function(req, res, next) {\n        count++;\n        next();\n      });\n      app.get('/foo/:user', function(req, res, next) {\n        count++;\n        next();\n      });\n      app.use(function(req, res) {\n        res.end([count, called, req.user].join(' '));\n      });\n\n      request(app)\n      .get('/foo/bob')\n      .expect('1 2 bob', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/app.render.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('..');\nvar path = require('node:path')\nvar tmpl = require('./support/tmpl');\n\ndescribe('app', function(){\n  describe('.render(name, fn)', function(){\n    it('should support absolute paths', function(done){\n      var app = createApp();\n\n      app.locals.user = { name: 'tobi' };\n\n      app.render(path.join(__dirname, 'fixtures', 'user.tmpl'), function (err, str) {\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n\n    it('should support absolute paths with \"view engine\"', function(done){\n      var app = createApp();\n\n      app.set('view engine', 'tmpl');\n      app.locals.user = { name: 'tobi' };\n\n      app.render(path.join(__dirname, 'fixtures', 'user'), function (err, str) {\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n\n    it('should expose app.locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.user = { name: 'tobi' };\n\n      app.render('user.tmpl', function (err, str) {\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n\n    it('should support index.<engine>', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.set('view engine', 'tmpl');\n\n      app.render('blog/post', function (err, str) {\n        if (err) return done(err);\n        assert.strictEqual(str, '<h1>blog post</h1>')\n        done();\n      })\n    })\n\n    it('should handle render error throws', function(done){\n      var app = express();\n\n      function View(name, options){\n        this.name = name;\n        this.path = 'fale';\n      }\n\n      View.prototype.render = function(options, fn){\n        throw new Error('err!');\n      };\n\n      app.set('view', View);\n\n      app.render('something', function(err, str){\n        assert.ok(err)\n        assert.strictEqual(err.message, 'err!')\n        done();\n      })\n    })\n\n    describe('when the file does not exist', function(){\n      it('should provide a helpful error', function(done){\n        var app = createApp();\n\n        app.set('views', path.join(__dirname, 'fixtures'))\n        app.render('rawr.tmpl', function (err) {\n          assert.ok(err)\n          assert.equal(err.message, 'Failed to lookup view \"rawr.tmpl\" in views directory \"' + path.join(__dirname, 'fixtures') + '\"')\n          done();\n        });\n      })\n    })\n\n    describe('when an error occurs', function(){\n      it('should invoke the callback', function(done){\n        var app = createApp();\n\n        app.set('views', path.join(__dirname, 'fixtures'))\n\n        app.render('user.tmpl', function (err) {\n          assert.ok(err)\n          assert.equal(err.name, 'RenderError')\n          done()\n        })\n      })\n    })\n\n    describe('when an extension is given', function(){\n      it('should render the template', function(done){\n        var app = createApp();\n\n        app.set('views', path.join(__dirname, 'fixtures'))\n\n        app.render('email.tmpl', function (err, str) {\n          if (err) return done(err);\n          assert.strictEqual(str, '<p>This is an email</p>')\n          done();\n        })\n      })\n    })\n\n    describe('when \"view engine\" is given', function(){\n      it('should render the template', function(done){\n        var app = createApp();\n\n        app.set('view engine', 'tmpl');\n        app.set('views', path.join(__dirname, 'fixtures'))\n\n        app.render('email', function(err, str){\n          if (err) return done(err);\n          assert.strictEqual(str, '<p>This is an email</p>')\n          done();\n        })\n      })\n    })\n\n    describe('when \"views\" is given', function(){\n      it('should lookup the file in the path', function(done){\n        var app = createApp();\n\n        app.set('views',  path.join(__dirname, 'fixtures', 'default_layout'))\n        app.locals.user = { name: 'tobi' };\n\n        app.render('user.tmpl', function (err, str) {\n          if (err) return done(err);\n          assert.strictEqual(str, '<p>tobi</p>')\n          done();\n        })\n      })\n\n      describe('when array of paths', function(){\n        it('should lookup the file in the path', function(done){\n          var app = createApp();\n          var views = [\n            path.join(__dirname, 'fixtures', 'local_layout'),\n            path.join(__dirname, 'fixtures', 'default_layout')\n          ]\n\n          app.set('views', views);\n          app.locals.user = { name: 'tobi' };\n\n          app.render('user.tmpl', function (err, str) {\n            if (err) return done(err);\n            assert.strictEqual(str, '<span>tobi</span>')\n            done();\n          })\n        })\n\n        it('should lookup in later paths until found', function(done){\n          var app = createApp();\n          var views = [\n            path.join(__dirname, 'fixtures', 'local_layout'),\n            path.join(__dirname, 'fixtures', 'default_layout')\n          ]\n\n          app.set('views', views);\n          app.locals.name = 'tobi';\n\n          app.render('name.tmpl', function (err, str) {\n            if (err) return done(err);\n            assert.strictEqual(str, '<p>tobi</p>')\n            done();\n          })\n        })\n\n        it('should error if file does not exist', function(done){\n          var app = createApp();\n          var views = [\n            path.join(__dirname, 'fixtures', 'local_layout'),\n            path.join(__dirname, 'fixtures', 'default_layout')\n          ]\n\n          app.set('views', views);\n          app.locals.name = 'tobi';\n\n          app.render('pet.tmpl', function (err, str) {\n            assert.ok(err)\n            assert.equal(err.message, 'Failed to lookup view \"pet.tmpl\" in views directories \"' + views[0] + '\" or \"' + views[1] + '\"')\n            done();\n          })\n        })\n      })\n    })\n\n    describe('when a \"view\" constructor is given', function(){\n      it('should create an instance of it', function(done){\n        var app = express();\n\n        function View(name, options){\n          this.name = name;\n          this.path = 'path is required by application.js as a signal of success even though it is not used there.';\n        }\n\n        View.prototype.render = function(options, fn){\n          fn(null, 'abstract engine');\n        };\n\n        app.set('view', View);\n\n        app.render('something', function(err, str){\n          if (err) return done(err);\n          assert.strictEqual(str, 'abstract engine')\n          done();\n        })\n      })\n    })\n\n    describe('caching', function(){\n      it('should always lookup view without cache', function(done){\n        var app = express();\n        var count = 0;\n\n        function View(name, options){\n          this.name = name;\n          this.path = 'fake';\n          count++;\n        }\n\n        View.prototype.render = function(options, fn){\n          fn(null, 'abstract engine');\n        };\n\n        app.set('view cache', false);\n        app.set('view', View);\n\n        app.render('something', function(err, str){\n          if (err) return done(err);\n          assert.strictEqual(count, 1)\n          assert.strictEqual(str, 'abstract engine')\n          app.render('something', function(err, str){\n            if (err) return done(err);\n            assert.strictEqual(count, 2)\n            assert.strictEqual(str, 'abstract engine')\n            done();\n          })\n        })\n      })\n\n      it('should cache with \"view cache\" setting', function(done){\n        var app = express();\n        var count = 0;\n\n        function View(name, options){\n          this.name = name;\n          this.path = 'fake';\n          count++;\n        }\n\n        View.prototype.render = function(options, fn){\n          fn(null, 'abstract engine');\n        };\n\n        app.set('view cache', true);\n        app.set('view', View);\n\n        app.render('something', function(err, str){\n          if (err) return done(err);\n          assert.strictEqual(count, 1)\n          assert.strictEqual(str, 'abstract engine')\n          app.render('something', function(err, str){\n            if (err) return done(err);\n            assert.strictEqual(count, 1)\n            assert.strictEqual(str, 'abstract engine')\n            done();\n          })\n        })\n      })\n    })\n  })\n\n  describe('.render(name, options, fn)', function(){\n    it('should render the template', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n\n      var user = { name: 'tobi' };\n\n      app.render('user.tmpl', { user: user }, function (err, str) {\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n\n    it('should expose app.locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.user = { name: 'tobi' };\n\n      app.render('user.tmpl', {}, function (err, str) {\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n        done();\n      })\n    })\n\n    it('should give precedence to app.render() locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.user = { name: 'tobi' };\n      var jane = { name: 'jane' };\n\n      app.render('user.tmpl', { user: jane }, function (err, str) {\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>jane</p>')\n        done();\n      })\n    })\n\n    it('should accept null or undefined options', function (done) {\n      var app = createApp()\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.user = { name: 'tobi' }\n\n      app.render('user.tmpl', null, function (err, str) {\n        if (err) return done(err);\n        assert.strictEqual(str, '<p>tobi</p>')\n\n        app.render('user.tmpl', undefined, function (err2, str2) {\n          if (err2) return done(err2);\n          assert.strictEqual(str2, '<p>tobi</p>')\n          done()\n        })\n      })\n    })\n\n    describe('caching', function(){\n      it('should cache with cache option', function(done){\n        var app = express();\n        var count = 0;\n\n        function View(name, options){\n          this.name = name;\n          this.path = 'fake';\n          count++;\n        }\n\n        View.prototype.render = function(options, fn){\n          fn(null, 'abstract engine');\n        };\n\n        app.set('view cache', false);\n        app.set('view', View);\n\n        app.render('something', {cache: true}, function(err, str){\n          if (err) return done(err);\n          assert.strictEqual(count, 1)\n          assert.strictEqual(str, 'abstract engine')\n          app.render('something', {cache: true}, function(err, str){\n            if (err) return done(err);\n            assert.strictEqual(count, 1)\n            assert.strictEqual(str, 'abstract engine')\n            done();\n          })\n        })\n      })\n    })\n  })\n})\n\nfunction createApp() {\n  var app = express();\n\n  app.engine('.tmpl', tmpl);\n\n  return app;\n}\n"
  },
  {
    "path": "test/app.request.js",
    "content": "'use strict'\n\nvar after = require('after')\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('app', function(){\n  describe('.request', function(){\n    it('should extend the request prototype', function(done){\n      var app = express();\n\n      app.request.querystring = function(){\n        return require('node:url').parse(this.url).query;\n      };\n\n      app.use(function(req, res){\n        res.end(req.querystring());\n      });\n\n      request(app)\n      .get('/foo?name=tobi')\n      .expect('name=tobi', done);\n    })\n\n    it('should only extend for the referenced app', function (done) {\n      var app1 = express()\n      var app2 = express()\n      var cb = after(2, done)\n\n      app1.request.foobar = function () {\n        return 'tobi'\n      }\n\n      app1.get('/', function (req, res) {\n        res.send(req.foobar())\n      })\n\n      app2.get('/', function (req, res) {\n        res.send(req.foobar())\n      })\n\n      request(app1)\n        .get('/')\n        .expect(200, 'tobi', cb)\n\n      request(app2)\n        .get('/')\n        .expect(500, /(?:not a function|has no method)/, cb)\n    })\n\n    it('should inherit to sub apps', function (done) {\n      var app1 = express()\n      var app2 = express()\n      var cb = after(2, done)\n\n      app1.request.foobar = function () {\n        return 'tobi'\n      }\n\n      app1.use('/sub', app2)\n\n      app1.get('/', function (req, res) {\n        res.send(req.foobar())\n      })\n\n      app2.get('/', function (req, res) {\n        res.send(req.foobar())\n      })\n\n      request(app1)\n        .get('/')\n        .expect(200, 'tobi', cb)\n\n      request(app1)\n        .get('/sub')\n        .expect(200, 'tobi', cb)\n    })\n\n    it('should allow sub app to override', function (done) {\n      var app1 = express()\n      var app2 = express()\n      var cb = after(2, done)\n\n      app1.request.foobar = function () {\n        return 'tobi'\n      }\n\n      app2.request.foobar = function () {\n        return 'loki'\n      }\n\n      app1.use('/sub', app2)\n\n      app1.get('/', function (req, res) {\n        res.send(req.foobar())\n      })\n\n      app2.get('/', function (req, res) {\n        res.send(req.foobar())\n      })\n\n      request(app1)\n        .get('/')\n        .expect(200, 'tobi', cb)\n\n      request(app1)\n        .get('/sub')\n        .expect(200, 'loki', cb)\n    })\n\n    it('should not pollute parent app', function (done) {\n      var app1 = express()\n      var app2 = express()\n      var cb = after(2, done)\n\n      app1.request.foobar = function () {\n        return 'tobi'\n      }\n\n      app2.request.foobar = function () {\n        return 'loki'\n      }\n\n      app1.use('/sub', app2)\n\n      app1.get('/sub/foo', function (req, res) {\n        res.send(req.foobar())\n      })\n\n      app2.get('/', function (req, res) {\n        res.send(req.foobar())\n      })\n\n      request(app1)\n        .get('/sub')\n        .expect(200, 'loki', cb)\n\n      request(app1)\n        .get('/sub/foo')\n        .expect(200, 'tobi', cb)\n    })\n  })\n})\n"
  },
  {
    "path": "test/app.response.js",
    "content": "'use strict'\n\nvar after = require('after')\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('app', function(){\n  describe('.response', function(){\n    it('should extend the response prototype', function(done){\n      var app = express();\n\n      app.response.shout = function(str){\n        this.send(str.toUpperCase());\n      };\n\n      app.use(function(req, res){\n        res.shout('hey');\n      });\n\n      request(app)\n      .get('/')\n      .expect('HEY', done);\n    })\n\n    it('should only extend for the referenced app', function (done) {\n      var app1 = express()\n      var app2 = express()\n      var cb = after(2, done)\n\n      app1.response.shout = function (str) {\n        this.send(str.toUpperCase())\n      }\n\n      app1.get('/', function (req, res) {\n        res.shout('foo')\n      })\n\n      app2.get('/', function (req, res) {\n        res.shout('foo')\n      })\n\n      request(app1)\n        .get('/')\n        .expect(200, 'FOO', cb)\n\n      request(app2)\n        .get('/')\n        .expect(500, /(?:not a function|has no method)/, cb)\n    })\n\n    it('should inherit to sub apps', function (done) {\n      var app1 = express()\n      var app2 = express()\n      var cb = after(2, done)\n\n      app1.response.shout = function (str) {\n        this.send(str.toUpperCase())\n      }\n\n      app1.use('/sub', app2)\n\n      app1.get('/', function (req, res) {\n        res.shout('foo')\n      })\n\n      app2.get('/', function (req, res) {\n        res.shout('foo')\n      })\n\n      request(app1)\n        .get('/')\n        .expect(200, 'FOO', cb)\n\n      request(app1)\n        .get('/sub')\n        .expect(200, 'FOO', cb)\n    })\n\n    it('should allow sub app to override', function (done) {\n      var app1 = express()\n      var app2 = express()\n      var cb = after(2, done)\n\n      app1.response.shout = function (str) {\n        this.send(str.toUpperCase())\n      }\n\n      app2.response.shout = function (str) {\n        this.send(str + '!')\n      }\n\n      app1.use('/sub', app2)\n\n      app1.get('/', function (req, res) {\n        res.shout('foo')\n      })\n\n      app2.get('/', function (req, res) {\n        res.shout('foo')\n      })\n\n      request(app1)\n        .get('/')\n        .expect(200, 'FOO', cb)\n\n      request(app1)\n        .get('/sub')\n        .expect(200, 'foo!', cb)\n    })\n\n    it('should not pollute parent app', function (done) {\n      var app1 = express()\n      var app2 = express()\n      var cb = after(2, done)\n\n      app1.response.shout = function (str) {\n        this.send(str.toUpperCase())\n      }\n\n      app2.response.shout = function (str) {\n        this.send(str + '!')\n      }\n\n      app1.use('/sub', app2)\n\n      app1.get('/sub/foo', function (req, res) {\n        res.shout('foo')\n      })\n\n      app2.get('/', function (req, res) {\n        res.shout('foo')\n      })\n\n      request(app1)\n        .get('/sub')\n        .expect(200, 'foo!', cb)\n\n      request(app1)\n        .get('/sub/foo')\n        .expect(200, 'FOO', cb)\n    })\n  })\n})\n"
  },
  {
    "path": "test/app.route.js",
    "content": "'use strict'\n\nvar express = require('../');\nvar request = require('supertest');\n\ndescribe('app.route', function(){\n  it('should return a new route', function(done){\n    var app = express();\n\n    app.route('/foo')\n    .get(function(req, res) {\n      res.send('get');\n    })\n    .post(function(req, res) {\n      res.send('post');\n    });\n\n    request(app)\n    .post('/foo')\n    .expect('post', done);\n  });\n\n  it('should all .VERB after .all', function(done){\n    var app = express();\n\n    app.route('/foo')\n    .all(function(req, res, next) {\n      next();\n    })\n    .get(function(req, res) {\n      res.send('get');\n    })\n    .post(function(req, res) {\n      res.send('post');\n    });\n\n    request(app)\n    .post('/foo')\n    .expect('post', done);\n  });\n\n  it('should support dynamic routes', function(done){\n    var app = express();\n\n    app.route('/:foo')\n    .get(function(req, res) {\n      res.send(req.params.foo);\n    });\n\n    request(app)\n    .get('/test')\n    .expect('test', done);\n  });\n\n  it('should not error on empty routes', function(done){\n    var app = express();\n\n    app.route('/:foo');\n\n    request(app)\n    .get('/test')\n    .expect(404, done);\n  });\n\n  describe('promise support', function () {\n    it('should pass rejected promise value', function (done) {\n      var app = express()\n      var route = app.route('/foo')\n\n      route.all(function createError (req, res, next) {\n        return Promise.reject(new Error('boom!'))\n      })\n\n      route.all(function helloWorld (req, res) {\n        res.send('hello, world!')\n      })\n\n      route.all(function handleError (err, req, res, next) {\n        res.status(500)\n        res.send('caught: ' + err.message)\n      })\n\n      request(app)\n      .get('/foo')\n      .expect(500, 'caught: boom!', done)\n    })\n\n    it('should pass rejected promise without value', function (done) {\n      var app = express()\n      var route = app.route('/foo')\n\n      route.all(function createError (req, res, next) {\n        return Promise.reject()\n      })\n\n      route.all(function helloWorld (req, res) {\n        res.send('hello, world!')\n      })\n\n      route.all(function handleError (err, req, res, next) {\n        res.status(500)\n        res.send('caught: ' + err.message)\n      })\n\n      request(app)\n      .get('/foo')\n      .expect(500, 'caught: Rejected promise', done)\n    })\n\n    it('should ignore resolved promise', function (done) {\n      var app = express()\n      var route = app.route('/foo')\n\n      route.all(function createError (req, res, next) {\n        res.send('saw GET /foo')\n        return Promise.resolve('foo')\n      })\n\n      route.all(function () {\n        done(new Error('Unexpected route invoke'))\n      })\n\n      request(app)\n      .get('/foo')\n      .expect(200, 'saw GET /foo', done)\n    })\n\n    describe('error handling', function () {\n      it('should pass rejected promise value', function (done) {\n        var app = express()\n        var route = app.route('/foo')\n\n        route.all(function createError (req, res, next) {\n          return Promise.reject(new Error('boom!'))\n        })\n\n        route.all(function handleError (err, req, res, next) {\n          return Promise.reject(new Error('caught: ' + err.message))\n        })\n\n        route.all(function handleError (err, req, res, next) {\n          res.status(500)\n          res.send('caught again: ' + err.message)\n        })\n\n        request(app)\n        .get('/foo')\n        .expect(500, 'caught again: caught: boom!', done)\n      })\n\n      it('should pass rejected promise without value', function (done) {\n        var app = express()\n        var route = app.route('/foo')\n\n        route.all(function createError (req, res, next) {\n          return Promise.reject(new Error('boom!'))\n        })\n\n        route.all(function handleError (err, req, res, next) {\n          return Promise.reject()\n        })\n\n        route.all(function handleError (err, req, res, next) {\n          res.status(500)\n          res.send('caught again: ' + err.message)\n        })\n\n        request(app)\n        .get('/foo')\n        .expect(500, 'caught again: Rejected promise', done)\n      })\n\n      it('should ignore resolved promise', function (done) {\n        var app = express()\n        var route = app.route('/foo')\n\n        route.all(function createError (req, res, next) {\n          return Promise.reject(new Error('boom!'))\n        })\n\n        route.all(function handleError (err, req, res, next) {\n          res.status(500)\n          res.send('caught: ' + err.message)\n          return Promise.resolve('foo')\n        })\n\n        route.all(function () {\n          done(new Error('Unexpected route invoke'))\n        })\n\n        request(app)\n        .get('/foo')\n        .expect(500, 'caught: boom!', done)\n      })\n    })\n  })\n});\n"
  },
  {
    "path": "test/app.router.js",
    "content": "'use strict'\n\nvar after = require('after');\nvar express = require('../')\n  , request = require('supertest')\n  , assert = require('node:assert')\n  , methods = require('../lib/utils').methods;\n\nvar shouldSkipQuery = require('./support/utils').shouldSkipQuery\n\ndescribe('app.router', function () {\n  it('should restore req.params after leaving router', function (done) {\n    var app = express();\n    var router = new express.Router();\n\n    function handler1(req, res, next) {\n      res.setHeader('x-user-id', String(req.params.id));\n      next()\n    }\n\n    function handler2(req, res) {\n      res.send(req.params.id);\n    }\n\n    router.use(function (req, res, next) {\n      res.setHeader('x-router', String(req.params.id));\n      next();\n    });\n\n    app.get('/user/:id', handler1, router, handler2);\n\n    request(app)\n      .get('/user/1')\n      .expect('x-router', 'undefined')\n      .expect('x-user-id', '1')\n      .expect(200, '1', done);\n  })\n\n  describe('methods', function () {\n    methods.forEach(function (method) {\n      if (method === 'connect') return;\n\n      it('should include ' + method.toUpperCase(), function (done) {\n        if (method === 'query' && shouldSkipQuery(process.versions.node)) {\n          this.skip()\n        }\n        var app = express();\n\n        app[method]('/foo', function (req, res) {\n          res.send(method)\n        });\n\n        request(app)\n        [method]('/foo')\n          .expect(200, done)\n      })\n\n      it('should reject numbers for app.' + method, function () {\n        var app = express();\n        assert.throws(app[method].bind(app, '/', 3), /argument handler must be a function/);\n      })\n    });\n\n    it('should re-route when method is altered', function (done) {\n      var app = express();\n      var cb = after(3, done);\n\n      app.use(function (req, res, next) {\n        if (req.method !== 'POST') return next();\n        req.method = 'DELETE';\n        res.setHeader('X-Method-Altered', '1');\n        next();\n      });\n\n      app.delete('/', function (req, res) {\n        res.end('deleted everything');\n      });\n\n      request(app)\n        .get('/')\n        .expect(404, cb)\n\n      request(app)\n        .delete('/')\n        .expect(200, 'deleted everything', cb);\n\n      request(app)\n        .post('/')\n        .expect('X-Method-Altered', '1')\n        .expect(200, 'deleted everything', cb);\n    });\n  })\n\n  describe('decode params', function () {\n    it('should decode correct params', function (done) {\n      var app = express();\n\n      app.get('/:name', function (req, res) {\n        res.send(req.params.name);\n      });\n\n      request(app)\n        .get('/foo%2Fbar')\n        .expect('foo/bar', done);\n    })\n\n    it('should not accept params in malformed paths', function (done) {\n      var app = express();\n\n      app.get('/:name', function (req, res) {\n        res.send(req.params.name);\n      });\n\n      request(app)\n        .get('/%foobar')\n        .expect(400, done);\n    })\n\n    it('should not decode spaces', function (done) {\n      var app = express();\n\n      app.get('/:name', function (req, res) {\n        res.send(req.params.name);\n      });\n\n      request(app)\n        .get('/foo+bar')\n        .expect('foo+bar', done);\n    })\n\n    it('should work with unicode', function (done) {\n      var app = express();\n\n      app.get('/:name', function (req, res) {\n        res.send(req.params.name);\n      });\n\n      request(app)\n        .get('/%ce%b1')\n        .expect('\\u03b1', done);\n    })\n  })\n\n  it('should be .use()able', function (done) {\n    var app = express();\n\n    var calls = [];\n\n    app.use(function (req, res, next) {\n      calls.push('before');\n      next();\n    });\n\n    app.get('/', function (req, res, next) {\n      calls.push('GET /')\n      next();\n    });\n\n    app.use(function (req, res, next) {\n      calls.push('after');\n      res.json(calls)\n    });\n\n    request(app)\n      .get('/')\n      .expect(200, ['before', 'GET /', 'after'], done)\n  })\n\n  describe('when given a regexp', function () {\n    it('should match the pathname only', function (done) {\n      var app = express();\n\n      app.get(/^\\/user\\/[0-9]+$/, function (req, res) {\n        res.end('user');\n      });\n\n      request(app)\n        .get('/user/12?foo=bar')\n        .expect('user', done);\n    })\n\n    it('should populate req.params with the captures', function (done) {\n      var app = express();\n\n      app.get(/^\\/user\\/([0-9]+)\\/(view|edit)?$/, function (req, res) {\n        var id = req.params[0]\n          , op = req.params[1];\n        res.end(op + 'ing user ' + id);\n      });\n\n      request(app)\n        .get('/user/10/edit')\n        .expect('editing user 10', done);\n    })\n\n    if (supportsRegexp('(?<foo>.*)')) {\n      it('should populate req.params with named captures', function (done) {\n        var app = express();\n        var re = new RegExp('^/user/(?<userId>[0-9]+)/(view|edit)?$');\n\n        app.get(re, function (req, res) {\n          var id = req.params.userId\n            , op = req.params[0];\n          res.end(op + 'ing user ' + id);\n        });\n\n        request(app)\n          .get('/user/10/edit')\n          .expect('editing user 10', done);\n      })\n    }\n\n    it('should ensure regexp matches path prefix', function (done) {\n      var app = express()\n      var p = []\n\n      app.use(/\\/api.*/, function (req, res, next) {\n        p.push('a')\n        next()\n      })\n      app.use(/api/, function (req, res, next) {\n        p.push('b')\n        next()\n      })\n      app.use(/\\/test/, function (req, res, next) {\n        p.push('c')\n        next()\n      })\n      app.use(function (req, res) {\n        res.end()\n      })\n\n      request(app)\n        .get('/test/api/1234')\n        .expect(200, function (err) {\n          if (err) return done(err)\n          assert.deepEqual(p, ['c'])\n          done()\n        })\n    })\n  })\n\n  describe('case sensitivity', function () {\n    it('should be disabled by default', function (done) {\n      var app = express();\n\n      app.get('/user', function (req, res) {\n        res.end('tj');\n      });\n\n      request(app)\n        .get('/USER')\n        .expect('tj', done);\n    })\n\n    describe('when \"case sensitive routing\" is enabled', function () {\n      it('should match identical casing', function (done) {\n        var app = express();\n\n        app.enable('case sensitive routing');\n\n        app.get('/uSer', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/uSer')\n          .expect('tj', done);\n      })\n\n      it('should not match otherwise', function (done) {\n        var app = express();\n\n        app.enable('case sensitive routing');\n\n        app.get('/uSer', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user')\n          .expect(404, done);\n      })\n    })\n  })\n\n  describe('params', function () {\n    it('should overwrite existing req.params by default', function (done) {\n      var app = express();\n      var router = new express.Router();\n\n      router.get('/:action', function (req, res) {\n        res.send(req.params);\n      });\n\n      app.use('/user/:user', router);\n\n      request(app)\n        .get('/user/1/get')\n        .expect(200, '{\"action\":\"get\"}', done);\n    })\n\n    it('should allow merging existing req.params', function (done) {\n      var app = express();\n      var router = new express.Router({ mergeParams: true });\n\n      router.get('/:action', function (req, res) {\n        var keys = Object.keys(req.params).sort();\n        res.send(keys.map(function (k) { return [k, req.params[k]] }));\n      });\n\n      app.use('/user/:user', router);\n\n      request(app)\n        .get('/user/tj/get')\n        .expect(200, '[[\"action\",\"get\"],[\"user\",\"tj\"]]', done);\n    })\n\n    it('should use params from router', function (done) {\n      var app = express();\n      var router = new express.Router({ mergeParams: true });\n\n      router.get('/:thing', function (req, res) {\n        var keys = Object.keys(req.params).sort();\n        res.send(keys.map(function (k) { return [k, req.params[k]] }));\n      });\n\n      app.use('/user/:thing', router);\n\n      request(app)\n        .get('/user/tj/get')\n        .expect(200, '[[\"thing\",\"get\"]]', done);\n    })\n\n    it('should merge numeric indices req.params', function (done) {\n      var app = express();\n      var router = new express.Router({ mergeParams: true });\n\n      router.get(/^\\/(.*)\\.(.*)/, function (req, res) {\n        var keys = Object.keys(req.params).sort();\n        res.send(keys.map(function (k) { return [k, req.params[k]] }));\n      });\n\n      app.use(/^\\/user\\/id:(\\d+)/, router);\n\n      request(app)\n        .get('/user/id:10/profile.json')\n        .expect(200, '[[\"0\",\"10\"],[\"1\",\"profile\"],[\"2\",\"json\"]]', done);\n    })\n\n    it('should merge numeric indices req.params when more in parent', function (done) {\n      var app = express();\n      var router = new express.Router({ mergeParams: true });\n\n      router.get(/\\/(.*)/, function (req, res) {\n        var keys = Object.keys(req.params).sort();\n        res.send(keys.map(function (k) { return [k, req.params[k]] }));\n      });\n\n      app.use(/^\\/user\\/id:(\\d+)\\/name:(\\w+)/, router);\n\n      request(app)\n        .get('/user/id:10/name:tj/profile')\n        .expect(200, '[[\"0\",\"10\"],[\"1\",\"tj\"],[\"2\",\"profile\"]]', done);\n    })\n\n    it('should merge numeric indices req.params when parent has same number', function (done) {\n      var app = express();\n      var router = new express.Router({ mergeParams: true });\n\n      router.get(/\\/name:(\\w+)/, function (req, res) {\n        var keys = Object.keys(req.params).sort();\n        res.send(keys.map(function (k) { return [k, req.params[k]] }));\n      });\n\n      app.use(/\\/user\\/id:(\\d+)/, router);\n\n      request(app)\n        .get('/user/id:10/name:tj')\n        .expect(200, '[[\"0\",\"10\"],[\"1\",\"tj\"]]', done);\n    })\n\n    it('should ignore invalid incoming req.params', function (done) {\n      var app = express();\n      var router = new express.Router({ mergeParams: true });\n\n      router.get('/:name', function (req, res) {\n        var keys = Object.keys(req.params).sort();\n        res.send(keys.map(function (k) { return [k, req.params[k]] }));\n      });\n\n      app.use('/user/', function (req, res, next) {\n        req.params = 3; // wat?\n        router(req, res, next);\n      });\n\n      request(app)\n        .get('/user/tj')\n        .expect(200, '[[\"name\",\"tj\"]]', done);\n    })\n\n    it('should restore req.params', function (done) {\n      var app = express();\n      var router = new express.Router({ mergeParams: true });\n\n      router.get(/\\/user:(\\w+)\\//, function (req, res, next) {\n        next();\n      });\n\n      app.use(/\\/user\\/id:(\\d+)/, function (req, res, next) {\n        router(req, res, function (err) {\n          var keys = Object.keys(req.params).sort();\n          res.send(keys.map(function (k) { return [k, req.params[k]] }));\n        });\n      });\n\n      request(app)\n        .get('/user/id:42/user:tj/profile')\n        .expect(200, '[[\"0\",\"42\"]]', done);\n    })\n  })\n\n  describe('trailing slashes', function () {\n    it('should be optional by default', function (done) {\n      var app = express();\n\n      app.get('/user', function (req, res) {\n        res.end('tj');\n      });\n\n      request(app)\n        .get('/user/')\n        .expect('tj', done);\n    })\n\n    describe('when \"strict routing\" is enabled', function () {\n      it('should match trailing slashes', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.get('/user/', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user/')\n          .expect('tj', done);\n      })\n\n      it('should pass-though middleware', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.use(function (req, res, next) {\n          res.setHeader('x-middleware', 'true');\n          next();\n        });\n\n        app.get('/user/', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user/')\n          .expect('x-middleware', 'true')\n          .expect(200, 'tj', done);\n      })\n\n      it('should pass-though mounted middleware', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.use('/user/', function (req, res, next) {\n          res.setHeader('x-middleware', 'true');\n          next();\n        });\n\n        app.get('/user/test/', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user/test/')\n          .expect('x-middleware', 'true')\n          .expect(200, 'tj', done);\n      })\n\n      it('should match no slashes', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.get('/user', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user')\n          .expect('tj', done);\n      })\n\n      it('should match middleware when omitting the trailing slash', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.use('/user/', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user')\n          .expect(200, 'tj', done);\n      })\n\n      it('should match middleware', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.use('/user', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user')\n          .expect(200, 'tj', done);\n      })\n\n      it('should match middleware when adding the trailing slash', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.use('/user', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user/')\n          .expect(200, 'tj', done);\n      })\n\n      it('should fail when omitting the trailing slash', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.get('/user/', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user')\n          .expect(404, done);\n      })\n\n      it('should fail when adding the trailing slash', function (done) {\n        var app = express();\n\n        app.enable('strict routing');\n\n        app.get('/user', function (req, res) {\n          res.end('tj');\n        });\n\n        request(app)\n          .get('/user/')\n          .expect(404, done);\n      })\n    })\n  })\n\n  it('should allow literal \".\"', function (done) {\n    var app = express();\n\n    app.get('/api/users/:from..:to', function (req, res) {\n      var from = req.params.from\n        , to = req.params.to;\n\n      res.end('users from ' + from + ' to ' + to);\n    });\n\n    request(app)\n      .get('/api/users/1..50')\n      .expect('users from 1 to 50', done);\n  })\n\n  describe(':name', function () {\n    it('should denote a capture group', function (done) {\n      var app = express();\n\n      app.get('/user/:user', function (req, res) {\n        res.end(req.params.user);\n      });\n\n      request(app)\n        .get('/user/tj')\n        .expect('tj', done);\n    })\n\n    it('should match a single segment only', function (done) {\n      var app = express();\n\n      app.get('/user/:user', function (req, res) {\n        res.end(req.params.user);\n      });\n\n      request(app)\n        .get('/user/tj/edit')\n        .expect(404, done);\n    })\n\n    it('should allow several capture groups', function (done) {\n      var app = express();\n\n      app.get('/user/:user/:op', function (req, res) {\n        res.end(req.params.op + 'ing ' + req.params.user);\n      });\n\n      request(app)\n        .get('/user/tj/edit')\n        .expect('editing tj', done);\n    })\n\n    it('should work following a partial capture group', function (done) {\n      var app = express();\n      var cb = after(2, done);\n\n      app.get('/user{s}/:user/:op', function (req, res) {\n        res.end(req.params.op + 'ing ' + req.params.user + (req.url.startsWith('/users') ? ' (old)' : ''));\n      });\n\n      request(app)\n        .get('/user/tj/edit')\n        .expect('editing tj', cb);\n\n      request(app)\n        .get('/users/tj/edit')\n        .expect('editing tj (old)', cb);\n    })\n\n    it('should work inside literal parenthesis', function (done) {\n      var app = express();\n\n      app.get('/:user\\\\(:op\\\\)', function (req, res) {\n        res.end(req.params.op + 'ing ' + req.params.user);\n      });\n\n      request(app)\n        .get('/tj(edit)')\n        .expect('editing tj', done);\n    })\n\n    it('should work in array of paths', function (done) {\n      var app = express();\n      var cb = after(2, done);\n\n      app.get(['/user/:user/poke', '/user/:user/pokes'], function (req, res) {\n        res.end('poking ' + req.params.user);\n      });\n\n      request(app)\n        .get('/user/tj/poke')\n        .expect('poking tj', cb);\n\n      request(app)\n        .get('/user/tj/pokes')\n        .expect('poking tj', cb);\n    })\n  })\n\n  describe(':name?', function () {\n    it('should denote an optional capture group', function (done) {\n      var app = express();\n\n      app.get('/user/:user{/:op}', function (req, res) {\n        var op = req.params.op || 'view';\n        res.end(op + 'ing ' + req.params.user);\n      });\n\n      request(app)\n        .get('/user/tj')\n        .expect('viewing tj', done);\n    })\n\n    it('should populate the capture group', function (done) {\n      var app = express();\n\n      app.get('/user/:user{/:op}', function (req, res) {\n        var op = req.params.op || 'view';\n        res.end(op + 'ing ' + req.params.user);\n      });\n\n      request(app)\n        .get('/user/tj/edit')\n        .expect('editing tj', done);\n    })\n  })\n\n  describe(':name*', function () {\n    it('should match one segment', function (done) {\n      var app = express()\n\n      app.get('/user/*user', function (req, res) {\n        res.end(req.params.user[0])\n      })\n\n      request(app)\n        .get('/user/122')\n        .expect('122', done)\n    })\n\n    it('should match many segments', function (done) {\n      var app = express()\n\n      app.get('/user/*user', function (req, res) {\n        res.end(req.params.user.join('/'))\n      })\n\n      request(app)\n        .get('/user/1/2/3/4')\n        .expect('1/2/3/4', done)\n    })\n\n    it('should match zero segments', function (done) {\n      var app = express()\n\n      app.get('/user{/*user}', function (req, res) {\n        res.end(req.params.user)\n      })\n\n      request(app)\n        .get('/user')\n        .expect('', done)\n    })\n  })\n\n  describe(':name+', function () {\n    it('should match one segment', function (done) {\n      var app = express()\n\n      app.get('/user/*user', function (req, res) {\n        res.end(req.params.user[0])\n      })\n\n      request(app)\n        .get('/user/122')\n        .expect(200, '122', done)\n    })\n\n    it('should match many segments', function (done) {\n      var app = express()\n\n      app.get('/user/*user', function (req, res) {\n        res.end(req.params.user.join('/'))\n      })\n\n      request(app)\n        .get('/user/1/2/3/4')\n        .expect(200, '1/2/3/4', done)\n    })\n\n    it('should not match zero segments', function (done) {\n      var app = express()\n\n      app.get('/user/*user', function (req, res) {\n        res.end(req.params.user)\n      })\n\n      request(app)\n        .get('/user')\n        .expect(404, done)\n    })\n  })\n\n  describe('.:name', function () {\n    it('should denote a format', function (done) {\n      var app = express();\n      var cb = after(2, done)\n\n      app.get('/:name.:format', function (req, res) {\n        res.end(req.params.name + ' as ' + req.params.format);\n      });\n\n      request(app)\n        .get('/foo.json')\n        .expect(200, 'foo as json', cb)\n\n      request(app)\n        .get('/foo')\n        .expect(404, cb)\n    })\n  })\n\n  describe('.:name?', function () {\n    it('should denote an optional format', function (done) {\n      var app = express();\n      var cb = after(2, done)\n\n      app.get('/:name{.:format}', function (req, res) {\n        res.end(req.params.name + ' as ' + (req.params.format || 'html'));\n      });\n\n      request(app)\n        .get('/foo')\n        .expect(200, 'foo as html', cb)\n\n      request(app)\n        .get('/foo.json')\n        .expect(200, 'foo as json', cb)\n    })\n  })\n\n  describe('when next() is called', function () {\n    it('should continue lookup', function (done) {\n      var app = express()\n        , calls = [];\n\n      app.get('/foo{/:bar}', function (req, res, next) {\n        calls.push('/foo/:bar?');\n        next();\n      });\n\n      app.get('/bar', function () {\n        assert(0);\n      });\n\n      app.get('/foo', function (req, res, next) {\n        calls.push('/foo');\n        next();\n      });\n\n      app.get('/foo', function (req, res) {\n        calls.push('/foo 2');\n        res.json(calls)\n      });\n\n      request(app)\n        .get('/foo')\n        .expect(200, ['/foo/:bar?', '/foo', '/foo 2'], done)\n    })\n  })\n\n  describe('when next(\"route\") is called', function () {\n    it('should jump to next route', function (done) {\n      var app = express()\n\n      function fn(req, res, next) {\n        res.set('X-Hit', '1')\n        next('route')\n      }\n\n      app.get('/foo', fn, function (req, res) {\n        res.end('failure')\n      });\n\n      app.get('/foo', function (req, res) {\n        res.end('success')\n      })\n\n      request(app)\n        .get('/foo')\n        .expect('X-Hit', '1')\n        .expect(200, 'success', done)\n    })\n  })\n\n  describe('when next(\"router\") is called', function () {\n    it('should jump out of router', function (done) {\n      var app = express()\n      var router = express.Router()\n\n      function fn(req, res, next) {\n        res.set('X-Hit', '1')\n        next('router')\n      }\n\n      router.get('/foo', fn, function (req, res) {\n        res.end('failure')\n      })\n\n      router.get('/foo', function (req, res) {\n        res.end('failure')\n      })\n\n      app.use(router)\n\n      app.get('/foo', function (req, res) {\n        res.end('success')\n      })\n\n      request(app)\n        .get('/foo')\n        .expect('X-Hit', '1')\n        .expect(200, 'success', done)\n    })\n  })\n\n  describe('when next(err) is called', function () {\n    it('should break out of app.router', function (done) {\n      var app = express()\n        , calls = [];\n\n      app.get('/foo{/:bar}', function (req, res, next) {\n        calls.push('/foo/:bar?');\n        next();\n      });\n\n      app.get('/bar', function () {\n        assert(0);\n      });\n\n      app.get('/foo', function (req, res, next) {\n        calls.push('/foo');\n        next(new Error('fail'));\n      });\n\n      app.get('/foo', function () {\n        assert(0);\n      });\n\n      app.use(function (err, req, res, next) {\n        res.json({\n          calls: calls,\n          error: err.message\n        })\n      })\n\n      request(app)\n        .get('/foo')\n        .expect(200, { calls: ['/foo/:bar?', '/foo'], error: 'fail' }, done)\n    })\n\n    it('should call handler in same route, if exists', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        next(new Error('boom!'));\n      }\n\n      function fn2(req, res, next) {\n        res.send('foo here');\n      }\n\n      function fn3(err, req, res, next) {\n        res.send('route go ' + err.message);\n      }\n\n      app.get('/foo', fn1, fn2, fn3);\n\n      app.use(function (err, req, res, next) {\n        res.end('error!');\n      })\n\n      request(app)\n        .get('/foo')\n        .expect('route go boom!', done)\n    })\n  })\n\n  describe('promise support', function () {\n    it('should pass rejected promise value', function (done) {\n      var app = express()\n      var router = new express.Router()\n\n      router.use(function createError(req, res, next) {\n        return Promise.reject(new Error('boom!'))\n      })\n\n      router.use(function sawError(err, req, res, next) {\n        res.send('saw ' + err.name + ': ' + err.message)\n      })\n\n      app.use(router)\n\n      request(app)\n        .get('/')\n        .expect(200, 'saw Error: boom!', done)\n    })\n\n    it('should pass rejected promise without value', function (done) {\n      var app = express()\n      var router = new express.Router()\n\n      router.use(function createError(req, res, next) {\n        return Promise.reject()\n      })\n\n      router.use(function sawError(err, req, res, next) {\n        res.send('saw ' + err.name + ': ' + err.message)\n      })\n\n      app.use(router)\n\n      request(app)\n        .get('/')\n        .expect(200, 'saw Error: Rejected promise', done)\n    })\n\n    it('should ignore resolved promise', function (done) {\n      var app = express()\n      var router = new express.Router()\n\n      router.use(function createError(req, res, next) {\n        res.send('saw GET /foo')\n        return Promise.resolve('foo')\n      })\n\n      router.use(function () {\n        done(new Error('Unexpected middleware invoke'))\n      })\n\n      app.use(router)\n\n      request(app)\n        .get('/foo')\n        .expect(200, 'saw GET /foo', done)\n    })\n\n    describe('error handling', function () {\n      it('should pass rejected promise value', function (done) {\n        var app = express()\n        var router = new express.Router()\n\n        router.use(function createError(req, res, next) {\n          return Promise.reject(new Error('boom!'))\n        })\n\n        router.use(function handleError(err, req, res, next) {\n          return Promise.reject(new Error('caught: ' + err.message))\n        })\n\n        router.use(function sawError(err, req, res, next) {\n          res.send('saw ' + err.name + ': ' + err.message)\n        })\n\n        app.use(router)\n\n        request(app)\n          .get('/')\n          .expect(200, 'saw Error: caught: boom!', done)\n      })\n\n      it('should pass rejected promise without value', function (done) {\n        var app = express()\n        var router = new express.Router()\n\n        router.use(function createError(req, res, next) {\n          return Promise.reject()\n        })\n\n        router.use(function handleError(err, req, res, next) {\n          return Promise.reject(new Error('caught: ' + err.message))\n        })\n\n        router.use(function sawError(err, req, res, next) {\n          res.send('saw ' + err.name + ': ' + err.message)\n        })\n\n        app.use(router)\n\n        request(app)\n          .get('/')\n          .expect(200, 'saw Error: caught: Rejected promise', done)\n      })\n\n      it('should ignore resolved promise', function (done) {\n        var app = express()\n        var router = new express.Router()\n\n        router.use(function createError(req, res, next) {\n          return Promise.reject(new Error('boom!'))\n        })\n\n        router.use(function handleError(err, req, res, next) {\n          res.send('saw ' + err.name + ': ' + err.message)\n          return Promise.resolve('foo')\n        })\n\n        router.use(function () {\n          done(new Error('Unexpected middleware invoke'))\n        })\n\n        app.use(router)\n\n        request(app)\n          .get('/foo')\n          .expect(200, 'saw Error: boom!', done)\n      })\n    })\n  })\n\n  it('should allow rewriting of the url', function (done) {\n    var app = express();\n\n    app.get('/account/edit', function (req, res, next) {\n      req.user = { id: 12 }; // faux authenticated user\n      req.url = '/user/' + req.user.id + '/edit';\n      next();\n    });\n\n    app.get('/user/:id/edit', function (req, res) {\n      res.send('editing user ' + req.params.id);\n    });\n\n    request(app)\n      .get('/account/edit')\n      .expect('editing user 12', done);\n  })\n\n  it('should run in order added', function (done) {\n    var app = express();\n    var path = [];\n\n    app.get('/*path', function (req, res, next) {\n      path.push(0);\n      next();\n    });\n\n    app.get('/user/:id', function (req, res, next) {\n      path.push(1);\n      next();\n    });\n\n    app.use(function (req, res, next) {\n      path.push(2);\n      next();\n    });\n\n    app.all('/user/:id', function (req, res, next) {\n      path.push(3);\n      next();\n    });\n\n    app.get('/*splat', function (req, res, next) {\n      path.push(4);\n      next();\n    });\n\n    app.use(function (req, res, next) {\n      path.push(5);\n      res.end(path.join(','))\n    });\n\n    request(app)\n      .get('/user/1')\n      .expect(200, '0,1,2,3,4,5', done);\n  })\n\n  it('should be chainable', function () {\n    var app = express();\n    assert.strictEqual(app.get('/', function () { }), app)\n  })\n\n  it('should not use disposed router/middleware', function (done) {\n    // more context: https://github.com/expressjs/express/issues/5743#issuecomment-2277148412\n\n    var app = express();\n    var router = new express.Router();\n\n    router.use(function (req, res, next) {\n      res.setHeader('old', 'foo');\n      next();\n    });\n\n    app.use(function (req, res, next) {\n      return router.handle(req, res, next);\n    });\n\n    app.get('/', function (req, res, next) {\n      res.send('yee');\n      next();\n    });\n\n    request(app)\n      .get('/')\n      .expect('old', 'foo')\n      .expect(function (res) {\n        if (typeof res.headers['new'] !== 'undefined') {\n          throw new Error('`new` header should not be present');\n        }\n      })\n      .expect(200, 'yee', function (err, res) {\n        if (err) return done(err);\n\n        router = new express.Router();\n\n        router.use(function (req, res, next) {\n          res.setHeader('new', 'bar');\n          next();\n        });\n\n        request(app)\n          .get('/')\n          .expect('new', 'bar')\n          .expect(function (res) {\n            if (typeof res.headers['old'] !== 'undefined') {\n              throw new Error('`old` header should not be present');\n            }\n          })\n          .expect(200, 'yee', done);\n      });\n  })\n})\n\nfunction supportsRegexp(source) {\n  try {\n    new RegExp(source)\n    return true\n  } catch (e) {\n    return false\n  }\n}\n"
  },
  {
    "path": "test/app.routes.error.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('app', function(){\n  describe('.VERB()', function(){\n    it('should not get invoked without error handler on error', function(done) {\n      var app = express();\n\n      app.use(function(req, res, next){\n        next(new Error('boom!'))\n      });\n\n      app.get('/bar', function(req, res){\n        res.send('hello, world!');\n      });\n\n      request(app)\n      .post('/bar')\n      .expect(500, /Error: boom!/, done);\n    });\n\n    it('should only call an error handling routing callback when an error is propagated', function(done){\n      var app = express();\n\n      var a = false;\n      var b = false;\n      var c = false;\n      var d = false;\n\n      app.get('/', function(req, res, next){\n        next(new Error('fabricated error'));\n      }, function(req, res, next) {\n        a = true;\n        next();\n      }, function(err, req, res, next){\n        b = true;\n        assert.strictEqual(err.message, 'fabricated error')\n        next(err);\n      }, function(err, req, res, next){\n        c = true;\n        assert.strictEqual(err.message, 'fabricated error')\n        next();\n      }, function(err, req, res, next){\n        d = true;\n        next();\n      }, function(req, res){\n        assert.ok(!a)\n        assert.ok(b)\n        assert.ok(c)\n        assert.ok(!d)\n        res.sendStatus(204);\n      });\n\n      request(app)\n      .get('/')\n      .expect(204, done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/app.use.js",
    "content": "'use strict'\n\nvar after = require('after');\nvar assert = require('node:assert')\nvar express = require('..');\nvar request = require('supertest');\n\ndescribe('app', function(){\n  it('should emit \"mount\" when mounted', function(done){\n    var blog = express()\n      , app = express();\n\n    blog.on('mount', function(arg){\n      assert.strictEqual(arg, app)\n      done();\n    });\n\n    app.use(blog);\n  })\n\n  describe('.use(app)', function(){\n    it('should mount the app', function(done){\n      var blog = express()\n        , app = express();\n\n      blog.get('/blog', function(req, res){\n        res.end('blog');\n      });\n\n      app.use(blog);\n\n      request(app)\n      .get('/blog')\n      .expect('blog', done);\n    })\n\n    it('should support mount-points', function(done){\n      var blog = express()\n        , forum = express()\n        , app = express();\n      var cb = after(2, done)\n\n      blog.get('/', function(req, res){\n        res.end('blog');\n      });\n\n      forum.get('/', function(req, res){\n        res.end('forum');\n      });\n\n      app.use('/blog', blog);\n      app.use('/forum', forum);\n\n      request(app)\n        .get('/blog')\n        .expect(200, 'blog', cb)\n\n      request(app)\n        .get('/forum')\n        .expect(200, 'forum', cb)\n    })\n\n    it('should set the child\\'s .parent', function(){\n      var blog = express()\n        , app = express();\n\n      app.use('/blog', blog);\n      assert.strictEqual(blog.parent, app)\n    })\n\n    it('should support dynamic routes', function(done){\n      var blog = express()\n        , app = express();\n\n      blog.get('/', function(req, res){\n        res.end('success');\n      });\n\n      app.use('/post/:article', blog);\n\n      request(app)\n      .get('/post/once-upon-a-time')\n      .expect('success', done);\n    })\n\n    it('should support mounted app anywhere', function(done){\n      var cb = after(3, done);\n      var blog = express()\n        , other = express()\n        , app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      blog.get('/', function(req, res){\n        res.end('success');\n      });\n\n      blog.once('mount', function (parent) {\n        assert.strictEqual(parent, app)\n        cb();\n      });\n      other.once('mount', function (parent) {\n        assert.strictEqual(parent, app)\n        cb();\n      });\n\n      app.use('/post/:article', fn1, other, fn2, blog);\n\n      request(app)\n      .get('/post/once-upon-a-time')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('success', cb);\n    })\n  })\n\n  describe('.use(middleware)', function(){\n    it('should accept multiple arguments', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      app.use(fn1, fn2, function fn3(req, res) {\n        res.setHeader('x-fn-3', 'hit');\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, done);\n    })\n\n    it('should invoke middleware for all requests', function (done) {\n      var app = express();\n      var cb = after(3, done);\n\n      app.use(function (req, res) {\n        res.send('saw ' + req.method + ' ' + req.url);\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, 'saw GET /', cb);\n\n      request(app)\n      .options('/')\n      .expect(200, 'saw OPTIONS /', cb);\n\n      request(app)\n      .post('/foo')\n      .expect(200, 'saw POST /foo', cb);\n    })\n\n    it('should accept array of middleware', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      function fn3(req, res, next) {\n        res.setHeader('x-fn-3', 'hit');\n        res.end();\n      }\n\n      app.use([fn1, fn2, fn3]);\n\n      request(app)\n      .get('/')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, done);\n    })\n\n    it('should accept multiple arrays of middleware', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      function fn3(req, res, next) {\n        res.setHeader('x-fn-3', 'hit');\n        res.end();\n      }\n\n      app.use([fn1, fn2], [fn3]);\n\n      request(app)\n      .get('/')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, done);\n    })\n\n    it('should accept nested arrays of middleware', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      function fn3(req, res, next) {\n        res.setHeader('x-fn-3', 'hit');\n        res.end();\n      }\n\n      app.use([[fn1], fn2], [fn3]);\n\n      request(app)\n      .get('/')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, done);\n    })\n  })\n\n  describe('.use(path, middleware)', function(){\n    it('should require middleware', function () {\n      var app = express()\n      assert.throws(function () { app.use('/') }, 'TypeError: app.use() requires a middleware function')\n    })\n\n    it('should reject string as middleware', function () {\n      var app = express()\n      assert.throws(function () { app.use('/', 'foo') }, /argument handler must be a function/)\n    })\n\n    it('should reject number as middleware', function () {\n      var app = express()\n      assert.throws(function () { app.use('/', 42) }, /argument handler must be a function/)\n    })\n\n    it('should reject null as middleware', function () {\n      var app = express()\n      assert.throws(function () { app.use('/', null) }, /argument handler must be a function/)\n    })\n\n    it('should reject Date as middleware', function () {\n      var app = express()\n      assert.throws(function () { app.use('/', new Date()) }, /argument handler must be a function/)\n    })\n\n    it('should strip path from req.url', function (done) {\n      var app = express();\n\n      app.use('/foo', function (req, res) {\n        res.send('saw ' + req.method + ' ' + req.url);\n      });\n\n      request(app)\n      .get('/foo/bar')\n      .expect(200, 'saw GET /bar', done);\n    })\n\n    it('should accept multiple arguments', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      app.use('/foo', fn1, fn2, function fn3(req, res) {\n        res.setHeader('x-fn-3', 'hit');\n        res.end();\n      });\n\n      request(app)\n      .get('/foo')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, done);\n    })\n\n    it('should invoke middleware for all requests starting with path', function (done) {\n      var app = express();\n      var cb = after(3, done);\n\n      app.use('/foo', function (req, res) {\n        res.send('saw ' + req.method + ' ' + req.url);\n      });\n\n      request(app)\n      .get('/')\n      .expect(404, cb);\n\n      request(app)\n      .post('/foo')\n      .expect(200, 'saw POST /', cb);\n\n      request(app)\n      .post('/foo/bar')\n      .expect(200, 'saw POST /bar', cb);\n    })\n\n    it('should work if path has trailing slash', function (done) {\n      var app = express();\n      var cb = after(3, done);\n\n      app.use('/foo/', function (req, res) {\n        res.send('saw ' + req.method + ' ' + req.url);\n      });\n\n      request(app)\n      .get('/')\n      .expect(404, cb);\n\n      request(app)\n      .post('/foo')\n      .expect(200, 'saw POST /', cb);\n\n      request(app)\n      .post('/foo/bar')\n      .expect(200, 'saw POST /bar', cb);\n    })\n\n    it('should accept array of middleware', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      function fn3(req, res, next) {\n        res.setHeader('x-fn-3', 'hit');\n        res.end();\n      }\n\n      app.use('/foo', [fn1, fn2, fn3]);\n\n      request(app)\n      .get('/foo')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, done);\n    })\n\n    it('should accept multiple arrays of middleware', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      function fn3(req, res, next) {\n        res.setHeader('x-fn-3', 'hit');\n        res.end();\n      }\n\n      app.use('/foo', [fn1, fn2], [fn3]);\n\n      request(app)\n      .get('/foo')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, done);\n    })\n\n    it('should accept nested arrays of middleware', function (done) {\n      var app = express();\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      function fn3(req, res, next) {\n        res.setHeader('x-fn-3', 'hit');\n        res.end();\n      }\n\n      app.use('/foo', [fn1, [fn2]], [fn3]);\n\n      request(app)\n      .get('/foo')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, done);\n    })\n\n    it('should support array of paths', function (done) {\n      var app = express();\n      var cb = after(3, done);\n\n      app.use(['/foo/', '/bar'], function (req, res) {\n        res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);\n      });\n\n      request(app)\n      .get('/')\n      .expect(404, cb);\n\n      request(app)\n      .get('/foo')\n      .expect(200, 'saw GET / through /foo', cb);\n\n      request(app)\n      .get('/bar')\n      .expect(200, 'saw GET / through /bar', cb);\n    })\n\n    it('should support array of paths with middleware array', function (done) {\n      var app = express();\n      var cb = after(2, done);\n\n      function fn1(req, res, next) {\n        res.setHeader('x-fn-1', 'hit');\n        next();\n      }\n\n      function fn2(req, res, next) {\n        res.setHeader('x-fn-2', 'hit');\n        next();\n      }\n\n      function fn3(req, res, next) {\n        res.setHeader('x-fn-3', 'hit');\n        res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);\n      }\n\n      app.use(['/foo/', '/bar'], [[fn1], fn2], [fn3]);\n\n      request(app)\n      .get('/foo')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, 'saw GET / through /foo', cb);\n\n      request(app)\n      .get('/bar')\n      .expect('x-fn-1', 'hit')\n      .expect('x-fn-2', 'hit')\n      .expect('x-fn-3', 'hit')\n      .expect(200, 'saw GET / through /bar', cb);\n    })\n\n    it('should support regexp path', function (done) {\n      var app = express();\n      var cb = after(4, done);\n\n      app.use(/^\\/[a-z]oo/, function (req, res) {\n        res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);\n      });\n\n      request(app)\n      .get('/')\n      .expect(404, cb);\n\n      request(app)\n      .get('/foo')\n      .expect(200, 'saw GET / through /foo', cb);\n\n      request(app)\n      .get('/zoo/bear')\n      .expect(200, 'saw GET /bear through /zoo/bear', cb);\n\n      request(app)\n      .get('/get/zoo')\n      .expect(404, cb);\n    })\n\n    it('should support empty string path', function (done) {\n      var app = express();\n\n      app.use('', function (req, res) {\n        res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, 'saw GET / through /', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/config.js",
    "content": "'use strict'\n\nvar assert = require('node:assert');\nvar express = require('..');\n\ndescribe('config', function () {\n  describe('.set()', function () {\n    it('should set a value', function () {\n      var app = express();\n      app.set('foo', 'bar');\n      assert.equal(app.get('foo'), 'bar');\n    })\n\n    it('should set prototype values', function () {\n      var app = express()\n      app.set('hasOwnProperty', 42)\n      assert.strictEqual(app.get('hasOwnProperty'), 42)\n    })\n\n    it('should return the app', function () {\n      var app = express();\n      assert.equal(app.set('foo', 'bar'), app);\n    })\n\n    it('should return the app when undefined', function () {\n      var app = express();\n      assert.equal(app.set('foo', undefined), app);\n    })\n\n    it('should return set value', function () {\n      var app = express()\n      app.set('foo', 'bar')\n      assert.strictEqual(app.set('foo'), 'bar')\n    })\n\n    it('should return undefined for prototype values', function () {\n      var app = express()\n      assert.strictEqual(app.set('hasOwnProperty'), undefined)\n    })\n\n    describe('\"etag\"', function(){\n      it('should throw on bad value', function(){\n        var app = express();\n        assert.throws(app.set.bind(app, 'etag', 42), /unknown value/);\n      })\n\n      it('should set \"etag fn\"', function(){\n        var app = express()\n        var fn = function(){}\n        app.set('etag', fn)\n        assert.equal(app.get('etag fn'), fn)\n      })\n    })\n\n    describe('\"trust proxy\"', function(){\n      it('should set \"trust proxy fn\"', function(){\n        var app = express()\n        var fn = function(){}\n        app.set('trust proxy', fn)\n        assert.equal(app.get('trust proxy fn'), fn)\n      })\n    })\n  })\n\n  describe('.get()', function(){\n    it('should return undefined when unset', function(){\n      var app = express();\n      assert.strictEqual(app.get('foo'), undefined);\n    })\n\n    it('should return undefined for prototype values', function () {\n      var app = express()\n      assert.strictEqual(app.get('hasOwnProperty'), undefined)\n    })\n\n    it('should otherwise return the value', function(){\n      var app = express();\n      app.set('foo', 'bar');\n      assert.equal(app.get('foo'), 'bar');\n    })\n\n    describe('when mounted', function(){\n      it('should default to the parent app', function(){\n        var app = express();\n        var blog = express();\n\n        app.set('title', 'Express');\n        app.use(blog);\n        assert.equal(blog.get('title'), 'Express');\n      })\n\n      it('should given precedence to the child', function(){\n        var app = express();\n        var blog = express();\n\n        app.use(blog);\n        app.set('title', 'Express');\n        blog.set('title', 'Some Blog');\n\n        assert.equal(blog.get('title'), 'Some Blog');\n      })\n\n      it('should inherit \"trust proxy\" setting', function () {\n        var app = express();\n        var blog = express();\n\n        function fn() { return false }\n\n        app.set('trust proxy', fn);\n        assert.equal(app.get('trust proxy'), fn);\n        assert.equal(app.get('trust proxy fn'), fn);\n\n        app.use(blog);\n\n        assert.equal(blog.get('trust proxy'), fn);\n        assert.equal(blog.get('trust proxy fn'), fn);\n      })\n\n      it('should prefer child \"trust proxy\" setting', function () {\n        var app = express();\n        var blog = express();\n\n        function fn1() { return false }\n        function fn2() { return true }\n\n        app.set('trust proxy', fn1);\n        assert.equal(app.get('trust proxy'), fn1);\n        assert.equal(app.get('trust proxy fn'), fn1);\n\n        blog.set('trust proxy', fn2);\n        assert.equal(blog.get('trust proxy'), fn2);\n        assert.equal(blog.get('trust proxy fn'), fn2);\n\n        app.use(blog);\n\n        assert.equal(app.get('trust proxy'), fn1);\n        assert.equal(app.get('trust proxy fn'), fn1);\n        assert.equal(blog.get('trust proxy'), fn2);\n        assert.equal(blog.get('trust proxy fn'), fn2);\n      })\n    })\n  })\n\n  describe('.enable()', function(){\n    it('should set the value to true', function(){\n      var app = express();\n      assert.equal(app.enable('tobi'), app);\n      assert.strictEqual(app.get('tobi'), true);\n    })\n\n    it('should set prototype values', function () {\n      var app = express()\n      app.enable('hasOwnProperty')\n      assert.strictEqual(app.get('hasOwnProperty'), true)\n    })\n  })\n\n  describe('.disable()', function(){\n    it('should set the value to false', function(){\n      var app = express();\n      assert.equal(app.disable('tobi'), app);\n      assert.strictEqual(app.get('tobi'), false);\n    })\n\n    it('should set prototype values', function () {\n      var app = express()\n      app.disable('hasOwnProperty')\n      assert.strictEqual(app.get('hasOwnProperty'), false)\n    })\n  })\n\n  describe('.enabled()', function(){\n    it('should default to false', function(){\n      var app = express();\n      assert.strictEqual(app.enabled('foo'), false);\n    })\n\n    it('should return true when set', function(){\n      var app = express();\n      app.set('foo', 'bar');\n      assert.strictEqual(app.enabled('foo'), true);\n    })\n\n    it('should default to false for prototype values', function () {\n      var app = express()\n      assert.strictEqual(app.enabled('hasOwnProperty'), false)\n    })\n  })\n\n  describe('.disabled()', function(){\n    it('should default to true', function(){\n      var app = express();\n      assert.strictEqual(app.disabled('foo'), true);\n    })\n\n    it('should return false when set', function(){\n      var app = express();\n      app.set('foo', 'bar');\n      assert.strictEqual(app.disabled('foo'), false);\n    })\n\n    it('should default to true for prototype values', function () {\n      var app = express()\n      assert.strictEqual(app.disabled('hasOwnProperty'), true)\n    })\n  })\n})\n"
  },
  {
    "path": "test/exports.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('../');\nvar request = require('supertest');\n\ndescribe('exports', function(){\n  it('should expose Router', function(){\n    assert.strictEqual(typeof express.Router, 'function')\n  })\n\n  it('should expose json middleware', function () {\n    assert.equal(typeof express.json, 'function')\n    assert.equal(express.json.length, 1)\n  })\n\n  it('should expose raw middleware', function () {\n    assert.equal(typeof express.raw, 'function')\n    assert.equal(express.raw.length, 1)\n  })\n\n  it('should expose static middleware', function () {\n    assert.equal(typeof express.static, 'function')\n    assert.equal(express.static.length, 2)\n  })\n\n  it('should expose text middleware', function () {\n    assert.equal(typeof express.text, 'function')\n    assert.equal(express.text.length, 1)\n  })\n\n  it('should expose urlencoded middleware', function () {\n    assert.equal(typeof express.urlencoded, 'function')\n    assert.equal(express.urlencoded.length, 1)\n  })\n\n  it('should expose the application prototype', function(){\n    assert.strictEqual(typeof express.application, 'object')\n    assert.strictEqual(typeof express.application.set, 'function')\n  })\n\n  it('should expose the request prototype', function(){\n    assert.strictEqual(typeof express.request, 'object')\n    assert.strictEqual(typeof express.request.accepts, 'function')\n  })\n\n  it('should expose the response prototype', function(){\n    assert.strictEqual(typeof express.response, 'object')\n    assert.strictEqual(typeof express.response.send, 'function')\n  })\n\n  it('should permit modifying the .application prototype', function(){\n    express.application.foo = function(){ return 'bar'; };\n    assert.strictEqual(express().foo(), 'bar')\n  })\n\n  it('should permit modifying the .request prototype', function(done){\n    express.request.foo = function(){ return 'bar'; };\n    var app = express();\n\n    app.use(function(req, res, next){\n      res.end(req.foo());\n    });\n\n    request(app)\n    .get('/')\n    .expect('bar', done);\n  })\n\n  it('should permit modifying the .response prototype', function(done){\n    express.response.foo = function(){ this.send('bar'); };\n    var app = express();\n\n    app.use(function(req, res, next){\n      res.foo();\n    });\n\n    request(app)\n    .get('/')\n    .expect('bar', done);\n  })\n})\n"
  },
  {
    "path": "test/express.json.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage\nconst { Buffer } = require('node:buffer');\n\nvar express = require('..')\nvar request = require('supertest')\n\ndescribe('express.json()', function () {\n  it('should parse JSON', function (done) {\n    request(createApp())\n      .post('/')\n      .set('Content-Type', 'application/json')\n      .send('{\"user\":\"tobi\"}')\n      .expect(200, '{\"user\":\"tobi\"}', done)\n  })\n\n  it('should handle Content-Length: 0', function (done) {\n    request(createApp())\n      .post('/')\n      .set('Content-Type', 'application/json')\n      .set('Content-Length', '0')\n      .expect(200, '{}', done)\n  })\n\n  it('should handle empty message-body', function (done) {\n    request(createApp())\n      .post('/')\n      .set('Content-Type', 'application/json')\n      .set('Transfer-Encoding', 'chunked')\n      .expect(200, '{}', done)\n  })\n\n  it('should handle no message-body', function (done) {\n    request(createApp())\n      .post('/')\n      .set('Content-Type', 'application/json')\n      .unset('Transfer-Encoding')\n      .expect(200, '{}', done)\n  })\n\n  // The old node error message modification in body parser is catching this\n  it('should 400 when only whitespace', function (done) {\n    request(createApp())\n      .post('/')\n      .set('Content-Type', 'application/json')\n      .send('  \\n')\n      .expect(400, '[entity.parse.failed] ' + parseError(' \\n'), done)\n  })\n\n  it('should 400 when invalid content-length', function (done) {\n    var app = express()\n\n    app.use(function (req, res, next) {\n      req.headers['content-length'] = '20' // bad length\n      next()\n    })\n\n    app.use(express.json())\n\n    app.post('/', function (req, res) {\n      res.json(req.body)\n    })\n\n    request(app)\n      .post('/')\n      .set('Content-Type', 'application/json')\n      .send('{\"str\":')\n      .expect(400, /content length/, done)\n  })\n\n  it('should handle duplicated middleware', function (done) {\n    var app = express()\n\n    app.use(express.json())\n    app.use(express.json())\n\n    app.post('/', function (req, res) {\n      res.json(req.body)\n    })\n\n    request(app)\n      .post('/')\n      .set('Content-Type', 'application/json')\n      .send('{\"user\":\"tobi\"}')\n      .expect(200, '{\"user\":\"tobi\"}', done)\n  })\n\n  describe('when JSON is invalid', function () {\n    before(function () {\n      this.app = createApp()\n    })\n\n    it('should 400 for bad token', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('{:')\n        .expect(400, '[entity.parse.failed] ' + parseError('{:'), done)\n    })\n\n    it('should 400 for incomplete', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('{\"user\"')\n        .expect(400, '[entity.parse.failed] ' + parseError('{\"user\"'), done)\n    })\n\n    it('should include original body on error object', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .set('X-Error-Property', 'body')\n        .send(' {\"user\"')\n        .expect(400, ' {\"user\"', done)\n    })\n  })\n\n  describe('with limit option', function () {\n    it('should 413 when over limit with Content-Length', function (done) {\n      var buf = Buffer.alloc(1024, '.')\n      request(createApp({ limit: '1kb' }))\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .set('Content-Length', '1034')\n        .send(JSON.stringify({ str: buf.toString() }))\n        .expect(413, '[entity.too.large] request entity too large', done)\n    })\n\n    it('should 413 when over limit with chunked encoding', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var buf = Buffer.alloc(1024, '.')\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/json')\n      test.set('Transfer-Encoding', 'chunked')\n      test.write('{\"str\":')\n      test.write('\"' + buf.toString() + '\"}')\n      test.expect(413, done)\n    })\n\n    it('should 413 when inflated body over limit', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex'))\n      test.expect(413, done)\n    })\n\n    it('should accept number of bytes', function (done) {\n      var buf = Buffer.alloc(1024, '.')\n      request(createApp({ limit: 1024 }))\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send(JSON.stringify({ str: buf.toString() }))\n        .expect(413, done)\n    })\n\n    it('should not change when options altered', function (done) {\n      var buf = Buffer.alloc(1024, '.')\n      var options = { limit: '1kb' }\n      var app = createApp(options)\n\n      options.limit = '100kb'\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send(JSON.stringify({ str: buf.toString() }))\n        .expect(413, done)\n    })\n\n    it('should not hang response', function (done) {\n      var buf = Buffer.alloc(10240, '.')\n      var app = createApp({ limit: '8kb' })\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/json')\n      test.write(buf)\n      test.write(buf)\n      test.write(buf)\n      test.expect(413, done)\n    })\n\n    it('should not error when inflating', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex'))\n      test.expect(413, done)\n    })\n  })\n\n  describe('with inflate option', function () {\n    describe('when false', function () {\n      before(function () {\n        this.app = createApp({ inflate: false })\n      })\n\n      it('should not accept content-encoding', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Encoding', 'gzip')\n        test.set('Content-Type', 'application/json')\n        test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))\n        test.expect(415, '[encoding.unsupported] content encoding unsupported', done)\n      })\n    })\n\n    describe('when true', function () {\n      before(function () {\n        this.app = createApp({ inflate: true })\n      })\n\n      it('should accept content-encoding', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Encoding', 'gzip')\n        test.set('Content-Type', 'application/json')\n        test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))\n        test.expect(200, '{\"name\":\"论\"}', done)\n      })\n    })\n  })\n\n  describe('with strict option', function () {\n    describe('when undefined', function () {\n      before(function () {\n        this.app = createApp()\n      })\n\n      it('should 400 on primitives', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/json')\n          .send('true')\n          .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)\n      })\n    })\n\n    describe('when false', function () {\n      before(function () {\n        this.app = createApp({ strict: false })\n      })\n\n      it('should parse primitives', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/json')\n          .send('true')\n          .expect(200, 'true', done)\n      })\n    })\n\n    describe('when true', function () {\n      before(function () {\n        this.app = createApp({ strict: true })\n      })\n\n      it('should not parse primitives', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/json')\n          .send('true')\n          .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)\n      })\n\n      it('should not parse primitives with leading whitespaces', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/json')\n          .send('    true')\n          .expect(400, '[entity.parse.failed] ' + parseError('    #rue').replace(/#/g, 't'), done)\n      })\n\n      it('should allow leading whitespaces in JSON', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/json')\n          .send('   { \"user\": \"tobi\" }')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should include correct message in stack trace', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/json')\n          .set('X-Error-Property', 'stack')\n          .send('true')\n          .expect(400)\n          .expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't')))\n          .end(done)\n      })\n    })\n  })\n\n  describe('with type option', function () {\n    describe('when \"application/vnd.api+json\"', function () {\n      before(function () {\n        this.app = createApp({ type: 'application/vnd.api+json' })\n      })\n\n      it('should parse JSON for custom type', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/vnd.api+json')\n          .send('{\"user\":\"tobi\"}')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should ignore standard type', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/json')\n          .send('{\"user\":\"tobi\"}')\n          .expect(200, '', done)\n      })\n    })\n\n    describe('when [\"application/json\", \"application/vnd.api+json\"]', function () {\n      before(function () {\n        this.app = createApp({\n          type: ['application/json', 'application/vnd.api+json']\n        })\n      })\n\n      it('should parse JSON for \"application/json\"', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/json')\n          .send('{\"user\":\"tobi\"}')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should parse JSON for \"application/vnd.api+json\"', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/vnd.api+json')\n          .send('{\"user\":\"tobi\"}')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should ignore \"application/x-json\"', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-json')\n          .send('{\"user\":\"tobi\"}')\n          .expect(200, '', done)\n      })\n    })\n\n    describe('when a function', function () {\n      it('should parse when truthy value returned', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          return req.headers['content-type'] === 'application/vnd.api+json'\n        }\n\n        request(app)\n          .post('/')\n          .set('Content-Type', 'application/vnd.api+json')\n          .send('{\"user\":\"tobi\"}')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should work without content-type', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          return true\n        }\n\n        var test = request(app).post('/')\n        test.write('{\"user\":\"tobi\"}')\n        test.expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should not invoke without a body', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          throw new Error('oops!')\n        }\n\n        request(app)\n          .get('/')\n          .expect(404, done)\n      })\n    })\n  })\n\n  describe('with verify option', function () {\n    it('should assert value if function', function () {\n      assert.throws(createApp.bind(null, { verify: 'lol' }),\n        /TypeError: option verify must be function/)\n    })\n\n    it('should error from verify', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x5b) throw new Error('no arrays')\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('[\"tobi\"]')\n        .expect(403, '[entity.verify.failed] no arrays', done)\n    })\n\n    it('should allow custom codes', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] !== 0x5b) return\n          var err = new Error('no arrays')\n          err.status = 400\n          throw err\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('[\"tobi\"]')\n        .expect(400, '[entity.verify.failed] no arrays', done)\n    })\n\n    it('should allow custom type', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] !== 0x5b) return\n          var err = new Error('no arrays')\n          err.type = 'foo.bar'\n          throw err\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('[\"tobi\"]')\n        .expect(403, '[foo.bar] no arrays', done)\n    })\n\n    it('should include original body on error object', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x5b) throw new Error('no arrays')\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .set('X-Error-Property', 'body')\n        .send('[\"tobi\"]')\n        .expect(403, '[\"tobi\"]', done)\n    })\n\n    it('should allow pass-through', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x5b) throw new Error('no arrays')\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('{\"user\":\"tobi\"}')\n        .expect(200, '{\"user\":\"tobi\"}', done)\n    })\n\n    it('should work with different charsets', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x5b) throw new Error('no arrays')\n        }\n      })\n\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/json; charset=utf-16')\n      test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should 415 on unknown charset prior to verify', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          throw new Error('unexpected verify call')\n        }\n      })\n\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/json; charset=x-bogus')\n      test.write(Buffer.from('00000000', 'hex'))\n      test.expect(415, '[charset.unsupported] unsupported charset \"X-BOGUS\"', done)\n    })\n  })\n\n  describe('async local storage', function () {\n    before(function () {\n      var app = express()\n      var store = { foo: 'bar' }\n\n      app.use(function (req, res, next) {\n        req.asyncLocalStorage = new AsyncLocalStorage()\n        req.asyncLocalStorage.run(store, next)\n      })\n\n      app.use(express.json())\n\n      app.use(function (req, res, next) {\n        var local = req.asyncLocalStorage.getStore()\n\n        if (local) {\n          res.setHeader('x-store-foo', String(local.foo))\n        }\n\n        next()\n      })\n\n      app.use(function (err, req, res, next) {\n        var local = req.asyncLocalStorage.getStore()\n\n        if (local) {\n          res.setHeader('x-store-foo', String(local.foo))\n        }\n\n        res.status(err.status || 500)\n        res.send('[' + err.type + '] ' + err.message)\n      })\n\n      app.post('/', function (req, res) {\n        res.json(req.body)\n      })\n\n      this.app = app\n    })\n\n    it('should persist store', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('{\"user\":\"tobi\"}')\n        .expect(200)\n        .expect('x-store-foo', 'bar')\n        .expect('{\"user\":\"tobi\"}')\n        .end(done)\n    })\n\n    it('should persist store when unmatched content-type', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/fizzbuzz')\n        .send('buzz')\n        .expect(200)\n        .expect('x-store-foo', 'bar')\n        .expect('')\n        .end(done)\n    })\n\n    it('should persist store when inflated', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))\n      test.expect(200)\n      test.expect('x-store-foo', 'bar')\n      test.expect('{\"name\":\"论\"}')\n      test.end(done)\n    })\n\n    it('should persist store when inflate error', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))\n      test.expect(400)\n      test.expect('x-store-foo', 'bar')\n      test.end(done)\n    })\n\n    it('should persist store when parse error', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('{\"user\":')\n        .expect(400)\n        .expect('x-store-foo', 'bar')\n        .end(done)\n    })\n\n    it('should persist store when limit exceeded', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/json')\n        .send('{\"user\":\"' + Buffer.alloc(1024 * 100, '.').toString() + '\"}')\n        .expect(413)\n        .expect('x-store-foo', 'bar')\n        .end(done)\n    })\n  })\n\n  describe('charset', function () {\n    before(function () {\n      this.app = createApp()\n    })\n\n    it('should parse utf-8', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/json; charset=utf-8')\n      test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should parse utf-16', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/json; charset=utf-16')\n      test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should parse when content-length != char length', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/json; charset=utf-8')\n      test.set('Content-Length', '13')\n      test.write(Buffer.from('7b2274657374223a22c3a5227d', 'hex'))\n      test.expect(200, '{\"test\":\"å\"}', done)\n    })\n\n    it('should default to utf-8', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should fail on unknown charset', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/json; charset=koi8-r')\n      test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))\n      test.expect(415, '[charset.unsupported] unsupported charset \"KOI8-R\"', done)\n    })\n  })\n\n  describe('encoding', function () {\n    before(function () {\n      this.app = createApp({ limit: '1kb' })\n    })\n\n    it('should parse without encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should support identity encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'identity')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should support gzip encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should support deflate encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'deflate')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('789cab56ca4bcc4d55b2527ab16e97522d00274505ac', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should be case-insensitive', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'GZIP')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should 415 on unknown encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'nulls')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('000000000000', 'hex'))\n      test.expect(415, '[encoding.unsupported] unsupported content encoding \"nulls\"', done)\n    })\n\n    it('should 400 on malformed encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))\n      test.expect(400, done)\n    })\n\n    it('should 413 when inflated value exceeds limit', function (done) {\n      // gzip'd data exceeds 1kb, but deflated below 1kb\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/json')\n      test.write(Buffer.from('1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000', 'hex'))\n      test.write(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'))\n      test.write(Buffer.from('0000000000000000004f0625b3b71650c30000', 'hex'))\n      test.expect(413, done)\n    })\n  })\n})\n\nfunction createApp (options) {\n  var app = express()\n\n  app.use(express.json(options))\n\n  app.use(function (err, req, res, next) {\n    // console.log(err)\n    res.status(err.status || 500)\n    res.send(String(req.headers['x-error-property']\n      ? err[req.headers['x-error-property']]\n      : ('[' + err.type + '] ' + err.message)))\n  })\n\n  app.post('/', function (req, res) {\n    res.json(req.body)\n  })\n\n  return app\n}\n\nfunction parseError (str) {\n  try {\n    JSON.parse(str); throw new SyntaxError('strict violation')\n  } catch (e) {\n    return e.message\n  }\n}\n\nfunction shouldContainInBody (str) {\n  return function (res) {\n    assert.ok(res.text.indexOf(str) !== -1,\n      'expected \\'' + res.text + '\\' to contain \\'' + str + '\\'')\n  }\n}\n"
  },
  {
    "path": "test/express.raw.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage\n\nvar express = require('..')\nvar request = require('supertest')\nconst { Buffer } = require('node:buffer');\n\ndescribe('express.raw()', function () {\n  before(function () {\n    this.app = createApp()\n  })\n\n  it('should parse application/octet-stream', function (done) {\n    request(this.app)\n      .post('/')\n      .set('Content-Type', 'application/octet-stream')\n      .send('the user is tobi')\n      .expect(200, { buf: '746865207573657220697320746f6269' }, done)\n  })\n\n  it('should 400 when invalid content-length', function (done) {\n    var app = express()\n\n    app.use(function (req, res, next) {\n      req.headers['content-length'] = '20' // bad length\n      next()\n    })\n\n    app.use(express.raw())\n\n    app.post('/', function (req, res) {\n      if (Buffer.isBuffer(req.body)) {\n        res.json({ buf: req.body.toString('hex') })\n      } else {\n        res.json(req.body)\n      }\n    })\n\n    request(app)\n      .post('/')\n      .set('Content-Type', 'application/octet-stream')\n      .send('stuff')\n      .expect(400, /content length/, done)\n  })\n\n  it('should handle Content-Length: 0', function (done) {\n    request(this.app)\n      .post('/')\n      .set('Content-Type', 'application/octet-stream')\n      .set('Content-Length', '0')\n      .expect(200, { buf: '' }, done)\n  })\n\n  it('should handle empty message-body', function (done) {\n    request(this.app)\n      .post('/')\n      .set('Content-Type', 'application/octet-stream')\n      .set('Transfer-Encoding', 'chunked')\n      .send('')\n      .expect(200, { buf: '' }, done)\n  })\n\n  it('should handle duplicated middleware', function (done) {\n    var app = express()\n\n    app.use(express.raw())\n    app.use(express.raw())\n\n    app.post('/', function (req, res) {\n      if (Buffer.isBuffer(req.body)) {\n        res.json({ buf: req.body.toString('hex') })\n      } else {\n        res.json(req.body)\n      }\n    })\n\n    request(app)\n      .post('/')\n      .set('Content-Type', 'application/octet-stream')\n      .send('the user is tobi')\n      .expect(200, { buf: '746865207573657220697320746f6269' }, done)\n  })\n\n  describe('with limit option', function () {\n    it('should 413 when over limit with Content-Length', function (done) {\n      var buf = Buffer.alloc(1028, '.')\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.set('Content-Length', '1028')\n      test.write(buf)\n      test.expect(413, done)\n    })\n\n    it('should 413 when over limit with chunked encoding', function (done) {\n      var buf = Buffer.alloc(1028, '.')\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.set('Transfer-Encoding', 'chunked')\n      test.write(buf)\n      test.expect(413, done)\n    })\n\n    it('should 413 when inflated body over limit', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))\n      test.expect(413, done)\n    })\n\n    it('should accept number of bytes', function (done) {\n      var buf = Buffer.alloc(1028, '.')\n      var app = createApp({ limit: 1024 })\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(buf)\n      test.expect(413, done)\n    })\n\n    it('should not change when options altered', function (done) {\n      var buf = Buffer.alloc(1028, '.')\n      var options = { limit: '1kb' }\n      var app = createApp(options)\n\n      options.limit = '100kb'\n\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(buf)\n      test.expect(413, done)\n    })\n\n    it('should not hang response', function (done) {\n      var buf = Buffer.alloc(10240, '.')\n      var app = createApp({ limit: '8kb' })\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(buf)\n      test.write(buf)\n      test.write(buf)\n      test.expect(413, done)\n    })\n\n    it('should not error when inflating', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex'))\n      test.expect(413, done)\n    })\n  })\n\n  describe('with inflate option', function () {\n    describe('when false', function () {\n      before(function () {\n        this.app = createApp({ inflate: false })\n      })\n\n      it('should not accept content-encoding', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Encoding', 'gzip')\n        test.set('Content-Type', 'application/octet-stream')\n        test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n        test.expect(415, '[encoding.unsupported] content encoding unsupported', done)\n      })\n    })\n\n    describe('when true', function () {\n      before(function () {\n        this.app = createApp({ inflate: true })\n      })\n\n      it('should accept content-encoding', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Encoding', 'gzip')\n        test.set('Content-Type', 'application/octet-stream')\n        test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n        test.expect(200, { buf: '6e616d653de8aeba' }, done)\n      })\n    })\n  })\n\n  describe('with type option', function () {\n    describe('when \"application/vnd+octets\"', function () {\n      before(function () {\n        this.app = createApp({ type: 'application/vnd+octets' })\n      })\n\n      it('should parse for custom type', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Type', 'application/vnd+octets')\n        test.write(Buffer.from('000102', 'hex'))\n        test.expect(200, { buf: '000102' }, done)\n      })\n\n      it('should ignore standard type', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Type', 'application/octet-stream')\n        test.write(Buffer.from('000102', 'hex'))\n        test.expect(200, '', done)\n      })\n    })\n\n    describe('when [\"application/octet-stream\", \"application/vnd+octets\"]', function () {\n      before(function () {\n        this.app = createApp({\n          type: ['application/octet-stream', 'application/vnd+octets']\n        })\n      })\n\n      it('should parse \"application/octet-stream\"', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Type', 'application/octet-stream')\n        test.write(Buffer.from('000102', 'hex'))\n        test.expect(200, { buf: '000102' }, done)\n      })\n\n      it('should parse \"application/vnd+octets\"', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Type', 'application/vnd+octets')\n        test.write(Buffer.from('000102', 'hex'))\n        test.expect(200, { buf: '000102' }, done)\n      })\n\n      it('should ignore \"application/x-foo\"', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Type', 'application/x-foo')\n        test.write(Buffer.from('000102', 'hex'))\n        test.expect(200, '', done)\n      })\n    })\n\n    describe('when a function', function () {\n      it('should parse when truthy value returned', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          return req.headers['content-type'] === 'application/vnd.octet'\n        }\n\n        var test = request(app).post('/')\n        test.set('Content-Type', 'application/vnd.octet')\n        test.write(Buffer.from('000102', 'hex'))\n        test.expect(200, { buf: '000102' }, done)\n      })\n\n      it('should work without content-type', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          return true\n        }\n\n        var test = request(app).post('/')\n        test.write(Buffer.from('000102', 'hex'))\n        test.expect(200, { buf: '000102' }, done)\n      })\n\n      it('should not invoke without a body', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          throw new Error('oops!')\n        }\n\n        request(app)\n          .get('/')\n          .expect(404, done)\n      })\n    })\n  })\n\n  describe('with verify option', function () {\n    it('should assert value is function', function () {\n      assert.throws(createApp.bind(null, { verify: 'lol' }),\n        /TypeError: option verify must be function/)\n    })\n\n    it('should error from verify', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x00) throw new Error('no leading null')\n        }\n      })\n\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('000102', 'hex'))\n      test.expect(403, '[entity.verify.failed] no leading null', done)\n    })\n\n    it('should allow custom codes', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] !== 0x00) return\n          var err = new Error('no leading null')\n          err.status = 400\n          throw err\n        }\n      })\n\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('000102', 'hex'))\n      test.expect(400, '[entity.verify.failed] no leading null', done)\n    })\n\n    it('should allow pass-through', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x00) throw new Error('no leading null')\n        }\n      })\n\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('0102', 'hex'))\n      test.expect(200, { buf: '0102' }, done)\n    })\n  })\n\n  describe('async local storage', function () {\n    before(function () {\n      var app = express()\n      var store = { foo: 'bar' }\n\n      app.use(function (req, res, next) {\n        req.asyncLocalStorage = new AsyncLocalStorage()\n        req.asyncLocalStorage.run(store, next)\n      })\n\n      app.use(express.raw())\n\n      app.use(function (req, res, next) {\n        var local = req.asyncLocalStorage.getStore()\n\n        if (local) {\n          res.setHeader('x-store-foo', String(local.foo))\n        }\n\n        next()\n      })\n\n      app.use(function (err, req, res, next) {\n        var local = req.asyncLocalStorage.getStore()\n\n        if (local) {\n          res.setHeader('x-store-foo', String(local.foo))\n        }\n\n        res.status(err.status || 500)\n        res.send('[' + err.type + '] ' + err.message)\n      })\n\n      app.post('/', function (req, res) {\n        if (Buffer.isBuffer(req.body)) {\n          res.json({ buf: req.body.toString('hex') })\n        } else {\n          res.json(req.body)\n        }\n      })\n\n      this.app = app\n    })\n\n    it('should persist store', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/octet-stream')\n        .send('the user is tobi')\n        .expect(200)\n        .expect('x-store-foo', 'bar')\n        .expect({ buf: '746865207573657220697320746f6269' })\n        .end(done)\n    })\n\n    it('should persist store when unmatched content-type', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/fizzbuzz')\n        .send('buzz')\n        .expect(200)\n        .expect('x-store-foo', 'bar')\n        .end(done)\n    })\n\n    it('should persist store when inflated', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n      test.expect(200)\n      test.expect('x-store-foo', 'bar')\n      test.expect({ buf: '6e616d653de8aeba' })\n      test.end(done)\n    })\n\n    it('should persist store when inflate error', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))\n      test.expect(400)\n      test.expect('x-store-foo', 'bar')\n      test.end(done)\n    })\n\n    it('should persist store when limit exceeded', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/octet-stream')\n        .send('the user is ' + Buffer.alloc(1024 * 100, '.').toString())\n        .expect(413)\n        .expect('x-store-foo', 'bar')\n        .end(done)\n    })\n  })\n\n  describe('charset', function () {\n    before(function () {\n      this.app = createApp()\n    })\n\n    it('should ignore charset', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/octet-stream; charset=utf-8')\n      test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))\n      test.expect(200, { buf: '6e616d6520697320e8aeba' }, done)\n    })\n  })\n\n  describe('encoding', function () {\n    before(function () {\n      this.app = createApp({ limit: '10kb' })\n    })\n\n    it('should parse without encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('6e616d653de8aeba', 'hex'))\n      test.expect(200, { buf: '6e616d653de8aeba' }, done)\n    })\n\n    it('should support identity encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'identity')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('6e616d653de8aeba', 'hex'))\n      test.expect(200, { buf: '6e616d653de8aeba' }, done)\n    })\n\n    it('should support gzip encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n      test.expect(200, { buf: '6e616d653de8aeba' }, done)\n    })\n\n    it('should support deflate encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'deflate')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex'))\n      test.expect(200, { buf: '6e616d653de8aeba' }, done)\n    })\n\n    it('should be case-insensitive', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'GZIP')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n      test.expect(200, { buf: '6e616d653de8aeba' }, done)\n    })\n\n    it('should 415 on unknown encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'nulls')\n      test.set('Content-Type', 'application/octet-stream')\n      test.write(Buffer.from('000000000000', 'hex'))\n      test.expect(415, '[encoding.unsupported] unsupported content encoding \"nulls\"', done)\n    })\n  })\n})\n\nfunction createApp (options) {\n  var app = express()\n\n  app.use(express.raw(options))\n\n  app.use(function (err, req, res, next) {\n    res.status(err.status || 500)\n    res.send(String(req.headers['x-error-property']\n      ? err[req.headers['x-error-property']]\n      : ('[' + err.type + '] ' + err.message)))\n  })\n\n  app.post('/', function (req, res) {\n    if (Buffer.isBuffer(req.body)) {\n      res.json({ buf: req.body.toString('hex') })\n    } else {\n      res.json(req.body)\n    }\n  })\n\n  return app\n}\n"
  },
  {
    "path": "test/express.static.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('..')\nvar path = require('node:path')\nconst { Buffer } = require('node:buffer');\n\nvar request = require('supertest')\nvar utils = require('./support/utils')\n\nvar fixtures = path.join(__dirname, '/fixtures')\nvar relative = path.relative(process.cwd(), fixtures)\n\nvar skipRelative = ~relative.indexOf('..') || path.resolve(relative) === relative\n\ndescribe('express.static()', function () {\n  describe('basic operations', function () {\n    before(function () {\n      this.app = createApp()\n    })\n\n    it('should require root path', function () {\n      assert.throws(express.static.bind(), /root path required/)\n    })\n\n    it('should require root path to be string', function () {\n      assert.throws(express.static.bind(null, 42), /root path.*string/)\n    })\n\n    it('should serve static files', function (done) {\n      request(this.app)\n        .get('/todo.txt')\n        .expect(200, '- groceries', done)\n    })\n\n    it('should support nesting', function (done) {\n      request(this.app)\n        .get('/users/tobi.txt')\n        .expect(200, 'ferret', done)\n    })\n\n    it('should set Content-Type', function (done) {\n      request(this.app)\n        .get('/todo.txt')\n        .expect('Content-Type', 'text/plain; charset=utf-8')\n        .expect(200, done)\n    })\n\n    it('should set Last-Modified', function (done) {\n      request(this.app)\n        .get('/todo.txt')\n        .expect('Last-Modified', /\\d{2} \\w{3} \\d{4}/)\n        .expect(200, done)\n    })\n\n    it('should default max-age=0', function (done) {\n      request(this.app)\n        .get('/todo.txt')\n        .expect('Cache-Control', 'public, max-age=0')\n        .expect(200, done)\n    })\n\n    it('should support urlencoded pathnames', function (done) {\n      request(this.app)\n        .get('/%25%20of%20dogs.txt')\n        .expect(200, '20%', done)\n    })\n\n    it('should not choke on auth-looking URL', function (done) {\n      request(this.app)\n        .get('//todo@txt')\n        .expect(404, 'Not Found', done)\n    })\n\n    it('should support index.html', function (done) {\n      request(this.app)\n        .get('/users/')\n        .expect(200)\n        .expect('Content-Type', /html/)\n        .expect('<p>tobi, loki, jane</p>', done)\n    })\n\n    it('should support ../', function (done) {\n      request(this.app)\n        .get('/users/../todo.txt')\n        .expect(200, '- groceries', done)\n    })\n\n    it('should support HEAD', function (done) {\n      request(this.app)\n        .head('/todo.txt')\n        .expect(200)\n        .expect(utils.shouldNotHaveBody())\n        .end(done)\n    })\n\n    it('should skip POST requests', function (done) {\n      request(this.app)\n        .post('/todo.txt')\n        .expect(404, 'Not Found', done)\n    })\n\n    it('should support conditional requests', function (done) {\n      var app = this.app\n\n      request(app)\n        .get('/todo.txt')\n        .end(function (err, res) {\n          if (err) throw err\n          request(app)\n            .get('/todo.txt')\n            .set('If-None-Match', res.headers.etag)\n            .expect(304, done)\n        })\n    })\n\n    it('should support precondition checks', function (done) {\n      request(this.app)\n        .get('/todo.txt')\n        .set('If-Match', '\"foo\"')\n        .expect(412, done)\n    })\n\n    it('should serve zero-length files', function (done) {\n      request(this.app)\n        .get('/empty.txt')\n        .expect(200, '', done)\n    })\n\n    it('should ignore hidden files', function (done) {\n      request(this.app)\n        .get('/.name')\n        .expect(404, 'Not Found', done)\n    })\n  });\n\n  (skipRelative ? describe.skip : describe)('current dir', function () {\n    before(function () {\n      this.app = createApp('.')\n    })\n\n    it('should be served with \".\"', function (done) {\n      var dest = relative.split(path.sep).join('/')\n      request(this.app)\n        .get('/' + dest + '/todo.txt')\n        .expect(200, '- groceries', done)\n    })\n  })\n\n  describe('acceptRanges', function () {\n    describe('when false', function () {\n      it('should not include Accept-Ranges', function (done) {\n        request(createApp(fixtures, { 'acceptRanges': false }))\n          .get('/nums.txt')\n          .expect(utils.shouldNotHaveHeader('Accept-Ranges'))\n          .expect(200, '123456789', done)\n      })\n\n      it('should ignore Rage request header', function (done) {\n        request(createApp(fixtures, { 'acceptRanges': false }))\n          .get('/nums.txt')\n          .set('Range', 'bytes=0-3')\n          .expect(utils.shouldNotHaveHeader('Accept-Ranges'))\n          .expect(utils.shouldNotHaveHeader('Content-Range'))\n          .expect(200, '123456789', done)\n      })\n    })\n\n    describe('when true', function () {\n      it('should include Accept-Ranges', function (done) {\n        request(createApp(fixtures, { 'acceptRanges': true }))\n          .get('/nums.txt')\n          .expect('Accept-Ranges', 'bytes')\n          .expect(200, '123456789', done)\n      })\n\n      it('should obey Rage request header', function (done) {\n        request(createApp(fixtures, { 'acceptRanges': true }))\n          .get('/nums.txt')\n          .set('Range', 'bytes=0-3')\n          .expect('Accept-Ranges', 'bytes')\n          .expect('Content-Range', 'bytes 0-3/9')\n          .expect(206, '1234', done)\n      })\n    })\n  })\n\n  describe('cacheControl', function () {\n    describe('when false', function () {\n      it('should not include Cache-Control', function (done) {\n        request(createApp(fixtures, { 'cacheControl': false }))\n          .get('/nums.txt')\n          .expect(utils.shouldNotHaveHeader('Cache-Control'))\n          .expect(200, '123456789', done)\n      })\n\n      it('should ignore maxAge', function (done) {\n        request(createApp(fixtures, { 'cacheControl': false, 'maxAge': 12000 }))\n          .get('/nums.txt')\n          .expect(utils.shouldNotHaveHeader('Cache-Control'))\n          .expect(200, '123456789', done)\n      })\n    })\n\n    describe('when true', function () {\n      it('should include Cache-Control', function (done) {\n        request(createApp(fixtures, { 'cacheControl': true }))\n          .get('/nums.txt')\n          .expect('Cache-Control', 'public, max-age=0')\n          .expect(200, '123456789', done)\n      })\n    })\n  })\n\n  describe('extensions', function () {\n    it('should be not be enabled by default', function (done) {\n      request(createApp(fixtures))\n        .get('/todo')\n        .expect(404, done)\n    })\n\n    it('should be configurable', function (done) {\n      request(createApp(fixtures, { 'extensions': 'txt' }))\n        .get('/todo')\n        .expect(200, '- groceries', done)\n    })\n\n    it('should support disabling extensions', function (done) {\n      request(createApp(fixtures, { 'extensions': false }))\n        .get('/todo')\n        .expect(404, done)\n    })\n\n    it('should support fallbacks', function (done) {\n      request(createApp(fixtures, { 'extensions': ['htm', 'html', 'txt'] }))\n        .get('/todo')\n        .expect(200, '<li>groceries</li>', done)\n    })\n\n    it('should 404 if nothing found', function (done) {\n      request(createApp(fixtures, { 'extensions': ['htm', 'html', 'txt'] }))\n        .get('/bob')\n        .expect(404, done)\n    })\n  })\n\n  describe('fallthrough', function () {\n    it('should default to true', function (done) {\n      request(createApp())\n        .get('/does-not-exist')\n        .expect(404, 'Not Found', done)\n    })\n\n    describe('when true', function () {\n      before(function () {\n        this.app = createApp(fixtures, { 'fallthrough': true })\n      })\n\n      it('should fall-through when OPTIONS request', function (done) {\n        request(this.app)\n          .options('/todo.txt')\n          .expect(404, 'Not Found', done)\n      })\n\n      it('should fall-through when URL malformed', function (done) {\n        request(this.app)\n          .get('/%')\n          .expect(404, 'Not Found', done)\n      })\n\n      it('should fall-through when traversing past root', function (done) {\n        request(this.app)\n          .get('/users/../../todo.txt')\n          .expect(404, 'Not Found', done)\n      })\n\n      it('should fall-through when URL too long', function (done) {\n        var app = express()\n        var root = fixtures + Array(10000).join('/foobar')\n\n        app.use(express.static(root, { 'fallthrough': true }))\n        app.use(function (req, res, next) {\n          res.sendStatus(404)\n        })\n\n        request(app)\n          .get('/')\n          .expect(404, 'Not Found', done)\n      })\n\n      describe('with redirect: true', function () {\n        before(function () {\n          this.app = createApp(fixtures, { 'fallthrough': true, 'redirect': true })\n        })\n\n        it('should fall-through when directory', function (done) {\n          request(this.app)\n            .get('/pets/')\n            .expect(404, 'Not Found', done)\n        })\n\n        it('should redirect when directory without slash', function (done) {\n          request(this.app)\n            .get('/pets')\n            .expect(301, /Redirecting/, done)\n        })\n      })\n\n      describe('with redirect: false', function () {\n        before(function () {\n          this.app = createApp(fixtures, { 'fallthrough': true, 'redirect': false })\n        })\n\n        it('should fall-through when directory', function (done) {\n          request(this.app)\n            .get('/pets/')\n            .expect(404, 'Not Found', done)\n        })\n\n        it('should fall-through when directory without slash', function (done) {\n          request(this.app)\n            .get('/pets')\n            .expect(404, 'Not Found', done)\n        })\n      })\n    })\n\n    describe('when false', function () {\n      before(function () {\n        this.app = createApp(fixtures, { 'fallthrough': false })\n      })\n\n      it('should 405 when OPTIONS request', function (done) {\n        request(this.app)\n          .options('/todo.txt')\n          .expect('Allow', 'GET, HEAD')\n          .expect(405, done)\n      })\n\n      it('should 400 when URL malformed', function (done) {\n        request(this.app)\n          .get('/%')\n          .expect(400, /BadRequestError/, done)\n      })\n\n      it('should 403 when traversing past root', function (done) {\n        request(this.app)\n          .get('/users/../../todo.txt')\n          .expect(403, /ForbiddenError/, done)\n      })\n\n      it('should 404 when URL too long', function (done) {\n        var app = express()\n        var root = fixtures + Array(10000).join('/foobar')\n\n        app.use(express.static(root, { 'fallthrough': false }))\n        app.use(function (req, res, next) {\n          res.sendStatus(404)\n        })\n\n        request(app)\n          .get('/')\n          .expect(404, /ENAMETOOLONG/, done)\n      })\n\n      describe('with redirect: true', function () {\n        before(function () {\n          this.app = createApp(fixtures, { 'fallthrough': false, 'redirect': true })\n        })\n\n        it('should 404 when directory', function (done) {\n          request(this.app)\n            .get('/pets/')\n            .expect(404, /NotFoundError|ENOENT/, done)\n        })\n\n        it('should redirect when directory without slash', function (done) {\n          request(this.app)\n            .get('/pets')\n            .expect(301, /Redirecting/, done)\n        })\n      })\n\n      describe('with redirect: false', function () {\n        before(function () {\n          this.app = createApp(fixtures, { 'fallthrough': false, 'redirect': false })\n        })\n\n        it('should 404 when directory', function (done) {\n          request(this.app)\n            .get('/pets/')\n            .expect(404, /NotFoundError|ENOENT/, done)\n        })\n\n        it('should 404 when directory without slash', function (done) {\n          request(this.app)\n            .get('/pets')\n            .expect(404, /NotFoundError|ENOENT/, done)\n        })\n      })\n    })\n  })\n\n  describe('hidden files', function () {\n    before(function () {\n      this.app = createApp(fixtures, { 'dotfiles': 'allow' })\n    })\n\n    it('should be served when dotfiles: \"allow\" is given', function (done) {\n      request(this.app)\n        .get('/.name')\n        .expect(200)\n        .expect(utils.shouldHaveBody(Buffer.from('tobi')))\n        .end(done)\n    })\n  })\n\n  describe('immutable', function () {\n    it('should default to false', function (done) {\n      request(createApp(fixtures))\n        .get('/nums.txt')\n        .expect('Cache-Control', 'public, max-age=0', done)\n    })\n\n    it('should set immutable directive in Cache-Control', function (done) {\n      request(createApp(fixtures, { 'immutable': true, 'maxAge': '1h' }))\n        .get('/nums.txt')\n        .expect('Cache-Control', 'public, max-age=3600, immutable', done)\n    })\n  })\n\n  describe('lastModified', function () {\n    describe('when false', function () {\n      it('should not include Last-Modified', function (done) {\n        request(createApp(fixtures, { 'lastModified': false }))\n          .get('/nums.txt')\n          .expect(utils.shouldNotHaveHeader('Last-Modified'))\n          .expect(200, '123456789', done)\n      })\n    })\n\n    describe('when true', function () {\n      it('should include Last-Modified', function (done) {\n        request(createApp(fixtures, { 'lastModified': true }))\n          .get('/nums.txt')\n          .expect('Last-Modified', /^\\w{3}, \\d+ \\w+ \\d+ \\d+:\\d+:\\d+ \\w+$/)\n          .expect(200, '123456789', done)\n      })\n    })\n  })\n\n  describe('maxAge', function () {\n    it('should accept string', function (done) {\n      request(createApp(fixtures, { 'maxAge': '30d' }))\n        .get('/todo.txt')\n        .expect('cache-control', 'public, max-age=' + (60 * 60 * 24 * 30))\n        .expect(200, done)\n    })\n\n    it('should be reasonable when infinite', function (done) {\n      request(createApp(fixtures, { 'maxAge': Infinity }))\n        .get('/todo.txt')\n        .expect('cache-control', 'public, max-age=' + (60 * 60 * 24 * 365))\n        .expect(200, done)\n    })\n  })\n\n  describe('redirect', function () {\n    before(function () {\n      this.app = express()\n      this.app.use(function (req, res, next) {\n        req.originalUrl = req.url =\n          req.originalUrl.replace(/\\/snow(\\/|$)/, '/snow \\u2603$1')\n        next()\n      })\n      this.app.use(express.static(fixtures))\n    })\n\n    it('should redirect directories', function (done) {\n      request(this.app)\n        .get('/users')\n        .expect('Location', '/users/')\n        .expect(301, done)\n    })\n\n    it('should include HTML link', function (done) {\n      request(this.app)\n        .get('/users')\n        .expect('Location', '/users/')\n        .expect(301, /\\/users\\//, done)\n    })\n\n    it('should redirect directories with query string', function (done) {\n      request(this.app)\n        .get('/users?name=john')\n        .expect('Location', '/users/?name=john')\n        .expect(301, done)\n    })\n\n    it('should not redirect to protocol-relative locations', function (done) {\n      request(this.app)\n        .get('//users')\n        .expect('Location', '/users/')\n        .expect(301, done)\n    })\n\n    it('should ensure redirect URL is properly encoded', function (done) {\n      request(this.app)\n        .get('/snow')\n        .expect('Location', '/snow%20%E2%98%83/')\n        .expect('Content-Type', /html/)\n        .expect(301, />Redirecting to \\/snow%20%E2%98%83\\/</, done)\n    })\n\n    it('should respond with default Content-Security-Policy', function (done) {\n      request(this.app)\n        .get('/users')\n        .expect('Content-Security-Policy', \"default-src 'none'\")\n        .expect(301, done)\n    })\n\n    it('should not redirect incorrectly', function (done) {\n      request(this.app)\n        .get('/')\n        .expect(404, done)\n    })\n\n    describe('when false', function () {\n      before(function () {\n        this.app = createApp(fixtures, { 'redirect': false })\n      })\n\n      it('should disable redirect', function (done) {\n        request(this.app)\n          .get('/users')\n          .expect(404, done)\n      })\n    })\n  })\n\n  describe('setHeaders', function () {\n    before(function () {\n      this.app = express()\n      this.app.use(express.static(fixtures, { 'setHeaders': function (res) {\n        res.setHeader('x-custom', 'set')\n      } }))\n    })\n\n    it('should reject non-functions', function () {\n      assert.throws(express.static.bind(null, fixtures, { 'setHeaders': 3 }), /setHeaders.*function/)\n    })\n\n    it('should get called when sending file', function (done) {\n      request(this.app)\n        .get('/nums.txt')\n        .expect('x-custom', 'set')\n        .expect(200, done)\n    })\n\n    it('should not get called on 404', function (done) {\n      request(this.app)\n        .get('/bogus')\n        .expect(utils.shouldNotHaveHeader('x-custom'))\n        .expect(404, done)\n    })\n\n    it('should not get called on redirect', function (done) {\n      request(this.app)\n        .get('/users')\n        .expect(utils.shouldNotHaveHeader('x-custom'))\n        .expect(301, done)\n    })\n  })\n\n  describe('when traversing past root', function () {\n    before(function () {\n      this.app = createApp(fixtures, { 'fallthrough': false })\n    })\n\n    it('should catch urlencoded ../', function (done) {\n      request(this.app)\n        .get('/users/%2e%2e/%2e%2e/todo.txt')\n        .expect(403, done)\n    })\n\n    it('should not allow root path disclosure', function (done) {\n      request(this.app)\n        .get('/users/../../fixtures/todo.txt')\n        .expect(403, done)\n    })\n  })\n\n  describe('when request has \"Range\" header', function () {\n    before(function () {\n      this.app = createApp()\n    })\n\n    it('should support byte ranges', function (done) {\n      request(this.app)\n        .get('/nums.txt')\n        .set('Range', 'bytes=0-4')\n        .expect('12345', done)\n    })\n\n    it('should be inclusive', function (done) {\n      request(this.app)\n        .get('/nums.txt')\n        .set('Range', 'bytes=0-0')\n        .expect('1', done)\n    })\n\n    it('should set Content-Range', function (done) {\n      request(this.app)\n        .get('/nums.txt')\n        .set('Range', 'bytes=2-5')\n        .expect('Content-Range', 'bytes 2-5/9', done)\n    })\n\n    it('should support -n', function (done) {\n      request(this.app)\n        .get('/nums.txt')\n        .set('Range', 'bytes=-3')\n        .expect('789', done)\n    })\n\n    it('should support n-', function (done) {\n      request(this.app)\n        .get('/nums.txt')\n        .set('Range', 'bytes=3-')\n        .expect('456789', done)\n    })\n\n    it('should respond with 206 \"Partial Content\"', function (done) {\n      request(this.app)\n        .get('/nums.txt')\n        .set('Range', 'bytes=0-4')\n        .expect(206, done)\n    })\n\n    it('should set Content-Length to the # of octets transferred', function (done) {\n      request(this.app)\n        .get('/nums.txt')\n        .set('Range', 'bytes=2-3')\n        .expect('Content-Length', '2')\n        .expect(206, '34', done)\n    })\n\n    describe('when last-byte-pos of the range is greater than current length', function () {\n      it('is taken to be equal to one less than the current length', function (done) {\n        request(this.app)\n          .get('/nums.txt')\n          .set('Range', 'bytes=2-50')\n          .expect('Content-Range', 'bytes 2-8/9', done)\n      })\n\n      it('should adapt the Content-Length accordingly', function (done) {\n        request(this.app)\n          .get('/nums.txt')\n          .set('Range', 'bytes=2-50')\n          .expect('Content-Length', '7')\n          .expect(206, done)\n      })\n    })\n\n    describe('when the first- byte-pos of the range is greater than the current length', function () {\n      it('should respond with 416', function (done) {\n        request(this.app)\n          .get('/nums.txt')\n          .set('Range', 'bytes=9-50')\n          .expect(416, done)\n      })\n\n      it('should include a Content-Range header of complete length', function (done) {\n        request(this.app)\n          .get('/nums.txt')\n          .set('Range', 'bytes=9-50')\n          .expect('Content-Range', 'bytes */9')\n          .expect(416, done)\n      })\n    })\n\n    describe('when syntactically invalid', function () {\n      it('should respond with 200 and the entire contents', function (done) {\n        request(this.app)\n          .get('/nums.txt')\n          .set('Range', 'asdf')\n          .expect('123456789', done)\n      })\n    })\n  })\n\n  describe('when index at mount point', function () {\n    before(function () {\n      this.app = express()\n      this.app.use('/users', express.static(fixtures + '/users'))\n    })\n\n    it('should redirect correctly', function (done) {\n      request(this.app)\n        .get('/users')\n        .expect('Location', '/users/')\n        .expect(301, done)\n    })\n  })\n\n  describe('when mounted', function () {\n    before(function () {\n      this.app = express()\n      this.app.use('/static', express.static(fixtures))\n    })\n\n    it('should redirect relative to the originalUrl', function (done) {\n      request(this.app)\n        .get('/static/users')\n        .expect('Location', '/static/users/')\n        .expect(301, done)\n    })\n\n    it('should not choke on auth-looking URL', function (done) {\n      request(this.app)\n        .get('//todo@txt')\n        .expect(404, done)\n    })\n  })\n\n  //\n  // NOTE: This is not a real part of the API, but\n  //       over time this has become something users\n  //       are doing, so this will prevent unseen\n  //       regressions around this use-case.\n  //\n  describe('when mounted \"root\" as a file', function () {\n    before(function () {\n      this.app = express()\n      this.app.use('/todo.txt', express.static(fixtures + '/todo.txt'))\n    })\n\n    it('should load the file when on trailing slash', function (done) {\n      request(this.app)\n        .get('/todo.txt')\n        .expect(200, '- groceries', done)\n    })\n\n    it('should 404 when trailing slash', function (done) {\n      request(this.app)\n        .get('/todo.txt/')\n        .expect(404, done)\n    })\n  })\n\n  describe('when responding non-2xx or 304', function () {\n    it('should not alter the status', function (done) {\n      var app = express()\n\n      app.use(function (req, res, next) {\n        res.status(501)\n        next()\n      })\n      app.use(express.static(fixtures))\n\n      request(app)\n        .get('/todo.txt')\n        .expect(501, '- groceries', done)\n    })\n  })\n\n  describe('when index file serving disabled', function () {\n    before(function () {\n      this.app = express()\n      this.app.use('/static', express.static(fixtures, { 'index': false }))\n      this.app.use(function (req, res, next) {\n        res.sendStatus(404)\n      })\n    })\n\n    it('should next() on directory', function (done) {\n      request(this.app)\n        .get('/static/users/')\n        .expect(404, 'Not Found', done)\n    })\n\n    it('should redirect to trailing slash', function (done) {\n      request(this.app)\n        .get('/static/users')\n        .expect('Location', '/static/users/')\n        .expect(301, done)\n    })\n\n    it('should next() on mount point', function (done) {\n      request(this.app)\n        .get('/static/')\n        .expect(404, 'Not Found', done)\n    })\n\n    it('should redirect to trailing slash mount point', function (done) {\n      request(this.app)\n        .get('/static')\n        .expect('Location', '/static/')\n        .expect(301, done)\n    })\n  })\n})\n\nfunction createApp (dir, options, fn) {\n  var app = express()\n  var root = dir || fixtures\n\n  app.use(express.static(root, options))\n\n  app.use(function (req, res, next) {\n    res.sendStatus(404)\n  })\n\n  return app\n}\n"
  },
  {
    "path": "test/express.text.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage\nconst { Buffer } = require('node:buffer');\nvar express = require('..')\nvar request = require('supertest')\n\ndescribe('express.text()', function () {\n  before(function () {\n    this.app = createApp()\n  })\n\n  it('should parse text/plain', function (done) {\n    request(this.app)\n      .post('/')\n      .set('Content-Type', 'text/plain')\n      .send('user is tobi')\n      .expect(200, '\"user is tobi\"', done)\n  })\n\n  it('should 400 when invalid content-length', function (done) {\n    var app = express()\n\n    app.use(function (req, res, next) {\n      req.headers['content-length'] = '20' // bad length\n      next()\n    })\n\n    app.use(express.text())\n\n    app.post('/', function (req, res) {\n      res.json(req.body)\n    })\n\n    request(app)\n      .post('/')\n      .set('Content-Type', 'text/plain')\n      .send('user')\n      .expect(400, /content length/, done)\n  })\n\n  it('should handle Content-Length: 0', function (done) {\n    request(createApp({ limit: '1kb' }))\n      .post('/')\n      .set('Content-Type', 'text/plain')\n      .set('Content-Length', '0')\n      .expect(200, '\"\"', done)\n  })\n\n  it('should handle empty message-body', function (done) {\n    request(createApp({ limit: '1kb' }))\n      .post('/')\n      .set('Content-Type', 'text/plain')\n      .set('Transfer-Encoding', 'chunked')\n      .send('')\n      .expect(200, '\"\"', done)\n  })\n\n  it('should handle duplicated middleware', function (done) {\n    var app = express()\n\n    app.use(express.text())\n    app.use(express.text())\n\n    app.post('/', function (req, res) {\n      res.json(req.body)\n    })\n\n    request(app)\n      .post('/')\n      .set('Content-Type', 'text/plain')\n      .send('user is tobi')\n      .expect(200, '\"user is tobi\"', done)\n  })\n\n  describe('with defaultCharset option', function () {\n    it('should change default charset', function (done) {\n      var server = createApp({ defaultCharset: 'koi8-r' })\n      var test = request(server).post('/')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('6e616d6520697320cec5d4', 'hex'))\n      test.expect(200, '\"name is нет\"', done)\n    })\n\n    it('should honor content-type charset', function (done) {\n      var server = createApp({ defaultCharset: 'koi8-r' })\n      var test = request(server).post('/')\n      test.set('Content-Type', 'text/plain; charset=utf-8')\n      test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n  })\n\n  describe('with limit option', function () {\n    it('should 413 when over limit with Content-Length', function (done) {\n      var buf = Buffer.alloc(1028, '.')\n      request(createApp({ limit: '1kb' }))\n        .post('/')\n        .set('Content-Type', 'text/plain')\n        .set('Content-Length', '1028')\n        .send(buf.toString())\n        .expect(413, done)\n    })\n\n    it('should 413 when over limit with chunked encoding', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var buf = Buffer.alloc(1028, '.')\n      var test = request(app).post('/')\n      test.set('Content-Type', 'text/plain')\n      test.set('Transfer-Encoding', 'chunked')\n      test.write(buf.toString())\n      test.expect(413, done)\n    })\n\n    it('should 413 when inflated body over limit', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))\n      test.expect(413, done)\n    })\n\n    it('should accept number of bytes', function (done) {\n      var buf = Buffer.alloc(1028, '.')\n      request(createApp({ limit: 1024 }))\n        .post('/')\n        .set('Content-Type', 'text/plain')\n        .send(buf.toString())\n        .expect(413, done)\n    })\n\n    it('should not change when options altered', function (done) {\n      var buf = Buffer.alloc(1028, '.')\n      var options = { limit: '1kb' }\n      var app = createApp(options)\n\n      options.limit = '100kb'\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'text/plain')\n        .send(buf.toString())\n        .expect(413, done)\n    })\n\n    it('should not hang response', function (done) {\n      var app = createApp({ limit: '8kb' })\n      var buf = Buffer.alloc(10240, '.')\n      var test = request(app).post('/')\n      test.set('Content-Type', 'text/plain')\n      test.write(buf)\n      test.write(buf)\n      test.write(buf)\n      test.expect(413, done)\n    })\n\n    it('should not error when inflating', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a1470404', 'hex'))\n      setTimeout(function () {\n        test.expect(413, done)\n      }, 100)\n    })\n  })\n\n  describe('with inflate option', function () {\n    describe('when false', function () {\n      before(function () {\n        this.app = createApp({ inflate: false })\n      })\n\n      it('should not accept content-encoding', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Encoding', 'gzip')\n        test.set('Content-Type', 'text/plain')\n        test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))\n        test.expect(415, '[encoding.unsupported] content encoding unsupported', done)\n      })\n    })\n\n    describe('when true', function () {\n      before(function () {\n        this.app = createApp({ inflate: true })\n      })\n\n      it('should accept content-encoding', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Encoding', 'gzip')\n        test.set('Content-Type', 'text/plain')\n        test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))\n        test.expect(200, '\"name is 论\"', done)\n      })\n    })\n  })\n\n  describe('with type option', function () {\n    describe('when \"text/html\"', function () {\n      before(function () {\n        this.app = createApp({ type: 'text/html' })\n      })\n\n      it('should parse for custom type', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'text/html')\n          .send('<b>tobi</b>')\n          .expect(200, '\"<b>tobi</b>\"', done)\n      })\n\n      it('should ignore standard type', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'text/plain')\n          .send('user is tobi')\n          .expect(200, '', done)\n      })\n    })\n\n    describe('when [\"text/html\", \"text/plain\"]', function () {\n      before(function () {\n        this.app = createApp({ type: ['text/html', 'text/plain'] })\n      })\n\n      it('should parse \"text/html\"', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'text/html')\n          .send('<b>tobi</b>')\n          .expect(200, '\"<b>tobi</b>\"', done)\n      })\n\n      it('should parse \"text/plain\"', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'text/plain')\n          .send('tobi')\n          .expect(200, '\"tobi\"', done)\n      })\n\n      it('should ignore \"text/xml\"', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'text/xml')\n          .send('<user>tobi</user>')\n          .expect(200, '', done)\n      })\n    })\n\n    describe('when a function', function () {\n      it('should parse when truthy value returned', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          return req.headers['content-type'] === 'text/vnd.something'\n        }\n\n        request(app)\n          .post('/')\n          .set('Content-Type', 'text/vnd.something')\n          .send('user is tobi')\n          .expect(200, '\"user is tobi\"', done)\n      })\n\n      it('should work without content-type', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          return true\n        }\n\n        var test = request(app).post('/')\n        test.write('user is tobi')\n        test.expect(200, '\"user is tobi\"', done)\n      })\n\n      it('should not invoke without a body', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          throw new Error('oops!')\n        }\n\n        request(app)\n          .get('/')\n          .expect(404, done)\n      })\n    })\n  })\n\n  describe('with verify option', function () {\n    it('should assert value is function', function () {\n      assert.throws(createApp.bind(null, { verify: 'lol' }),\n        /TypeError: option verify must be function/)\n    })\n\n    it('should error from verify', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x20) throw new Error('no leading space')\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'text/plain')\n        .send(' user is tobi')\n        .expect(403, '[entity.verify.failed] no leading space', done)\n    })\n\n    it('should allow custom codes', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] !== 0x20) return\n          var err = new Error('no leading space')\n          err.status = 400\n          throw err\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'text/plain')\n        .send(' user is tobi')\n        .expect(400, '[entity.verify.failed] no leading space', done)\n    })\n\n    it('should allow pass-through', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x20) throw new Error('no leading space')\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'text/plain')\n        .send('user is tobi')\n        .expect(200, '\"user is tobi\"', done)\n    })\n\n    it('should 415 on unknown charset prior to verify', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          throw new Error('unexpected verify call')\n        }\n      })\n\n      var test = request(app).post('/')\n      test.set('Content-Type', 'text/plain; charset=x-bogus')\n      test.write(Buffer.from('00000000', 'hex'))\n      test.expect(415, '[charset.unsupported] unsupported charset \"X-BOGUS\"', done)\n    })\n  })\n\n  describe('async local storage', function () {\n    before(function () {\n      var app = express()\n      var store = { foo: 'bar' }\n\n      app.use(function (req, res, next) {\n        req.asyncLocalStorage = new AsyncLocalStorage()\n        req.asyncLocalStorage.run(store, next)\n      })\n\n      app.use(express.text())\n\n      app.use(function (req, res, next) {\n        var local = req.asyncLocalStorage.getStore()\n\n        if (local) {\n          res.setHeader('x-store-foo', String(local.foo))\n        }\n\n        next()\n      })\n\n      app.use(function (err, req, res, next) {\n        var local = req.asyncLocalStorage.getStore()\n\n        if (local) {\n          res.setHeader('x-store-foo', String(local.foo))\n        }\n\n        res.status(err.status || 500)\n        res.send('[' + err.type + '] ' + err.message)\n      })\n\n      app.post('/', function (req, res) {\n        res.json(req.body)\n      })\n\n      this.app = app\n    })\n\n    it('should persist store', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'text/plain')\n        .send('user is tobi')\n        .expect(200)\n        .expect('x-store-foo', 'bar')\n        .expect('\"user is tobi\"')\n        .end(done)\n    })\n\n    it('should persist store when unmatched content-type', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/fizzbuzz')\n        .send('buzz')\n        .expect(200)\n        .expect('x-store-foo', 'bar')\n        .end(done)\n    })\n\n    it('should persist store when inflated', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))\n      test.expect(200)\n      test.expect('x-store-foo', 'bar')\n      test.expect('\"name is 论\"')\n      test.end(done)\n    })\n\n    it('should persist store when inflate error', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000', 'hex'))\n      test.expect(400)\n      test.expect('x-store-foo', 'bar')\n      test.end(done)\n    })\n\n    it('should persist store when limit exceeded', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'text/plain')\n        .send('user is ' + Buffer.alloc(1024 * 100, '.').toString())\n        .expect(413)\n        .expect('x-store-foo', 'bar')\n        .end(done)\n    })\n  })\n\n  describe('charset', function () {\n    before(function () {\n      this.app = createApp()\n    })\n\n    it('should parse utf-8', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'text/plain; charset=utf-8')\n      test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n\n    it('should parse codepage charsets', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'text/plain; charset=koi8-r')\n      test.write(Buffer.from('6e616d6520697320cec5d4', 'hex'))\n      test.expect(200, '\"name is нет\"', done)\n    })\n\n    it('should parse when content-length != char length', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'text/plain; charset=utf-8')\n      test.set('Content-Length', '11')\n      test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n\n    it('should default to utf-8', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n\n    it('should 415 on unknown charset', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'text/plain; charset=x-bogus')\n      test.write(Buffer.from('00000000', 'hex'))\n      test.expect(415, '[charset.unsupported] unsupported charset \"X-BOGUS\"', done)\n    })\n  })\n\n  describe('encoding', function () {\n    before(function () {\n      this.app = createApp({ limit: '10kb' })\n    })\n\n    it('should parse without encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n\n    it('should support identity encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'identity')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n\n    it('should support gzip encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n\n    it('should support deflate encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'deflate')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('789ccb4bcc4d55c82c5678b16e17001a6f050e', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n\n    it('should be case-insensitive', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'GZIP')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))\n      test.expect(200, '\"name is 论\"', done)\n    })\n\n    it('should 415 on unknown encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'nulls')\n      test.set('Content-Type', 'text/plain')\n      test.write(Buffer.from('000000000000', 'hex'))\n      test.expect(415, '[encoding.unsupported] unsupported content encoding \"nulls\"', done)\n    })\n  })\n})\n\nfunction createApp (options) {\n  var app = express()\n\n  app.use(express.text(options))\n\n  app.use(function (err, req, res, next) {\n    res.status(err.status || 500)\n    res.send(String(req.headers['x-error-property']\n      ? err[req.headers['x-error-property']]\n      : ('[' + err.type + '] ' + err.message)))\n  })\n\n  app.post('/', function (req, res) {\n    res.json(req.body)\n  })\n\n  return app\n}\n"
  },
  {
    "path": "test/express.urlencoded.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage\nconst { Buffer } = require('node:buffer');\n\nvar express = require('..')\nvar request = require('supertest')\n\ndescribe('express.urlencoded()', function () {\n  before(function () {\n    this.app = createApp()\n  })\n\n  it('should parse x-www-form-urlencoded', function (done) {\n    request(this.app)\n      .post('/')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send('user=tobi')\n      .expect(200, '{\"user\":\"tobi\"}', done)\n  })\n\n  it('should 400 when invalid content-length', function (done) {\n    var app = express()\n\n    app.use(function (req, res, next) {\n      req.headers['content-length'] = '20' // bad length\n      next()\n    })\n\n    app.use(express.urlencoded())\n\n    app.post('/', function (req, res) {\n      res.json(req.body)\n    })\n\n    request(app)\n      .post('/')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send('str=')\n      .expect(400, /content length/, done)\n  })\n\n  it('should handle Content-Length: 0', function (done) {\n    request(this.app)\n      .post('/')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .set('Content-Length', '0')\n      .send('')\n      .expect(200, '{}', done)\n  })\n\n  it('should handle empty message-body', function (done) {\n    request(createApp({ limit: '1kb' }))\n      .post('/')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .set('Transfer-Encoding', 'chunked')\n      .send('')\n      .expect(200, '{}', done)\n  })\n\n  it('should handle duplicated middleware', function (done) {\n    var app = express()\n\n    app.use(express.urlencoded())\n    app.use(express.urlencoded())\n\n    app.post('/', function (req, res) {\n      res.json(req.body)\n    })\n\n    request(app)\n      .post('/')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send('user=tobi')\n      .expect(200, '{\"user\":\"tobi\"}', done)\n  })\n\n  it('should not parse extended syntax', function (done) {\n    request(this.app)\n      .post('/')\n      .set('Content-Type', 'application/x-www-form-urlencoded')\n      .send('user[name][first]=Tobi')\n      .expect(200, '{\"user[name][first]\":\"Tobi\"}', done)\n  })\n\n  describe('with extended option', function () {\n    describe('when false', function () {\n      before(function () {\n        this.app = createApp({ extended: false })\n      })\n\n      it('should not parse extended syntax', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('user[name][first]=Tobi')\n          .expect(200, '{\"user[name][first]\":\"Tobi\"}', done)\n      })\n\n      it('should parse multiple key instances', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('user=Tobi&user=Loki')\n          .expect(200, '{\"user\":[\"Tobi\",\"Loki\"]}', done)\n      })\n    })\n\n    describe('when true', function () {\n      before(function () {\n        this.app = createApp({ extended: true })\n      })\n\n      it('should parse multiple key instances', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('user=Tobi&user=Loki')\n          .expect(200, '{\"user\":[\"Tobi\",\"Loki\"]}', done)\n      })\n\n      it('should parse extended syntax', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('user[name][first]=Tobi')\n          .expect(200, '{\"user\":{\"name\":{\"first\":\"Tobi\"}}}', done)\n      })\n\n      it('should parse parameters with dots', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('user.name=Tobi')\n          .expect(200, '{\"user.name\":\"Tobi\"}', done)\n      })\n\n      it('should parse fully-encoded extended syntax', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('user%5Bname%5D%5Bfirst%5D=Tobi')\n          .expect(200, '{\"user\":{\"name\":{\"first\":\"Tobi\"}}}', done)\n      })\n\n      it('should parse array index notation', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('foo[0]=bar&foo[1]=baz')\n          .expect(200, '{\"foo\":[\"bar\",\"baz\"]}', done)\n      })\n\n      it('should parse array index notation with large array', function (done) {\n        var str = 'f[0]=0'\n\n        for (var i = 1; i < 500; i++) {\n          str += '&f[' + i + ']=' + i.toString(16)\n        }\n\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(str)\n          .expect(function (res) {\n            var obj = JSON.parse(res.text)\n            assert.strictEqual(Object.keys(obj).length, 1)\n            assert.strictEqual(Array.isArray(obj.f), true)\n            assert.strictEqual(obj.f.length, 500)\n          })\n          .expect(200, done)\n      })\n\n      it('should parse array of objects syntax', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!')\n          .expect(200, '{\"foo\":[{\"bar\":\"baz\",\"fizz\":\"buzz\"},\"done!\"]}', done)\n      })\n\n      it('should parse deep object', function (done) {\n        var str = 'foo'\n\n        for (var i = 0; i < 32; i++) {\n          str += '[p]'\n        }\n\n        str += '=bar'\n\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(str)\n          .expect(function (res) {\n            var obj = JSON.parse(res.text)\n            assert.strictEqual(Object.keys(obj).length, 1)\n            assert.strictEqual(typeof obj.foo, 'object')\n\n            var depth = 0\n            var ref = obj.foo\n            while ((ref = ref.p)) { depth++ }\n            assert.strictEqual(depth, 32)\n          })\n          .expect(200, done)\n      })\n    })\n  })\n\n  describe('with inflate option', function () {\n    describe('when false', function () {\n      before(function () {\n        this.app = createApp({ inflate: false })\n      })\n\n      it('should not accept content-encoding', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Encoding', 'gzip')\n        test.set('Content-Type', 'application/x-www-form-urlencoded')\n        test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n        test.expect(415, '[encoding.unsupported] content encoding unsupported', done)\n      })\n    })\n\n    describe('when true', function () {\n      before(function () {\n        this.app = createApp({ inflate: true })\n      })\n\n      it('should accept content-encoding', function (done) {\n        var test = request(this.app).post('/')\n        test.set('Content-Encoding', 'gzip')\n        test.set('Content-Type', 'application/x-www-form-urlencoded')\n        test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n        test.expect(200, '{\"name\":\"论\"}', done)\n      })\n    })\n  })\n\n  describe('with limit option', function () {\n    it('should 413 when over limit with Content-Length', function (done) {\n      var buf = Buffer.alloc(1024, '.')\n      request(createApp({ limit: '1kb' }))\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .set('Content-Length', '1028')\n        .send('str=' + buf.toString())\n        .expect(413, done)\n    })\n\n    it('should 413 when over limit with chunked encoding', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var buf = Buffer.alloc(1024, '.')\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.set('Transfer-Encoding', 'chunked')\n      test.write('str=')\n      test.write(buf.toString())\n      test.expect(413, done)\n    })\n\n    it('should 413 when inflated body over limit', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex'))\n      test.expect(413, done)\n    })\n\n    it('should accept number of bytes', function (done) {\n      var buf = Buffer.alloc(1024, '.')\n      request(createApp({ limit: 1024 }))\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .send('str=' + buf.toString())\n        .expect(413, done)\n    })\n\n    it('should not change when options altered', function (done) {\n      var buf = Buffer.alloc(1024, '.')\n      var options = { limit: '1kb' }\n      var app = createApp(options)\n\n      options.limit = '100kb'\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .send('str=' + buf.toString())\n        .expect(413, done)\n    })\n\n    it('should not hang response', function (done) {\n      var app = createApp({ limit: '8kb' })\n      var buf = Buffer.alloc(10240, '.')\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(buf)\n      test.write(buf)\n      test.write(buf)\n      test.expect(413, done)\n    })\n\n    it('should not error when inflating', function (done) {\n      var app = createApp({ limit: '1kb' })\n      var test = request(app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex'))\n      test.expect(413, done)\n    })\n  })\n\n  describe('with parameterLimit option', function () {\n    describe('with extended: false', function () {\n      it('should reject 0', function () {\n        assert.throws(createApp.bind(null, { extended: false, parameterLimit: 0 }),\n          /TypeError: option parameterLimit must be a positive number/)\n      })\n\n      it('should reject string', function () {\n        assert.throws(createApp.bind(null, { extended: false, parameterLimit: 'beep' }),\n          /TypeError: option parameterLimit must be a positive number/)\n      })\n\n      it('should 413 if over limit', function (done) {\n        request(createApp({ extended: false, parameterLimit: 10 }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(11))\n          .expect(413, '[parameters.too.many] too many parameters', done)\n      })\n\n      it('should work when at the limit', function (done) {\n        request(createApp({ extended: false, parameterLimit: 10 }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(10))\n          .expect(expectKeyCount(10))\n          .expect(200, done)\n      })\n\n      it('should work if number is floating point', function (done) {\n        request(createApp({ extended: false, parameterLimit: 10.1 }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(11))\n          .expect(413, /too many parameters/, done)\n      })\n\n      it('should work with large limit', function (done) {\n        request(createApp({ extended: false, parameterLimit: 5000 }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(5000))\n          .expect(expectKeyCount(5000))\n          .expect(200, done)\n      })\n\n      it('should work with Infinity limit', function (done) {\n        request(createApp({ extended: false, parameterLimit: Infinity }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(10000))\n          .expect(expectKeyCount(10000))\n          .expect(200, done)\n      })\n    })\n\n    describe('with extended: true', function () {\n      it('should reject 0', function () {\n        assert.throws(createApp.bind(null, { extended: true, parameterLimit: 0 }),\n          /TypeError: option parameterLimit must be a positive number/)\n      })\n\n      it('should reject string', function () {\n        assert.throws(createApp.bind(null, { extended: true, parameterLimit: 'beep' }),\n          /TypeError: option parameterLimit must be a positive number/)\n      })\n\n      it('should 413 if over limit', function (done) {\n        request(createApp({ extended: true, parameterLimit: 10 }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(11))\n          .expect(413, '[parameters.too.many] too many parameters', done)\n      })\n\n      it('should work when at the limit', function (done) {\n        request(createApp({ extended: true, parameterLimit: 10 }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(10))\n          .expect(expectKeyCount(10))\n          .expect(200, done)\n      })\n\n      it('should work if number is floating point', function (done) {\n        request(createApp({ extended: true, parameterLimit: 10.1 }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(11))\n          .expect(413, /too many parameters/, done)\n      })\n\n      it('should work with large limit', function (done) {\n        request(createApp({ extended: true, parameterLimit: 5000 }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(5000))\n          .expect(expectKeyCount(5000))\n          .expect(200, done)\n      })\n\n      it('should work with Infinity limit', function (done) {\n        request(createApp({ extended: true, parameterLimit: Infinity }))\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send(createManyParams(10000))\n          .expect(expectKeyCount(10000))\n          .expect(200, done)\n      })\n    })\n  })\n\n  describe('with type option', function () {\n    describe('when \"application/vnd.x-www-form-urlencoded\"', function () {\n      before(function () {\n        this.app = createApp({ type: 'application/vnd.x-www-form-urlencoded' })\n      })\n\n      it('should parse for custom type', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/vnd.x-www-form-urlencoded')\n          .send('user=tobi')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should ignore standard type', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('user=tobi')\n          .expect(200, '', done)\n      })\n    })\n\n    describe('when [\"urlencoded\", \"application/x-pairs\"]', function () {\n      before(function () {\n        this.app = createApp({\n          type: ['urlencoded', 'application/x-pairs']\n        })\n      })\n\n      it('should parse \"application/x-www-form-urlencoded\"', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-www-form-urlencoded')\n          .send('user=tobi')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should parse \"application/x-pairs\"', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-pairs')\n          .send('user=tobi')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should ignore application/x-foo', function (done) {\n        request(this.app)\n          .post('/')\n          .set('Content-Type', 'application/x-foo')\n          .send('user=tobi')\n          .expect(200, '', done)\n      })\n    })\n\n    describe('when a function', function () {\n      it('should parse when truthy value returned', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          return req.headers['content-type'] === 'application/vnd.something'\n        }\n\n        request(app)\n          .post('/')\n          .set('Content-Type', 'application/vnd.something')\n          .send('user=tobi')\n          .expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should work without content-type', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          return true\n        }\n\n        var test = request(app).post('/')\n        test.write('user=tobi')\n        test.expect(200, '{\"user\":\"tobi\"}', done)\n      })\n\n      it('should not invoke without a body', function (done) {\n        var app = createApp({ type: accept })\n\n        function accept (req) {\n          throw new Error('oops!')\n        }\n\n        request(app)\n          .get('/')\n          .expect(404, done)\n      })\n    })\n  })\n\n  describe('with verify option', function () {\n    it('should assert value if function', function () {\n      assert.throws(createApp.bind(null, { verify: 'lol' }),\n        /TypeError: option verify must be function/)\n    })\n\n    it('should error from verify', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x20) throw new Error('no leading space')\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .send(' user=tobi')\n        .expect(403, '[entity.verify.failed] no leading space', done)\n    })\n\n    it('should allow custom codes', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] !== 0x20) return\n          var err = new Error('no leading space')\n          err.status = 400\n          throw err\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .send(' user=tobi')\n        .expect(400, '[entity.verify.failed] no leading space', done)\n    })\n\n    it('should allow custom type', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] !== 0x20) return\n          var err = new Error('no leading space')\n          err.type = 'foo.bar'\n          throw err\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .send(' user=tobi')\n        .expect(403, '[foo.bar] no leading space', done)\n    })\n\n    it('should allow pass-through', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          if (buf[0] === 0x5b) throw new Error('no arrays')\n        }\n      })\n\n      request(app)\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .send('user=tobi')\n        .expect(200, '{\"user\":\"tobi\"}', done)\n    })\n\n    it('should 415 on unknown charset prior to verify', function (done) {\n      var app = createApp({\n        verify: function (req, res, buf) {\n          throw new Error('unexpected verify call')\n        }\n      })\n\n      var test = request(app).post('/')\n      test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus')\n      test.write(Buffer.from('00000000', 'hex'))\n      test.expect(415, '[charset.unsupported] unsupported charset \"X-BOGUS\"', done)\n    })\n  })\n\n  describe('async local storage', function () {\n    before(function () {\n      var app = express()\n      var store = { foo: 'bar' }\n\n      app.use(function (req, res, next) {\n        req.asyncLocalStorage = new AsyncLocalStorage()\n        req.asyncLocalStorage.run(store, next)\n      })\n\n      app.use(express.urlencoded())\n\n      app.use(function (req, res, next) {\n        var local = req.asyncLocalStorage.getStore()\n\n        if (local) {\n          res.setHeader('x-store-foo', String(local.foo))\n        }\n\n        next()\n      })\n\n      app.use(function (err, req, res, next) {\n        var local = req.asyncLocalStorage.getStore()\n\n        if (local) {\n          res.setHeader('x-store-foo', String(local.foo))\n        }\n\n        res.status(err.status || 500)\n        res.send('[' + err.type + '] ' + err.message)\n      })\n\n      app.post('/', function (req, res) {\n        res.json(req.body)\n      })\n\n      this.app = app\n    })\n\n    it('should persist store', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .send('user=tobi')\n        .expect(200)\n        .expect('x-store-foo', 'bar')\n        .expect('{\"user\":\"tobi\"}')\n        .end(done)\n    })\n\n    it('should persist store when unmatched content-type', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/fizzbuzz')\n        .send('buzz')\n        .expect(200)\n        .expect('x-store-foo', 'bar')\n        .end(done)\n    })\n\n    it('should persist store when inflated', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n      test.expect(200)\n      test.expect('x-store-foo', 'bar')\n      test.expect('{\"name\":\"论\"}')\n      test.end(done)\n    })\n\n    it('should persist store when inflate error', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))\n      test.expect(400)\n      test.expect('x-store-foo', 'bar')\n      test.end(done)\n    })\n\n    it('should persist store when limit exceeded', function (done) {\n      request(this.app)\n        .post('/')\n        .set('Content-Type', 'application/x-www-form-urlencoded')\n        .send('user=' + Buffer.alloc(1024 * 100, '.').toString())\n        .expect(413)\n        .expect('x-store-foo', 'bar')\n        .end(done)\n    })\n  })\n\n  describe('charset', function () {\n    before(function () {\n      this.app = createApp()\n    })\n\n    it('should parse utf-8', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')\n      test.write(Buffer.from('6e616d653de8aeba', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should parse when content-length != char length', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')\n      test.set('Content-Length', '7')\n      test.write(Buffer.from('746573743dc3a5', 'hex'))\n      test.expect(200, '{\"test\":\"å\"}', done)\n    })\n\n    it('should default to utf-8', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('6e616d653de8aeba', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should fail on unknown charset', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r')\n      test.write(Buffer.from('6e616d653dcec5d4', 'hex'))\n      test.expect(415, '[charset.unsupported] unsupported charset \"KOI8-R\"', done)\n    })\n  })\n\n  describe('encoding', function () {\n    before(function () {\n      this.app = createApp({ limit: '10kb' })\n    })\n\n    it('should parse without encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('6e616d653de8aeba', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should support identity encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'identity')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('6e616d653de8aeba', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should support gzip encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'gzip')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should support deflate encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'deflate')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should be case-insensitive', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'GZIP')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))\n      test.expect(200, '{\"name\":\"论\"}', done)\n    })\n\n    it('should 415 on unknown encoding', function (done) {\n      var test = request(this.app).post('/')\n      test.set('Content-Encoding', 'nulls')\n      test.set('Content-Type', 'application/x-www-form-urlencoded')\n      test.write(Buffer.from('000000000000', 'hex'))\n      test.expect(415, '[encoding.unsupported] unsupported content encoding \"nulls\"', done)\n    })\n  })\n})\n\nfunction createManyParams (count) {\n  var str = ''\n\n  if (count === 0) {\n    return str\n  }\n\n  str += '0=0'\n\n  for (var i = 1; i < count; i++) {\n    var n = i.toString(36)\n    str += '&' + n + '=' + n\n  }\n\n  return str\n}\n\nfunction createApp (options) {\n  var app = express()\n\n  app.use(express.urlencoded(options))\n\n  app.use(function (err, req, res, next) {\n    res.status(err.status || 500)\n    res.send(String(req.headers['x-error-property']\n      ? err[req.headers['x-error-property']]\n      : ('[' + err.type + '] ' + err.message)))\n  })\n\n  app.post('/', function (req, res) {\n    res.json(req.body)\n  })\n\n  return app\n}\n\nfunction expectKeyCount (count) {\n  return function (res) {\n    assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count)\n  }\n}\n"
  },
  {
    "path": "test/fixtures/% of dogs.txt",
    "content": "20%"
  },
  {
    "path": "test/fixtures/.name",
    "content": "tobi"
  },
  {
    "path": "test/fixtures/blog/index.html",
    "content": "<b>index</b>"
  },
  {
    "path": "test/fixtures/blog/post/index.tmpl",
    "content": "<h1>blog post</h1>"
  },
  {
    "path": "test/fixtures/broken.send",
    "content": ""
  },
  {
    "path": "test/fixtures/default_layout/name.tmpl",
    "content": "<p>$name</p>"
  },
  {
    "path": "test/fixtures/default_layout/user.tmpl",
    "content": "<p>$user.name</p>"
  },
  {
    "path": "test/fixtures/email.tmpl",
    "content": "<p>This is an email</p>"
  },
  {
    "path": "test/fixtures/empty.txt",
    "content": ""
  },
  {
    "path": "test/fixtures/local_layout/user.tmpl",
    "content": "<span>$user.name</span>"
  },
  {
    "path": "test/fixtures/name.tmpl",
    "content": "<p>$name</p>"
  },
  {
    "path": "test/fixtures/name.txt",
    "content": "tobi"
  },
  {
    "path": "test/fixtures/nums.txt",
    "content": "123456789"
  },
  {
    "path": "test/fixtures/pets/names.txt",
    "content": "tobi,loki"
  },
  {
    "path": "test/fixtures/snow ☃/.gitkeep",
    "content": ""
  },
  {
    "path": "test/fixtures/todo.html",
    "content": "<li>groceries</li>"
  },
  {
    "path": "test/fixtures/todo.txt",
    "content": "- groceries"
  },
  {
    "path": "test/fixtures/user.html",
    "content": "<p>{{user.name}}</p>"
  },
  {
    "path": "test/fixtures/user.tmpl",
    "content": "<p>$user.name</p>"
  },
  {
    "path": "test/fixtures/users/index.html",
    "content": "<p>tobi, loki, jane</p>"
  },
  {
    "path": "test/fixtures/users/tobi.txt",
    "content": "ferret"
  },
  {
    "path": "test/middleware.basic.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('../');\nvar request = require('supertest');\n\ndescribe('middleware', function(){\n  describe('.next()', function(){\n    it('should behave like connect', function(done){\n      var app = express()\n        , calls = [];\n\n      app.use(function(req, res, next){\n        calls.push('one');\n        next();\n      });\n\n      app.use(function(req, res, next){\n        calls.push('two');\n        next();\n      });\n\n      app.use(function(req, res){\n        var buf = '';\n        res.setHeader('Content-Type', 'application/json');\n        req.setEncoding('utf8');\n        req.on('data', function(chunk){ buf += chunk });\n        req.on('end', function(){\n          res.end(buf);\n        });\n      });\n\n      request(app)\n      .get('/')\n      .set('Content-Type', 'application/json')\n      .send('{\"foo\":\"bar\"}')\n      .expect('Content-Type', 'application/json')\n      .expect(function () { assert.deepEqual(calls, ['one', 'two']) })\n      .expect(200, '{\"foo\":\"bar\"}', done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/regression.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('throw after .end()', function(){\n  it('should fail gracefully', function(done){\n    var app = express();\n\n    app.get('/', function(req, res){\n      res.end('yay');\n      throw new Error('boom');\n    });\n\n    request(app)\n    .get('/')\n    .expect('yay')\n    .expect(200, done);\n  })\n})\n"
  },
  {
    "path": "test/req.accepts.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.accepts(type)', function(){\n    it('should return true when Accept is not present', function(done){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.end(req.accepts('json') ? 'yes' : 'no');\n      });\n\n      request(app)\n      .get('/')\n      .expect('yes', done);\n    })\n\n    it('should return true when present', function(done){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.end(req.accepts('json') ? 'yes' : 'no');\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', 'application/json')\n      .expect('yes', done);\n    })\n\n    it('should return false otherwise', function(done){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.end(req.accepts('json') ? 'yes' : 'no');\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', 'text/html')\n      .expect('no', done);\n    })\n  })\n\n  it('should accept an argument list of type names', function(done){\n    var app = express();\n\n    app.use(function(req, res, next){\n      res.end(req.accepts('json', 'html'));\n    });\n\n    request(app)\n    .get('/')\n    .set('Accept', 'application/json')\n    .expect('json', done);\n  })\n\n  describe('.accepts(types)', function(){\n    it('should return the first when Accept is not present', function(done){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.end(req.accepts(['json', 'html']));\n      });\n\n      request(app)\n      .get('/')\n      .expect('json', done);\n    })\n\n    it('should return the first acceptable type', function(done){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.end(req.accepts(['json', 'html']));\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', 'text/html')\n      .expect('html', done);\n    })\n\n    it('should return false when no match is made', function(done){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.end(req.accepts(['text/html', 'application/json']) ? 'yup' : 'nope');\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', 'foo/bar, bar/baz')\n      .expect('nope', done);\n    })\n\n    it('should take quality into account', function(done){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.end(req.accepts(['text/html', 'application/json']));\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', '*/html; q=.5, application/json')\n      .expect('application/json', done);\n    })\n\n    it('should return the first acceptable type with canonical mime types', function(done){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.end(req.accepts(['application/json', 'text/html']));\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', '*/html')\n      .expect('text/html', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.acceptsCharsets.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.acceptsCharsets(type)', function(){\n    describe('when Accept-Charset is not present', function(){\n      it('should return true', function(done){\n        var app = express();\n\n        app.use(function(req, res, next){\n          res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');\n        });\n\n        request(app)\n        .get('/')\n        .expect('yes', done);\n      })\n    })\n\n    describe('when Accept-Charset is present', function () {\n      it('should return true', function (done) {\n        var app = express();\n\n        app.use(function(req, res, next){\n          res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');\n        });\n\n        request(app)\n        .get('/')\n        .set('Accept-Charset', 'foo, bar, utf-8')\n        .expect('yes', done);\n      })\n\n      it('should return false otherwise', function(done){\n        var app = express();\n\n        app.use(function(req, res, next){\n          res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');\n        });\n\n        request(app)\n        .get('/')\n        .set('Accept-Charset', 'foo, bar')\n        .expect('no', done);\n      })\n\n      it('should return the best matching charset from multiple inputs', function (done) {\n        var app = express();\n\n        app.use(function(req, res, next){\n          res.end(req.acceptsCharsets('utf-8', 'iso-8859-1'));\n        });\n\n        request(app)\n        .get('/')\n        .set('Accept-Charset', 'iso-8859-1, utf-8')\n        .expect('iso-8859-1', done);\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.acceptsEncodings.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.acceptsEncodings', function () {\n    it('should return encoding if accepted', function (done) {\n      var app = express();\n\n      app.get('/', function (req, res) {\n        res.send({\n          gzip: req.acceptsEncodings('gzip'),\n          deflate: req.acceptsEncodings('deflate')\n        })\n      })\n\n      request(app)\n        .get('/')\n        .set('Accept-Encoding', ' gzip, deflate')\n        .expect(200, { gzip: 'gzip', deflate: 'deflate' }, done)\n    })\n\n    it('should be false if encoding not accepted', function(done){\n      var app = express();\n\n      app.get('/', function (req, res) {\n        res.send({\n          bogus: req.acceptsEncodings('bogus')\n        })\n      })\n\n      request(app)\n        .get('/')\n        .set('Accept-Encoding', ' gzip, deflate')\n        .expect(200, { bogus: false }, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.acceptsLanguages.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.acceptsLanguages', function(){\n    it('should return language if accepted', function (done) {\n      var app = express();\n\n      app.get('/', function (req, res) {\n        res.send({\n          'en-us': req.acceptsLanguages('en-us'),\n          en: req.acceptsLanguages('en')\n        })\n      })\n\n      request(app)\n        .get('/')\n        .set('Accept-Language', 'en;q=.5, en-us')\n        .expect(200, { 'en-us': 'en-us', en: 'en' }, done)\n    })\n\n    it('should be false if language not accepted', function(done){\n      var app = express();\n\n      app.get('/', function (req, res) {\n        res.send({\n          es: req.acceptsLanguages('es')\n        })\n      })\n\n      request(app)\n        .get('/')\n        .set('Accept-Language', 'en;q=.5, en-us')\n        .expect(200, { es: false }, done)\n    })\n\n    describe('when Accept-Language is not present', function(){\n      it('should always return language', function (done) {\n        var app = express();\n\n        app.get('/', function (req, res) {\n          res.send({\n            en: req.acceptsLanguages('en'),\n            es: req.acceptsLanguages('es'),\n            jp: req.acceptsLanguages('jp')\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200, { en: 'en', es: 'es', jp: 'jp' }, done)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.baseUrl.js",
    "content": "'use strict'\n\nvar express = require('..')\nvar request = require('supertest')\n\ndescribe('req', function(){\n  describe('.baseUrl', function(){\n    it('should be empty for top-level route', function(done){\n      var app = express()\n\n      app.get('/:a', function(req, res){\n        res.end(req.baseUrl)\n      })\n\n      request(app)\n      .get('/foo')\n      .expect(200, '', done)\n    })\n\n    it('should contain lower path', function(done){\n      var app = express()\n      var sub = express.Router()\n\n      sub.get('/:b', function(req, res){\n        res.end(req.baseUrl)\n      })\n      app.use('/:a', sub)\n\n      request(app)\n      .get('/foo/bar')\n      .expect(200, '/foo', done);\n    })\n\n    it('should contain full lower path', function(done){\n      var app = express()\n      var sub1 = express.Router()\n      var sub2 = express.Router()\n      var sub3 = express.Router()\n\n      sub3.get('/:d', function(req, res){\n        res.end(req.baseUrl)\n      })\n      sub2.use('/:c', sub3)\n      sub1.use('/:b', sub2)\n      app.use('/:a', sub1)\n\n      request(app)\n      .get('/foo/bar/baz/zed')\n      .expect(200, '/foo/bar/baz', done);\n    })\n\n    it('should travel through routers correctly', function(done){\n      var urls = []\n      var app = express()\n      var sub1 = express.Router()\n      var sub2 = express.Router()\n      var sub3 = express.Router()\n\n      sub3.get('/:d', function(req, res, next){\n        urls.push('0@' + req.baseUrl)\n        next()\n      })\n      sub2.use('/:c', sub3)\n      sub1.use('/', function(req, res, next){\n        urls.push('1@' + req.baseUrl)\n        next()\n      })\n      sub1.use('/bar', sub2)\n      sub1.use('/bar', function(req, res, next){\n        urls.push('2@' + req.baseUrl)\n        next()\n      })\n      app.use(function(req, res, next){\n        urls.push('3@' + req.baseUrl)\n        next()\n      })\n      app.use('/:a', sub1)\n      app.use(function(req, res, next){\n        urls.push('4@' + req.baseUrl)\n        res.end(urls.join(','))\n      })\n\n      request(app)\n      .get('/foo/bar/baz/zed')\n      .expect(200, '3@,1@/foo,0@/foo/bar/baz,2@/foo/bar,4@', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.fresh.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.fresh', function(){\n    it('should return true when the resource is not modified', function(done){\n      var app = express();\n      var etag = '\"12345\"';\n\n      app.use(function(req, res){\n        res.set('ETag', etag);\n        res.send(req.fresh);\n      });\n\n      request(app)\n      .get('/')\n      .set('If-None-Match', etag)\n      .expect(304, done);\n    })\n\n    it('should return false when the resource is modified', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set('ETag', '\"123\"');\n        res.send(req.fresh);\n      });\n\n      request(app)\n      .get('/')\n      .set('If-None-Match', '\"12345\"')\n      .expect(200, 'false', done);\n    })\n\n    it('should return false without response headers', function(done){\n      var app = express();\n\n      app.disable('x-powered-by')\n      app.use(function(req, res){\n        res.send(req.fresh);\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, 'false', done);\n    })\n\n    it('should ignore \"If-Modified-Since\" when \"If-None-Match\" is present', function(done) {\n      var app = express();\n      const etag = '\"FooBar\"'\n      const now = Date.now()\n\n      app.disable('x-powered-by')\n      app.use(function(req, res) {\n        res.set('Etag', etag)\n        res.set('Last-Modified', new Date(now).toUTCString())\n        res.send(req.fresh);\n      });\n\n      request(app)\n        .get('/')\n        .set('If-Modified-Since', new Date(now - 1000).toUTCString)\n        .set('If-None-Match', etag)\n        .expect(304, done);\n    })\n\n  })\n})\n"
  },
  {
    "path": "test/req.get.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest')\n  , assert = require('node:assert');\n\ndescribe('req', function(){\n  describe('.get(field)', function(){\n    it('should return the header field value', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        assert(req.get('Something-Else') === undefined);\n        res.end(req.get('Content-Type'));\n      });\n\n      request(app)\n      .post('/')\n      .set('Content-Type', 'application/json')\n      .expect('application/json', done);\n    })\n\n    it('should special-case Referer', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.get('Referer'));\n      });\n\n      request(app)\n      .post('/')\n      .set('Referrer', 'http://foobar.com')\n      .expect('http://foobar.com', done);\n    })\n\n    it('should throw missing header name', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.end(req.get())\n      })\n\n      request(app)\n      .get('/')\n      .expect(500, /TypeError: name argument is required to req.get/, done)\n    })\n\n    it('should throw for non-string header name', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.end(req.get(42))\n      })\n\n      request(app)\n      .get('/')\n      .expect(500, /TypeError: name must be a string to req.get/, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.host.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest')\n\ndescribe('req', function(){\n  describe('.host', function(){\n    it('should return the Host when present', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.host);\n      });\n\n      request(app)\n      .post('/')\n      .set('Host', 'example.com')\n      .expect('example.com', done);\n    })\n\n    it('should strip port number', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.host);\n      });\n\n      request(app)\n      .post('/')\n      .set('Host', 'example.com:3000')\n      .expect(200, 'example.com:3000', done);\n    })\n\n    it('should return undefined otherwise', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        req.headers.host = null;\n        res.end(String(req.host));\n      });\n\n      request(app)\n      .post('/')\n      .expect('undefined', done);\n    })\n\n    it('should work with IPv6 Host', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.host);\n      });\n\n      request(app)\n      .post('/')\n      .set('Host', '[::1]')\n      .expect('[::1]', done);\n    })\n\n    it('should work with IPv6 Host and port', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.host);\n      });\n\n      request(app)\n      .post('/')\n      .set('Host', '[::1]:3000')\n      .expect(200, '[::1]:3000', done);\n    })\n\n    describe('when \"trust proxy\" is enabled', function(){\n      it('should respect X-Forwarded-Host', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.use(function(req, res){\n          res.end(req.host);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'localhost')\n        .set('X-Forwarded-Host', 'example.com')\n        .expect('example.com', done);\n      })\n\n      it('should ignore X-Forwarded-Host if socket addr not trusted', function(done){\n        var app = express();\n\n        app.set('trust proxy', '10.0.0.1');\n\n        app.use(function(req, res){\n          res.end(req.host);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'localhost')\n        .set('X-Forwarded-Host', 'example.com')\n        .expect('localhost', done);\n      })\n\n      it('should default to Host', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.use(function(req, res){\n          res.end(req.host);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'example.com')\n        .expect('example.com', done);\n      })\n\n      describe('when trusting hop count', function () {\n        it('should respect X-Forwarded-Host', function (done) {\n          var app = express();\n\n          app.set('trust proxy', 1);\n\n          app.use(function (req, res) {\n            res.end(req.host);\n          });\n\n          request(app)\n          .get('/')\n          .set('Host', 'localhost')\n          .set('X-Forwarded-Host', 'example.com')\n          .expect('example.com', done);\n        })\n      })\n    })\n\n    describe('when \"trust proxy\" is disabled', function(){\n      it('should ignore X-Forwarded-Host', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.end(req.host);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'localhost')\n        .set('X-Forwarded-Host', 'evil')\n        .expect('localhost', done);\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.hostname.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest')\n\ndescribe('req', function(){\n  describe('.hostname', function(){\n    it('should return the Host when present', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.hostname);\n      });\n\n      request(app)\n      .post('/')\n      .set('Host', 'example.com')\n      .expect('example.com', done);\n    })\n\n    it('should strip port number', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.hostname);\n      });\n\n      request(app)\n      .post('/')\n      .set('Host', 'example.com:3000')\n      .expect('example.com', done);\n    })\n\n    it('should return undefined otherwise', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        req.headers.host = null;\n        res.end(String(req.hostname));\n      });\n\n      request(app)\n      .post('/')\n      .expect('undefined', done);\n    })\n\n    it('should work with IPv6 Host', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.hostname);\n      });\n\n      request(app)\n      .post('/')\n      .set('Host', '[::1]')\n      .expect('[::1]', done);\n    })\n\n    it('should work with IPv6 Host and port', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.hostname);\n      });\n\n      request(app)\n      .post('/')\n      .set('Host', '[::1]:3000')\n      .expect('[::1]', done);\n    })\n\n    describe('when \"trust proxy\" is enabled', function(){\n      it('should respect X-Forwarded-Host', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.use(function(req, res){\n          res.end(req.hostname);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'localhost')\n        .set('X-Forwarded-Host', 'example.com:3000')\n        .expect('example.com', done);\n      })\n\n      it('should ignore X-Forwarded-Host if socket addr not trusted', function(done){\n        var app = express();\n\n        app.set('trust proxy', '10.0.0.1');\n\n        app.use(function(req, res){\n          res.end(req.hostname);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'localhost')\n        .set('X-Forwarded-Host', 'example.com')\n        .expect('localhost', done);\n      })\n\n      it('should default to Host', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.use(function(req, res){\n          res.end(req.hostname);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'example.com')\n        .expect('example.com', done);\n      })\n\n      describe('when multiple X-Forwarded-Host', function () {\n        it('should use the first value', function (done) {\n          var app = express()\n\n          app.enable('trust proxy')\n\n          app.use(function (req, res) {\n            res.send(req.hostname)\n          })\n\n          request(app)\n          .get('/')\n          .set('Host', 'localhost')\n          .set('X-Forwarded-Host', 'example.com, foobar.com')\n          .expect(200, 'example.com', done)\n        })\n\n        it('should remove OWS around comma', function (done) {\n          var app = express()\n\n          app.enable('trust proxy')\n\n          app.use(function (req, res) {\n            res.send(req.hostname)\n          })\n\n          request(app)\n          .get('/')\n          .set('Host', 'localhost')\n          .set('X-Forwarded-Host', 'example.com , foobar.com')\n          .expect(200, 'example.com', done)\n        })\n\n        it('should strip port number', function (done) {\n          var app = express()\n\n          app.enable('trust proxy')\n\n          app.use(function (req, res) {\n            res.send(req.hostname)\n          })\n\n          request(app)\n          .get('/')\n          .set('Host', 'localhost')\n          .set('X-Forwarded-Host', 'example.com:8080 , foobar.com:8888')\n          .expect(200, 'example.com', done)\n        })\n      })\n    })\n\n    describe('when \"trust proxy\" is disabled', function(){\n      it('should ignore X-Forwarded-Host', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.end(req.hostname);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'localhost')\n        .set('X-Forwarded-Host', 'evil')\n        .expect('localhost', done);\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.ip.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.ip', function(){\n    describe('when X-Forwarded-For is present', function(){\n      describe('when \"trust proxy\" is enabled', function(){\n        it('should return the client addr', function(done){\n          var app = express();\n\n          app.enable('trust proxy');\n\n          app.use(function(req, res, next){\n            res.send(req.ip);\n          });\n\n          request(app)\n          .get('/')\n          .set('X-Forwarded-For', 'client, p1, p2')\n          .expect('client', done);\n        })\n\n        it('should return the addr after trusted proxy based on count', function (done) {\n          var app = express();\n\n          app.set('trust proxy', 2);\n\n          app.use(function(req, res, next){\n            res.send(req.ip);\n          });\n\n          request(app)\n          .get('/')\n          .set('X-Forwarded-For', 'client, p1, p2')\n          .expect('p1', done);\n        })\n\n        it('should return the addr after trusted proxy based on list', function (done) {\n          var app = express()\n\n          app.set('trust proxy', '10.0.0.1, 10.0.0.2, 127.0.0.1, ::1')\n\n          app.get('/', function (req, res) {\n            res.send(req.ip)\n          })\n\n          request(app)\n            .get('/')\n            .set('X-Forwarded-For', '10.0.0.2, 10.0.0.3, 10.0.0.1', '10.0.0.4')\n            .expect('10.0.0.3', done)\n        })\n\n        it('should return the addr after trusted proxy, from sub app', function (done) {\n          var app = express();\n          var sub = express();\n\n          app.set('trust proxy', 2);\n          app.use(sub);\n\n          sub.use(function (req, res, next) {\n            res.send(req.ip);\n          });\n\n          request(app)\n          .get('/')\n          .set('X-Forwarded-For', 'client, p1, p2')\n          .expect(200, 'p1', done);\n        })\n      })\n\n      describe('when \"trust proxy\" is disabled', function(){\n        it('should return the remote address', function(done){\n          var app = express();\n\n          app.use(function(req, res, next){\n            res.send(req.ip);\n          });\n\n          var test = request(app).get('/')\n          test.set('X-Forwarded-For', 'client, p1, p2')\n          test.expect(200, getExpectedClientAddress(test._server), done);\n        })\n      })\n    })\n\n    describe('when X-Forwarded-For is not present', function(){\n      it('should return the remote address', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.use(function(req, res, next){\n          res.send(req.ip);\n        });\n\n        var test = request(app).get('/')\n        test.expect(200, getExpectedClientAddress(test._server), done)\n      })\n    })\n  })\n})\n\n/**\n * Get the local client address depending on AF_NET of server\n */\n\nfunction getExpectedClientAddress(server) {\n  return server.address().address === '::'\n    ? '::ffff:127.0.0.1'\n    : '127.0.0.1';\n}\n"
  },
  {
    "path": "test/req.ips.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.ips', function(){\n    describe('when X-Forwarded-For is present', function(){\n      describe('when \"trust proxy\" is enabled', function(){\n        it('should return an array of the specified addresses', function(done){\n          var app = express();\n\n          app.enable('trust proxy');\n\n          app.use(function(req, res, next){\n            res.send(req.ips);\n          });\n\n          request(app)\n          .get('/')\n          .set('X-Forwarded-For', 'client, p1, p2')\n          .expect('[\"client\",\"p1\",\"p2\"]', done);\n        })\n\n        it('should stop at first untrusted', function(done){\n          var app = express();\n\n          app.set('trust proxy', 2);\n\n          app.use(function(req, res, next){\n            res.send(req.ips);\n          });\n\n          request(app)\n          .get('/')\n          .set('X-Forwarded-For', 'client, p1, p2')\n          .expect('[\"p1\",\"p2\"]', done);\n        })\n      })\n\n      describe('when \"trust proxy\" is disabled', function(){\n        it('should return an empty array', function(done){\n          var app = express();\n\n          app.use(function(req, res, next){\n            res.send(req.ips);\n          });\n\n          request(app)\n          .get('/')\n          .set('X-Forwarded-For', 'client, p1, p2')\n          .expect('[]', done);\n        })\n      })\n    })\n\n    describe('when X-Forwarded-For is not present', function(){\n      it('should return []', function(done){\n        var app = express();\n\n        app.use(function(req, res, next){\n          res.send(req.ips);\n        });\n\n        request(app)\n        .get('/')\n        .expect('[]', done);\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.is.js",
    "content": "'use strict'\n\nvar express = require('..')\nvar request = require('supertest')\n\ndescribe('req.is()', function () {\n  describe('when given a mime type', function () {\n    it('should return the type when matching', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('application/json'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json')\n      .send('{}')\n      .expect(200, '\"application/json\"', done)\n    })\n\n    it('should return false when not matching', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('image/jpeg'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json')\n      .send('{}')\n      .expect(200, 'false', done)\n    })\n\n    it('should ignore charset', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('application/json'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json; charset=UTF-8')\n      .send('{}')\n      .expect(200, '\"application/json\"', done)\n    })\n  })\n\n  describe('when content-type is not present', function(){\n    it('should return false', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('application/json'))\n      })\n\n      request(app)\n      .post('/')\n      .send('{}')\n      .expect(200, 'false', done)\n    })\n  })\n\n  describe('when given an extension', function(){\n    it('should lookup the mime type', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('json'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json')\n      .send('{}')\n      .expect(200, '\"json\"', done)\n    })\n  })\n\n  describe('when given */subtype', function(){\n    it('should return the full type when matching', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('*/json'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json')\n      .send('{}')\n      .expect(200, '\"application/json\"', done)\n    })\n\n    it('should return false when not matching', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('*/html'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json')\n      .send('{}')\n      .expect(200, 'false', done)\n    })\n\n    it('should ignore charset', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('*/json'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json; charset=UTF-8')\n      .send('{}')\n      .expect(200, '\"application/json\"', done)\n    })\n  })\n\n  describe('when given type/*', function(){\n    it('should return the full type when matching', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('application/*'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json')\n      .send('{}')\n      .expect(200, '\"application/json\"', done)\n    })\n\n    it('should return false when not matching', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('text/*'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json')\n      .send('{}')\n      .expect(200, 'false', done)\n    })\n\n    it('should ignore charset', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.is('application/*'))\n      })\n\n      request(app)\n      .post('/')\n      .type('application/json; charset=UTF-8')\n      .send('{}')\n      .expect(200, '\"application/json\"', done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.path.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.path', function(){\n    it('should return the parsed pathname', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.path);\n      });\n\n      request(app)\n      .get('/login?redirect=/post/1/comments')\n      .expect('/login', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.protocol.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.protocol', function(){\n    it('should return the protocol string', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.end(req.protocol);\n      });\n\n      request(app)\n      .get('/')\n      .expect('http', done);\n    })\n\n    describe('when \"trust proxy\" is enabled', function(){\n      it('should respect X-Forwarded-Proto', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.use(function(req, res){\n          res.end(req.protocol);\n        });\n\n        request(app)\n        .get('/')\n        .set('X-Forwarded-Proto', 'https')\n        .expect('https', done);\n      })\n\n      it('should default to the socket addr if X-Forwarded-Proto not present', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.use(function(req, res){\n          req.socket.encrypted = true;\n          res.end(req.protocol);\n        });\n\n        request(app)\n        .get('/')\n        .expect('https', done);\n      })\n\n      it('should ignore X-Forwarded-Proto if socket addr not trusted', function(done){\n        var app = express();\n\n        app.set('trust proxy', '10.0.0.1');\n\n        app.use(function(req, res){\n          res.end(req.protocol);\n        });\n\n        request(app)\n        .get('/')\n        .set('X-Forwarded-Proto', 'https')\n        .expect('http', done);\n      })\n\n      it('should default to http', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.use(function(req, res){\n          res.end(req.protocol);\n        });\n\n        request(app)\n        .get('/')\n        .expect('http', done);\n      })\n\n      describe('when trusting hop count', function () {\n        it('should respect X-Forwarded-Proto', function (done) {\n          var app = express();\n\n          app.set('trust proxy', 1);\n\n          app.use(function (req, res) {\n            res.end(req.protocol);\n          });\n\n          request(app)\n          .get('/')\n          .set('X-Forwarded-Proto', 'https')\n          .expect('https', done);\n        })\n      })\n    })\n\n    describe('when \"trust proxy\" is disabled', function(){\n      it('should ignore X-Forwarded-Proto', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.end(req.protocol);\n        });\n\n        request(app)\n        .get('/')\n        .set('X-Forwarded-Proto', 'https')\n        .expect('http', done);\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.query.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.query', function(){\n    it('should default to {}', function(done){\n      var app = createApp();\n\n      request(app)\n      .get('/')\n      .expect(200, '{}', done);\n    });\n\n    it('should default to parse simple keys', function (done) {\n      var app = createApp();\n\n      request(app)\n      .get('/?user[name]=tj')\n      .expect(200, '{\"user[name]\":\"tj\"}', done);\n    });\n\n    describe('when \"query parser\" is extended', function () {\n      it('should parse complex keys', function (done) {\n        var app = createApp('extended');\n\n        request(app)\n        .get('/?foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!')\n        .expect(200, '{\"foo\":[{\"bar\":\"baz\",\"fizz\":\"buzz\"},\"done!\"]}', done);\n      });\n\n      it('should parse parameters with dots', function (done) {\n        var app = createApp('extended');\n\n        request(app)\n        .get('/?user.name=tj')\n        .expect(200, '{\"user.name\":\"tj\"}', done);\n      });\n    });\n\n    describe('when \"query parser\" is simple', function () {\n      it('should not parse complex keys', function (done) {\n        var app = createApp('simple');\n\n        request(app)\n        .get('/?user%5Bname%5D=tj')\n        .expect(200, '{\"user[name]\":\"tj\"}', done);\n      });\n    });\n\n    describe('when \"query parser\" is a function', function () {\n      it('should parse using function', function (done) {\n        var app = createApp(function (str) {\n          return {'length': (str || '').length};\n        });\n\n        request(app)\n        .get('/?user%5Bname%5D=tj')\n        .expect(200, '{\"length\":17}', done);\n      });\n    });\n\n    describe('when \"query parser\" disabled', function () {\n      it('should not parse query', function (done) {\n        var app = createApp(false);\n\n        request(app)\n        .get('/?user%5Bname%5D=tj')\n        .expect(200, '{}', done);\n      });\n    });\n\n    describe('when \"query parser\" enabled', function () {\n      it('should not parse complex keys', function (done) {\n        var app = createApp(true);\n\n        request(app)\n        .get('/?user%5Bname%5D=tj')\n        .expect(200, '{\"user[name]\":\"tj\"}', done);\n      });\n    });\n\n    describe('when \"query parser\" an unknown value', function () {\n      it('should throw', function () {\n        assert.throws(createApp.bind(null, 'bogus'),\n          /unknown value.*query parser/)\n      });\n    });\n  })\n})\n\nfunction createApp(setting) {\n  var app = express();\n\n  if (setting !== undefined) {\n    app.set('query parser', setting);\n  }\n\n  app.use(function (req, res) {\n    res.send(req.query);\n  });\n\n  return app;\n}\n"
  },
  {
    "path": "test/req.range.js",
    "content": "'use strict'\n\nvar express = require('..');\nvar request = require('supertest')\n\ndescribe('req', function(){\n  describe('.range(size)', function(){\n    it('should return parsed ranges', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.range(120))\n      })\n\n      request(app)\n      .get('/')\n      .set('Range', 'bytes=0-50,51-100')\n      .expect(200, '[{\"start\":0,\"end\":50},{\"start\":51,\"end\":100}]', done)\n    })\n\n    it('should cap to the given size', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.range(75))\n      })\n\n      request(app)\n      .get('/')\n      .set('Range', 'bytes=0-100')\n      .expect(200, '[{\"start\":0,\"end\":74}]', done)\n    })\n\n    it('should cap to the given size when open-ended', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.range(75))\n      })\n\n      request(app)\n      .get('/')\n      .set('Range', 'bytes=0-')\n      .expect(200, '[{\"start\":0,\"end\":74}]', done)\n    })\n\n    it('should have a .type', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.range(120).type)\n      })\n\n      request(app)\n      .get('/')\n      .set('Range', 'bytes=0-100')\n      .expect(200, '\"bytes\"', done)\n    })\n\n    it('should accept any type', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.json(req.range(120).type)\n      })\n\n      request(app)\n      .get('/')\n      .set('Range', 'users=0-2')\n      .expect(200, '\"users\"', done)\n    })\n\n    it('should return undefined if no range', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.send(String(req.range(120)))\n      })\n\n      request(app)\n      .get('/')\n      .expect(200, 'undefined', done)\n    })\n  })\n\n  describe('.range(size, options)', function(){\n    describe('with \"combine: true\" option', function(){\n      it('should return combined ranges', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.json(req.range(120, {\n            combine: true\n          }))\n        })\n\n        request(app)\n        .get('/')\n        .set('Range', 'bytes=0-50,51-100')\n        .expect(200, '[{\"start\":0,\"end\":100}]', done)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.route.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.route', function(){\n    it('should be the executed Route', function(done){\n      var app = express();\n\n      app.get('/user/:id{/:op}', function(req, res, next){\n        res.header('path-1', req.route.path)\n        next();\n      });\n\n      app.get('/user/:id/edit', function(req, res){\n        res.header('path-2', req.route.path)\n        res.end();\n      });\n\n      request(app)\n        .get('/user/12/edit')\n        .expect('path-1', '/user/:id{/:op}')\n        .expect('path-2', '/user/:id/edit')\n        .expect(200, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.secure.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.secure', function(){\n    describe('when X-Forwarded-Proto is missing', function(){\n      it('should return false when http', function(done){\n        var app = express();\n\n        app.get('/', function(req, res){\n          res.send(req.secure ? 'yes' : 'no');\n        });\n\n        request(app)\n        .get('/')\n        .expect('no', done)\n      })\n    })\n  })\n\n  describe('.secure', function(){\n    describe('when X-Forwarded-Proto is present', function(){\n      it('should return false when http', function(done){\n        var app = express();\n\n        app.get('/', function(req, res){\n          res.send(req.secure ? 'yes' : 'no');\n        });\n\n        request(app)\n        .get('/')\n        .set('X-Forwarded-Proto', 'https')\n        .expect('no', done)\n      })\n\n      it('should return true when \"trust proxy\" is enabled', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.get('/', function(req, res){\n          res.send(req.secure ? 'yes' : 'no');\n        });\n\n        request(app)\n        .get('/')\n        .set('X-Forwarded-Proto', 'https')\n        .expect('yes', done)\n      })\n\n      it('should return false when initial proxy is http', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.get('/', function(req, res){\n          res.send(req.secure ? 'yes' : 'no');\n        });\n\n        request(app)\n        .get('/')\n        .set('X-Forwarded-Proto', 'http, https')\n        .expect('no', done)\n      })\n\n      it('should return true when initial proxy is https', function(done){\n        var app = express();\n\n        app.enable('trust proxy');\n\n        app.get('/', function(req, res){\n          res.send(req.secure ? 'yes' : 'no');\n        });\n\n        request(app)\n        .get('/')\n        .set('X-Forwarded-Proto', 'https, http')\n        .expect('yes', done)\n      })\n\n      describe('when \"trust proxy\" trusting hop count', function () {\n        it('should respect X-Forwarded-Proto', function (done) {\n          var app = express();\n\n          app.set('trust proxy', 1);\n\n          app.get('/', function (req, res) {\n            res.send(req.secure ? 'yes' : 'no');\n          });\n\n          request(app)\n          .get('/')\n          .set('X-Forwarded-Proto', 'https')\n          .expect('yes', done)\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.signedCookies.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest')\n  , cookieParser = require('cookie-parser')\n\ndescribe('req', function(){\n  describe('.signedCookies', function(){\n    it('should return a signed JSON cookie', function(done){\n      var app = express();\n\n      app.use(cookieParser('secret'));\n\n      app.use(function(req, res){\n        if (req.path === '/set') {\n          res.cookie('obj', { foo: 'bar' }, { signed: true });\n          res.end();\n        } else {\n          res.send(req.signedCookies);\n        }\n      });\n\n      request(app)\n      .get('/set')\n      .end(function(err, res){\n        if (err) return done(err);\n        var cookie = res.header['set-cookie'];\n\n        request(app)\n        .get('/')\n        .set('Cookie', cookie)\n        .expect(200, { obj: { foo: 'bar' } }, done)\n      });\n    })\n  })\n})\n\n"
  },
  {
    "path": "test/req.stale.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.stale', function(){\n    it('should return false when the resource is not modified', function(done){\n      var app = express();\n      var etag = '\"12345\"';\n\n      app.use(function(req, res){\n        res.set('ETag', etag);\n        res.send(req.stale);\n      });\n\n      request(app)\n      .get('/')\n      .set('If-None-Match', etag)\n      .expect(304, done);\n    })\n\n    it('should return true when the resource is modified', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set('ETag', '\"123\"');\n        res.send(req.stale);\n      });\n\n      request(app)\n      .get('/')\n      .set('If-None-Match', '\"12345\"')\n      .expect(200, 'true', done);\n    })\n\n    it('should return true without response headers', function(done){\n      var app = express();\n\n      app.disable('x-powered-by')\n      app.use(function(req, res){\n        res.send(req.stale);\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, 'true', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.subdomains.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.subdomains', function(){\n    describe('when present', function(){\n      it('should return an array', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.send(req.subdomains);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'tobi.ferrets.example.com')\n        .expect(200, ['ferrets', 'tobi'], done);\n      })\n\n      it('should work with IPv4 address', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.send(req.subdomains);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', '127.0.0.1')\n        .expect(200, [], done);\n      })\n\n      it('should work with IPv6 address', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.send(req.subdomains);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', '[::1]')\n        .expect(200, [], done);\n      })\n    })\n\n    describe('otherwise', function(){\n      it('should return an empty array', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.send(req.subdomains);\n        });\n\n        request(app)\n        .get('/')\n        .set('Host', 'example.com')\n        .expect(200, [], done);\n      })\n    })\n\n    describe('with no host', function(){\n      it('should return an empty array', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          req.headers.host = null;\n          res.send(req.subdomains);\n        });\n\n        request(app)\n        .get('/')\n        .expect(200, [], done);\n      })\n    })\n\n    describe('with trusted X-Forwarded-Host', function () {\n      it('should return an array', function (done) {\n        var app = express();\n\n        app.set('trust proxy', true);\n        app.use(function (req, res) {\n          res.send(req.subdomains);\n        });\n\n        request(app)\n        .get('/')\n        .set('X-Forwarded-Host', 'tobi.ferrets.example.com')\n        .expect(200, ['ferrets', 'tobi'], done);\n      })\n    })\n\n    describe('when subdomain offset is set', function(){\n      describe('when subdomain offset is zero', function(){\n        it('should return an array with the whole domain', function(done){\n          var app = express();\n          app.set('subdomain offset', 0);\n\n          app.use(function(req, res){\n            res.send(req.subdomains);\n          });\n\n          request(app)\n          .get('/')\n          .set('Host', 'tobi.ferrets.sub.example.com')\n          .expect(200, ['com', 'example', 'sub', 'ferrets', 'tobi'], done);\n        })\n\n        it('should return an array with the whole IPv4', function (done) {\n          var app = express();\n          app.set('subdomain offset', 0);\n\n          app.use(function(req, res){\n            res.send(req.subdomains);\n          });\n\n          request(app)\n          .get('/')\n          .set('Host', '127.0.0.1')\n          .expect(200, ['127.0.0.1'], done);\n        })\n\n        it('should return an array with the whole IPv6', function (done) {\n          var app = express();\n          app.set('subdomain offset', 0);\n\n          app.use(function(req, res){\n            res.send(req.subdomains);\n          });\n\n          request(app)\n          .get('/')\n          .set('Host', '[::1]')\n          .expect(200, ['[::1]'], done);\n        })\n      })\n\n      describe('when present', function(){\n        it('should return an array', function(done){\n          var app = express();\n          app.set('subdomain offset', 3);\n\n          app.use(function(req, res){\n            res.send(req.subdomains);\n          });\n\n          request(app)\n          .get('/')\n          .set('Host', 'tobi.ferrets.sub.example.com')\n          .expect(200, ['ferrets', 'tobi'], done);\n        })\n      })\n\n      describe('otherwise', function(){\n        it('should return an empty array', function(done){\n          var app = express();\n          app.set('subdomain offset', 3);\n\n          app.use(function(req, res){\n            res.send(req.subdomains);\n          });\n\n          request(app)\n          .get('/')\n          .set('Host', 'sub.example.com')\n          .expect(200, [], done);\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/req.xhr.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('req', function(){\n  describe('.xhr', function(){\n    before(function () {\n      this.app = express()\n      this.app.get('/', function (req, res) {\n        res.send(req.xhr)\n      })\n    })\n\n    it('should return true when X-Requested-With is xmlhttprequest', function(done){\n      request(this.app)\n        .get('/')\n        .set('X-Requested-With', 'xmlhttprequest')\n        .expect(200, 'true', done)\n    })\n\n    it('should case-insensitive', function(done){\n      request(this.app)\n        .get('/')\n        .set('X-Requested-With', 'XMLHttpRequest')\n        .expect(200, 'true', done)\n    })\n\n    it('should return false otherwise', function(done){\n      request(this.app)\n        .get('/')\n        .set('X-Requested-With', 'blahblah')\n        .expect(200, 'false', done)\n    })\n\n    it('should return false when not present', function(done){\n      request(this.app)\n        .get('/')\n        .expect(200, 'false', done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.append.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nvar express = require('..')\nvar request = require('supertest')\n\ndescribe('res', function () {\n  describe('.append(field, val)', function () {\n    it('should append multiple headers', function (done) {\n      var app = express()\n\n      app.use(function (req, res, next) {\n        res.append('Set-Cookie', 'foo=bar')\n        next()\n      })\n\n      app.use(function (req, res) {\n        res.append('Set-Cookie', 'fizz=buzz')\n        res.end()\n      })\n\n      request(app)\n        .get('/')\n        .expect(200)\n        .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz']))\n        .end(done)\n    })\n\n    it('should accept array of values', function (done) {\n      var app = express()\n\n      app.use(function (req, res, next) {\n        res.append('Set-Cookie', ['foo=bar', 'fizz=buzz'])\n        res.end()\n      })\n\n      request(app)\n        .get('/')\n        .expect(200)\n        .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz']))\n        .end(done)\n    })\n\n    it('should get reset by res.set(field, val)', function (done) {\n      var app = express()\n\n      app.use(function (req, res, next) {\n        res.append('Set-Cookie', 'foo=bar')\n        res.append('Set-Cookie', 'fizz=buzz')\n        next()\n      })\n\n      app.use(function (req, res) {\n        res.set('Set-Cookie', 'pet=tobi')\n        res.end()\n      });\n\n      request(app)\n        .get('/')\n        .expect(200)\n        .expect(shouldHaveHeaderValues('Set-Cookie', ['pet=tobi']))\n        .end(done)\n    })\n\n    it('should work with res.set(field, val) first', function (done) {\n      var app = express()\n\n      app.use(function (req, res, next) {\n        res.set('Set-Cookie', 'foo=bar')\n        next()\n      })\n\n      app.use(function(req, res){\n        res.append('Set-Cookie', 'fizz=buzz')\n        res.end()\n      })\n\n      request(app)\n        .get('/')\n        .expect(200)\n        .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz']))\n        .end(done)\n    })\n\n    it('should work together with res.cookie', function (done) {\n      var app = express()\n\n      app.use(function (req, res, next) {\n        res.cookie('foo', 'bar')\n        next()\n      })\n\n      app.use(function (req, res) {\n        res.append('Set-Cookie', 'fizz=buzz')\n        res.end()\n      })\n\n      request(app)\n        .get('/')\n        .expect(200)\n        .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar; Path=/', 'fizz=buzz']))\n        .end(done)\n    })\n  })\n})\n\nfunction shouldHaveHeaderValues (key, values) {\n  return function (res) {\n    var headers = res.headers[key.toLowerCase()]\n    assert.ok(headers, 'should have header \"' + key + '\"')\n    assert.strictEqual(headers.length, values.length, 'should have ' + values.length + ' occurrences of \"' + key + '\"')\n    for (var i = 0; i < values.length; i++) {\n      assert.strictEqual(headers[i], values[i])\n    }\n  }\n}\n"
  },
  {
    "path": "test/res.attachment.js",
    "content": "'use strict'\n\nconst { Buffer } = require('node:buffer');\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('res', function(){\n  describe('.attachment()', function(){\n    it('should Content-Disposition to attachment', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.attachment().send('foo');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Disposition', 'attachment', done);\n    })\n  })\n\n  describe('.attachment(filename)', function(){\n    it('should add the filename param', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.attachment('/path/to/image.png');\n        res.send('foo');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Disposition', 'attachment; filename=\"image.png\"', done);\n    })\n\n    it('should set the Content-Type', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.attachment('/path/to/image.png');\n        res.send(Buffer.alloc(4, '.'))\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'image/png', done);\n    })\n  })\n\n  describe('.attachment(utf8filename)', function(){\n    it('should add the filename and filename* params', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.attachment('/locales/日本語.txt');\n        res.send('japanese');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Disposition', 'attachment; filename=\"???.txt\"; filename*=UTF-8\\'\\'%E6%97%A5%E6%9C%AC%E8%AA%9E.txt')\n      .expect(200, done);\n    })\n\n    it('should set the Content-Type', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.attachment('/locales/日本語.txt');\n        res.send('japanese');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/plain; charset=utf-8', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.clearCookie.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('res', function(){\n  describe('.clearCookie(name)', function(){\n    it('should set a cookie passed expiry', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.clearCookie('sid').end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Set-Cookie', 'sid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT')\n      .expect(200, done)\n    })\n  })\n\n  describe('.clearCookie(name, options)', function(){\n    it('should set the given params', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.clearCookie('sid', { path: '/admin' }).end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT')\n      .expect(200, done)\n    })\n\n    it('should ignore maxAge', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.clearCookie('sid', { path: '/admin', maxAge: 1000 }).end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT')\n      .expect(200, done)\n    })\n\n    it('should ignore user supplied expires param', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.clearCookie('sid', { path: '/admin', expires: new Date() }).end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT')\n      .expect(200, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.cookie.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest')\n  , cookieParser = require('cookie-parser')\n\ndescribe('res', function(){\n  describe('.cookie(name, object)', function(){\n    it('should generate a JSON cookie', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.cookie('user', { name: 'tobi' }).end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Set-Cookie', 'user=j%3A%7B%22name%22%3A%22tobi%22%7D; Path=/')\n      .expect(200, done)\n    })\n  })\n\n  describe('.cookie(name, string)', function(){\n    it('should set a cookie', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.cookie('name', 'tobi').end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Set-Cookie', 'name=tobi; Path=/')\n      .expect(200, done)\n    })\n\n    it('should allow multiple calls', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.cookie('name', 'tobi');\n        res.cookie('age', 1);\n        res.cookie('gender', '?');\n        res.end();\n      });\n\n      request(app)\n        .get('/')\n        .expect('Set-Cookie', 'name=tobi; Path=/,age=1; Path=/,gender=%3F; Path=/')\n        .expect(200, done)\n    })\n  })\n\n  describe('.cookie(name, string, options)', function(){\n    it('should set params', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.cookie('name', 'tobi', { httpOnly: true, secure: true });\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Set-Cookie', 'name=tobi; Path=/; HttpOnly; Secure')\n      .expect(200, done)\n    })\n\n    describe('expires', function () {\n      it('should throw on invalid date', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { expires: new Date(NaN) })\n          res.end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(500, /option expires is invalid/, done)\n      })\n    })\n\n    describe('partitioned', function () {\n      it('should set partitioned', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { partitioned: true });\n          res.end();\n        });\n\n        request(app)\n          .get('/')\n          .expect('Set-Cookie', 'name=tobi; Path=/; Partitioned')\n          .expect(200, done)\n      })\n    })\n\n    describe('maxAge', function(){\n      it('should set relative expires', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.cookie('name', 'tobi', { maxAge: 1000 });\n          res.end();\n        });\n\n        request(app)\n          .get('/')\n          .expect('Set-Cookie', /name=tobi; Max-Age=1; Path=\\/; Expires=/)\n          .expect(200, done)\n      })\n\n      it('should set max-age', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.cookie('name', 'tobi', { maxAge: 1000 });\n          res.end();\n        });\n\n        request(app)\n        .get('/')\n        .expect('Set-Cookie', /Max-Age=1/, done)\n      })\n\n      it('should not mutate the options object', function(done){\n        var app = express();\n\n        var options = { maxAge: 1000 };\n        var optionsCopy = { ...options };\n\n        app.use(function(req, res){\n          res.cookie('name', 'tobi', options)\n          res.json(options)\n        });\n\n        request(app)\n        .get('/')\n        .expect(200, optionsCopy, done)\n      })\n\n      it('should not throw on null', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { maxAge: null })\n          res.end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Set-Cookie', 'name=tobi; Path=/')\n          .end(done)\n      })\n\n      it('should not throw on undefined', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { maxAge: undefined })\n          res.end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Set-Cookie', 'name=tobi; Path=/')\n          .end(done)\n      })\n\n      it('should throw an error with invalid maxAge', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { maxAge: 'foobar' })\n          res.end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(500, /option maxAge is invalid/, done)\n      })\n    })\n\n    describe('priority', function () {\n      it('should set low priority', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { priority: 'low' })\n          res.end()\n        })\n\n        request(app)\n          .get('/')\n          .expect('Set-Cookie', /Priority=Low/)\n          .expect(200, done)\n      })\n\n      it('should set medium priority', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { priority: 'medium' })\n          res.end()\n        })\n\n        request(app)\n          .get('/')\n          .expect('Set-Cookie', /Priority=Medium/)\n          .expect(200, done)\n      })\n\n      it('should set high priority', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { priority: 'high' })\n          res.end()\n        })\n\n        request(app)\n          .get('/')\n          .expect('Set-Cookie', /Priority=High/)\n          .expect(200, done)\n      })\n\n      it('should throw with invalid priority', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.cookie('name', 'tobi', { priority: 'foobar' })\n          res.end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(500, /option priority is invalid/, done)\n      })\n    })\n\n    describe('signed', function(){\n      it('should generate a signed JSON cookie', function(done){\n        var app = express();\n\n        app.use(cookieParser('foo bar baz'));\n\n        app.use(function(req, res){\n          res.cookie('user', { name: 'tobi' }, { signed: true }).end();\n        });\n\n        request(app)\n          .get('/')\n          .expect('Set-Cookie', 'user=s%3Aj%3A%7B%22name%22%3A%22tobi%22%7D.K20xcwmDS%2BPb1rsD95o5Jm5SqWs1KteqdnynnB7jkTE; Path=/')\n          .expect(200, done)\n      })\n    })\n\n    describe('signed without secret', function(){\n      it('should throw an error', function(done){\n        var app = express();\n\n        app.use(cookieParser());\n\n        app.use(function(req, res){\n          res.cookie('name', 'tobi', { signed: true }).end();\n        });\n\n        request(app)\n        .get('/')\n        .expect(500, /secret\\S+ required for signed cookies/, done);\n      })\n    })\n\n    describe('.signedCookie(name, string)', function(){\n      it('should set a signed cookie', function(done){\n        var app = express();\n\n        app.use(cookieParser('foo bar baz'));\n\n        app.use(function(req, res){\n          res.cookie('name', 'tobi', { signed: true }).end();\n        });\n\n        request(app)\n        .get('/')\n        .expect('Set-Cookie', 'name=s%3Atobi.xJjV2iZ6EI7C8E5kzwbfA9PVLl1ZR07UTnuTgQQ4EnQ; Path=/')\n        .expect(200, done)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.download.js",
    "content": "'use strict'\n\nvar after = require('after');\nvar assert = require('node:assert')\nvar AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage\nconst { Buffer } = require('node:buffer');\n\nvar express = require('..');\nvar path = require('node:path')\nvar request = require('supertest');\nvar utils = require('./support/utils')\n\nvar FIXTURES_PATH = path.join(__dirname, 'fixtures')\n\ndescribe('res', function(){\n  describe('.download(path)', function(){\n    it('should transfer as an attachment', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.download('test/fixtures/user.html');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/html; charset=utf-8')\n      .expect('Content-Disposition', 'attachment; filename=\"user.html\"')\n      .expect(200, '<p>{{user.name}}</p>', done)\n    })\n\n    it('should accept range requests', function (done) {\n      var app = express()\n\n      app.get('/', function (req, res) {\n        res.download('test/fixtures/user.html')\n      })\n\n      request(app)\n        .get('/')\n        .expect('Accept-Ranges', 'bytes')\n        .expect(200, '<p>{{user.name}}</p>', done)\n    })\n\n    it('should respond with requested byte range', function (done) {\n      var app = express()\n\n      app.get('/', function (req, res) {\n        res.download('test/fixtures/user.html')\n      })\n\n      request(app)\n        .get('/')\n        .set('Range', 'bytes=0-2')\n        .expect('Content-Range', 'bytes 0-2/20')\n        .expect(206, '<p>', done)\n    })\n  })\n\n  describe('.download(path, filename)', function(){\n    it('should provide an alternate filename', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.download('test/fixtures/user.html', 'document');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/html; charset=utf-8')\n      .expect('Content-Disposition', 'attachment; filename=\"document\"')\n      .expect(200, done)\n    })\n  })\n\n  describe('.download(path, fn)', function(){\n    it('should invoke the callback', function(done){\n      var app = express();\n      var cb = after(2, done);\n\n      app.use(function(req, res){\n        res.download('test/fixtures/user.html', cb);\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/html; charset=utf-8')\n      .expect('Content-Disposition', 'attachment; filename=\"user.html\"')\n      .expect(200, cb);\n    })\n\n    describe('async local storage', function () {\n      it('should persist store', function (done) {\n        var app = express()\n        var cb = after(2, done)\n        var store = { foo: 'bar' }\n\n        app.use(function (req, res, next) {\n          req.asyncLocalStorage = new AsyncLocalStorage()\n          req.asyncLocalStorage.run(store, next)\n        })\n\n        app.use(function (req, res) {\n          res.download('test/fixtures/name.txt', function (err) {\n            if (err) return cb(err)\n\n            var local = req.asyncLocalStorage.getStore()\n\n            assert.strictEqual(local.foo, 'bar')\n            cb()\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect('Content-Type', 'text/plain; charset=utf-8')\n          .expect('Content-Disposition', 'attachment; filename=\"name.txt\"')\n          .expect(200, 'tobi', cb)\n      })\n\n      it('should persist store on error', function (done) {\n        var app = express()\n        var store = { foo: 'bar' }\n\n        app.use(function (req, res, next) {\n          req.asyncLocalStorage = new AsyncLocalStorage()\n          req.asyncLocalStorage.run(store, next)\n        })\n\n        app.use(function (req, res) {\n          res.download('test/fixtures/does-not-exist', function (err) {\n            var local = req.asyncLocalStorage.getStore()\n\n            if (local) {\n              res.setHeader('x-store-foo', String(local.foo))\n            }\n\n            res.send(err ? 'got ' + err.status + ' error' : 'no error')\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('x-store-foo', 'bar')\n          .expect('got 404 error')\n          .end(done)\n      })\n    })\n  })\n\n  describe('.download(path, options)', function () {\n    it('should allow options to res.sendFile()', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.download('test/fixtures/.name', {\n          dotfiles: 'allow',\n          maxAge: '4h'\n        })\n      })\n\n      request(app)\n        .get('/')\n        .expect(200)\n        .expect('Content-Disposition', 'attachment; filename=\".name\"')\n        .expect('Cache-Control', 'public, max-age=14400')\n        .expect(utils.shouldHaveBody(Buffer.from('tobi')))\n        .end(done)\n    })\n\n    describe('with \"headers\" option', function () {\n      it('should set headers on response', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('test/fixtures/user.html', {\n            headers: {\n              'X-Foo': 'Bar',\n              'X-Bar': 'Foo'\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('X-Foo', 'Bar')\n          .expect('X-Bar', 'Foo')\n          .end(done)\n      })\n\n      it('should use last header when duplicated', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('test/fixtures/user.html', {\n            headers: {\n              'X-Foo': 'Bar',\n              'x-foo': 'bar'\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('X-Foo', 'bar')\n          .end(done)\n      })\n\n      it('should override Content-Type', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('test/fixtures/user.html', {\n            headers: {\n              'Content-Type': 'text/x-custom'\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Content-Type', 'text/x-custom')\n          .end(done)\n      })\n\n      it('should not set headers on 404', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('test/fixtures/does-not-exist', {\n            headers: {\n              'X-Foo': 'Bar'\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(404)\n          .expect(utils.shouldNotHaveHeader('X-Foo'))\n          .end(done)\n      })\n\n      describe('when headers contains Content-Disposition', function () {\n        it('should be ignored', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.download('test/fixtures/user.html', {\n              headers: {\n                'Content-Disposition': 'inline'\n              }\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Content-Disposition', 'attachment; filename=\"user.html\"')\n            .end(done)\n        })\n\n        it('should be ignored case-insensitively', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.download('test/fixtures/user.html', {\n              headers: {\n                'content-disposition': 'inline'\n              }\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Content-Disposition', 'attachment; filename=\"user.html\"')\n            .end(done)\n        })\n      })\n    })\n\n    describe('with \"root\" option', function () {\n      it('should allow relative path', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('name.txt', {\n            root: FIXTURES_PATH\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Content-Disposition', 'attachment; filename=\"name.txt\"')\n          .expect(utils.shouldHaveBody(Buffer.from('tobi')))\n          .end(done)\n      })\n\n      it('should allow up within root', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('fake/../name.txt', {\n            root: FIXTURES_PATH\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Content-Disposition', 'attachment; filename=\"name.txt\"')\n          .expect(utils.shouldHaveBody(Buffer.from('tobi')))\n          .end(done)\n      })\n\n      it('should reject up outside root', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          var p = '..' + path.sep +\n            path.relative(path.dirname(FIXTURES_PATH), path.join(FIXTURES_PATH, 'name.txt'))\n\n          res.download(p, {\n            root: FIXTURES_PATH\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(403)\n          .expect(utils.shouldNotHaveHeader('Content-Disposition'))\n          .end(done)\n      })\n\n      it('should reject reading outside root', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('../name.txt', {\n            root: FIXTURES_PATH\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(403)\n          .expect(utils.shouldNotHaveHeader('Content-Disposition'))\n          .end(done)\n      })\n    })\n  })\n\n  describe('.download(path, filename, fn)', function(){\n    it('should invoke the callback', function(done){\n      var app = express();\n      var cb = after(2, done);\n\n      app.use(function(req, res){\n        res.download('test/fixtures/user.html', 'document', cb)\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/html; charset=utf-8')\n      .expect('Content-Disposition', 'attachment; filename=\"document\"')\n      .expect(200, cb);\n    })\n  })\n\n  describe('.download(path, filename, options, fn)', function () {\n    it('should invoke the callback', function (done) {\n      var app = express()\n      var cb = after(2, done)\n      var options = {}\n\n      app.use(function (req, res) {\n        res.download('test/fixtures/user.html', 'document', options, cb)\n      })\n\n      request(app)\n      .get('/')\n      .expect(200)\n      .expect('Content-Type', 'text/html; charset=utf-8')\n      .expect('Content-Disposition', 'attachment; filename=\"document\"')\n      .end(cb)\n    })\n\n    it('should allow options to res.sendFile()', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.download('test/fixtures/.name', 'document', {\n          dotfiles: 'allow',\n          maxAge: '4h'\n        })\n      })\n\n      request(app)\n        .get('/')\n        .expect(200)\n        .expect('Content-Disposition', 'attachment; filename=\"document\"')\n        .expect('Cache-Control', 'public, max-age=14400')\n        .expect(utils.shouldHaveBody(Buffer.from('tobi')))\n        .end(done)\n    })\n\n    describe('when options.headers contains Content-Disposition', function () {\n      it('should be ignored', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('test/fixtures/user.html', 'document', {\n            headers: {\n              'Content-Type': 'text/x-custom',\n              'Content-Disposition': 'inline'\n            }\n          })\n        })\n\n        request(app)\n        .get('/')\n        .expect(200)\n        .expect('Content-Type', 'text/x-custom')\n        .expect('Content-Disposition', 'attachment; filename=\"document\"')\n        .end(done)\n      })\n\n      it('should be ignored case-insensitively', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.download('test/fixtures/user.html', 'document', {\n            headers: {\n              'content-type': 'text/x-custom',\n              'content-disposition': 'inline'\n            }\n          })\n        })\n\n        request(app)\n        .get('/')\n        .expect(200)\n        .expect('Content-Type', 'text/x-custom')\n        .expect('Content-Disposition', 'attachment; filename=\"document\"')\n        .end(done)\n      })\n    })\n  })\n\n  describe('on failure', function(){\n    it('should invoke the callback', function(done){\n      var app = express();\n\n      app.use(function (req, res, next) {\n        res.download('test/fixtures/foobar.html', function(err){\n          if (!err) return next(new Error('expected error'));\n          res.send('got ' + err.status + ' ' + err.code);\n        });\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, 'got 404 ENOENT', done);\n    })\n\n    it('should remove Content-Disposition', function(done){\n      var app = express()\n\n      app.use(function (req, res, next) {\n        res.download('test/fixtures/foobar.html', function(err){\n          if (!err) return next(new Error('expected error'));\n          res.end('failed');\n        });\n      });\n\n      request(app)\n        .get('/')\n        .expect(utils.shouldNotHaveHeader('Content-Disposition'))\n        .expect(200, 'failed', done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.format.js",
    "content": "'use strict'\n\nvar after = require('after')\nvar express = require('../')\n  , request = require('supertest')\n  , assert = require('node:assert');\n\nvar app1 = express();\n\napp1.use(function(req, res, next){\n  res.format({\n    'text/plain': function(){\n      res.send('hey');\n    },\n\n    'text/html': function(){\n      res.send('<p>hey</p>');\n    },\n\n    'application/json': function(a, b, c){\n      assert(req === a)\n      assert(res === b)\n      assert(next === c)\n      res.send({ message: 'hey' });\n    }\n  });\n});\n\napp1.use(function(err, req, res, next){\n  if (!err.types) throw err;\n  res.status(err.status)\n  res.send('Supports: ' + err.types.join(', '))\n})\n\nvar app2 = express();\n\napp2.use(function(req, res, next){\n  res.format({\n    text: function(){ res.send('hey') },\n    html: function(){ res.send('<p>hey</p>') },\n    json: function(){ res.send({ message: 'hey' }) }\n  });\n});\n\napp2.use(function(err, req, res, next){\n  res.status(err.status)\n  res.send('Supports: ' + err.types.join(', '))\n})\n\nvar app3 = express();\n\napp3.use(function(req, res, next){\n  res.format({\n    text: function(){ res.send('hey') },\n    default: function (a, b, c) {\n      assert(req === a)\n      assert(res === b)\n      assert(next === c)\n      res.send('default')\n    }\n  })\n});\n\nvar app4 = express();\n\napp4.get('/', function (req, res) {\n  res.format({\n    text: function(){ res.send('hey') },\n    html: function(){ res.send('<p>hey</p>') },\n    json: function(){ res.send({ message: 'hey' }) }\n  });\n});\n\napp4.use(function(err, req, res, next){\n  res.status(err.status)\n  res.send('Supports: ' + err.types.join(', '))\n})\n\nvar app5 = express();\n\napp5.use(function (req, res, next) {\n  res.format({\n    default: function () { res.send('hey') }\n  });\n});\n\ndescribe('res', function(){\n  describe('.format(obj)', function(){\n    describe('with canonicalized mime types', function(){\n      test(app1);\n    })\n\n    describe('with extnames', function(){\n      test(app2);\n    })\n\n    describe('with parameters', function(){\n      var app = express();\n\n      app.use(function(req, res, next){\n        res.format({\n          'text/plain; charset=utf-8': function(){ res.send('hey') },\n          'text/html; foo=bar; bar=baz': function(){ res.send('<p>hey</p>') },\n          'application/json; q=0.5': function(){ res.send({ message: 'hey' }) }\n        });\n      });\n\n      app.use(function(err, req, res, next){\n        res.status(err.status)\n        res.send('Supports: ' + err.types.join(', '))\n      });\n\n      test(app);\n    })\n\n    describe('given .default', function(){\n      it('should be invoked instead of auto-responding', function(done){\n        request(app3)\n        .get('/')\n        .set('Accept', 'text/html')\n        .expect('default', done);\n      })\n\n      it('should work when only .default is provided', function (done) {\n        request(app5)\n        .get('/')\n        .set('Accept', '*/*')\n        .expect('hey', done);\n      })\n\n      it('should be able to invoke other formatter', function (done) {\n        var app = express()\n\n        app.use(function (req, res, next) {\n          res.format({\n            json: function () { res.send('json') },\n            default: function () {\n              res.header('x-default', '1')\n              this.json()\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .set('Accept', 'text/plain')\n          .expect(200)\n          .expect('x-default', '1')\n          .expect('json')\n          .end(done)\n      })\n    })\n\n    describe('in router', function(){\n      test(app4);\n    })\n\n    describe('in router', function(){\n      var app = express();\n      var router = express.Router();\n\n      router.get('/', function (req, res) {\n        res.format({\n          text: function(){ res.send('hey') },\n          html: function(){ res.send('<p>hey</p>') },\n          json: function(){ res.send({ message: 'hey' }) }\n        });\n      });\n\n      router.use(function(err, req, res, next){\n        res.status(err.status)\n        res.send('Supports: ' + err.types.join(', '))\n      })\n\n      app.use(router)\n\n      test(app)\n    })\n  })\n})\n\nfunction test(app) {\n  it('should utilize qvalues in negotiation', function(done){\n    request(app)\n    .get('/')\n    .set('Accept', 'text/html; q=.5, application/json, */*; q=.1')\n    .expect({\"message\":\"hey\"}, done);\n  })\n\n  it('should allow wildcard type/subtypes', function(done){\n    request(app)\n    .get('/')\n    .set('Accept', 'text/html; q=.5, application/*, */*; q=.1')\n    .expect({\"message\":\"hey\"}, done);\n  })\n\n  it('should default the Content-Type', function(done){\n    request(app)\n    .get('/')\n    .set('Accept', 'text/html; q=.5, text/plain')\n    .expect('Content-Type', 'text/plain; charset=utf-8')\n    .expect('hey', done);\n  })\n\n  it('should set the correct charset for the Content-Type', function (done) {\n    var cb = after(3, done)\n\n    request(app)\n    .get('/')\n    .set('Accept', 'text/html')\n    .expect('Content-Type', 'text/html; charset=utf-8', cb)\n\n    request(app)\n    .get('/')\n    .set('Accept', 'text/plain')\n    .expect('Content-Type', 'text/plain; charset=utf-8', cb)\n\n    request(app)\n    .get('/')\n    .set('Accept', 'application/json')\n    .expect('Content-Type', 'application/json; charset=utf-8', cb)\n  })\n\n  it('should Vary: Accept', function(done){\n    request(app)\n    .get('/')\n    .set('Accept', 'text/html; q=.5, text/plain')\n    .expect('Vary', 'Accept', done);\n  })\n\n  describe('when Accept is not present', function(){\n    it('should invoke the first callback', function(done){\n      request(app)\n      .get('/')\n      .expect('hey', done);\n    })\n  })\n\n  describe('when no match is made', function(){\n    it('should respond with 406 not acceptable', function(done){\n      request(app)\n      .get('/')\n      .set('Accept', 'foo/bar')\n      .expect('Supports: text/plain, text/html, application/json')\n      .expect(406, done)\n    })\n  })\n}\n"
  },
  {
    "path": "test/res.get.js",
    "content": "'use strict'\n\nvar express = require('..');\nvar request = require('supertest');\n\ndescribe('res', function(){\n  describe('.get(field)', function(){\n    it('should get the response header field', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.setHeader('Content-Type', 'text/x-foo');\n        res.send(res.get('Content-Type'));\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, 'text/x-foo', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.json.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest')\n  , assert = require('node:assert');\n\ndescribe('res', function(){\n  describe('.json(object)', function(){\n    it('should not support jsonp callbacks', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.json({ foo: 'bar' });\n      });\n\n      request(app)\n      .get('/?callback=foo')\n      .expect('{\"foo\":\"bar\"}', done);\n    })\n\n    it('should not override previous Content-Types', function(done){\n      var app = express();\n\n      app.get('/', function(req, res){\n        res.type('application/vnd.example+json');\n        res.json({ hello: 'world' });\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'application/vnd.example+json; charset=utf-8')\n      .expect(200, '{\"hello\":\"world\"}', done);\n    })\n\n    describe('when given primitives', function(){\n      it('should respond with json for null', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.json(null);\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, 'null', done)\n      })\n\n      it('should respond with json for Number', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.json(300);\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '300', done)\n      })\n\n      it('should respond with json for String', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.json('str');\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '\"str\"', done)\n      })\n    })\n\n    describe('when given an array', function(){\n      it('should respond with json', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.json(['foo', 'bar', 'baz']);\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '[\"foo\",\"bar\",\"baz\"]', done)\n      })\n    })\n\n    describe('when given an object', function(){\n      it('should respond with json', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.json({ name: 'tobi' });\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '{\"name\":\"tobi\"}', done)\n      })\n    })\n\n    describe('\"json escape\" setting', function () {\n      it('should be undefined by default', function () {\n        var app = express()\n        assert.strictEqual(app.get('json escape'), undefined)\n      })\n\n      it('should unicode escape HTML-sniffing characters', function (done) {\n        var app = express()\n\n        app.enable('json escape')\n\n        app.use(function (req, res) {\n          res.json({ '&': '<script>' })\n        })\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '{\"\\\\u0026\":\"\\\\u003cscript\\\\u003e\"}', done)\n      })\n\n      it('should not break undefined escape', function (done) {\n        var app = express()\n\n        app.enable('json escape')\n\n        app.use(function (req, res) {\n          res.json(undefined)\n        })\n\n        request(app)\n          .get('/')\n          .expect('Content-Type', 'application/json; charset=utf-8')\n          .expect(200, '', done)\n      })\n    })\n\n    describe('\"json replacer\" setting', function(){\n      it('should be passed to JSON.stringify()', function(done){\n        var app = express();\n\n        app.set('json replacer', function(key, val){\n          return key[0] === '_'\n            ? undefined\n            : val;\n        });\n\n        app.use(function(req, res){\n          res.json({ name: 'tobi', _id: 12345 });\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '{\"name\":\"tobi\"}', done)\n      })\n    })\n\n    describe('\"json spaces\" setting', function(){\n      it('should be undefined by default', function(){\n        var app = express();\n        assert(undefined === app.get('json spaces'));\n      })\n\n      it('should be passed to JSON.stringify()', function(done){\n        var app = express();\n\n        app.set('json spaces', 2);\n\n        app.use(function(req, res){\n          res.json({ name: 'tobi', age: 2 });\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '{\\n  \"name\": \"tobi\",\\n  \"age\": 2\\n}', done)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.jsonp.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest')\n  , assert = require('node:assert');\nvar utils = require('./support/utils');\n\ndescribe('res', function(){\n  describe('.jsonp(object)', function(){\n    it('should respond with jsonp', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.jsonp({ count: 1 });\n      });\n\n      request(app)\n      .get('/?callback=something')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .expect(200, /something\\(\\{\"count\":1\\}\\);/, done);\n    })\n\n    it('should use first callback parameter with jsonp', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.jsonp({ count: 1 });\n      });\n\n      request(app)\n      .get('/?callback=something&callback=somethingelse')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .expect(200, /something\\(\\{\"count\":1\\}\\);/, done);\n    })\n\n    it('should ignore object callback parameter with jsonp', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.jsonp({ count: 1 });\n      });\n\n      request(app)\n      .get('/?callback[a]=something')\n      .expect('Content-Type', 'application/json; charset=utf-8')\n      .expect(200, '{\"count\":1}', done)\n    })\n\n    it('should allow renaming callback', function(done){\n      var app = express();\n\n      app.set('jsonp callback name', 'clb');\n\n      app.use(function(req, res){\n        res.jsonp({ count: 1 });\n      });\n\n      request(app)\n      .get('/?clb=something')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .expect(200, /something\\(\\{\"count\":1\\}\\);/, done);\n    })\n\n    it('should allow []', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.jsonp({ count: 1 });\n      });\n\n      request(app)\n      .get('/?callback=callbacks[123]')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .expect(200, /callbacks\\[123\\]\\(\\{\"count\":1\\}\\);/, done);\n    })\n\n    it('should disallow arbitrary js', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.jsonp({});\n      });\n\n      request(app)\n      .get('/?callback=foo;bar()')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .expect(200, /foobar\\(\\{\\}\\);/, done);\n    })\n\n    it('should escape utf whitespace', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.jsonp({ str: '\\u2028 \\u2029 woot' });\n      });\n\n      request(app)\n      .get('/?callback=foo')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .expect(200, /foo\\(\\{\"str\":\"\\\\u2028 \\\\u2029 woot\"\\}\\);/, done);\n    });\n\n    it('should not escape utf whitespace for json fallback', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.jsonp({ str: '\\u2028 \\u2029 woot' });\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'application/json; charset=utf-8')\n      .expect(200, '{\"str\":\"\\u2028 \\u2029 woot\"}', done);\n    });\n\n    it('should include security header and prologue', function (done) {\n      var app = express();\n\n      app.use(function(req, res){\n        res.jsonp({ count: 1 });\n      });\n\n      request(app)\n      .get('/?callback=something')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .expect('X-Content-Type-Options', 'nosniff')\n      .expect(200, /^\\/\\*\\*\\//, done);\n    })\n\n    it('should not override previous Content-Types with no callback', function(done){\n      var app = express();\n\n      app.get('/', function(req, res){\n        res.type('application/vnd.example+json');\n        res.jsonp({ hello: 'world' });\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'application/vnd.example+json; charset=utf-8')\n      .expect(utils.shouldNotHaveHeader('X-Content-Type-Options'))\n      .expect(200, '{\"hello\":\"world\"}', done);\n    })\n\n    it('should override previous Content-Types with callback', function(done){\n      var app = express();\n\n      app.get('/', function(req, res){\n        res.type('application/vnd.example+json');\n        res.jsonp({ hello: 'world' });\n      });\n\n      request(app)\n      .get('/?callback=cb')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .expect('X-Content-Type-Options', 'nosniff')\n      .expect(200, /cb\\(\\{\"hello\":\"world\"\\}\\);$/, done);\n    })\n\n    describe('when given undefined', function () {\n      it('should invoke callback with no arguments', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.jsonp(undefined)\n        })\n\n        request(app)\n          .get('/?callback=cb')\n          .expect('Content-Type', 'text/javascript; charset=utf-8')\n          .expect(200, /cb\\(\\)/, done)\n      })\n    })\n\n    describe('when given null', function () {\n      it('should invoke callback with null', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.jsonp(null)\n        })\n\n        request(app)\n          .get('/?callback=cb')\n          .expect('Content-Type', 'text/javascript; charset=utf-8')\n          .expect(200, /cb\\(null\\)/, done)\n      })\n    })\n\n    describe('when given a string', function () {\n      it('should invoke callback with a string', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.jsonp('tobi')\n        })\n\n        request(app)\n          .get('/?callback=cb')\n          .expect('Content-Type', 'text/javascript; charset=utf-8')\n          .expect(200, /cb\\(\"tobi\"\\)/, done)\n      })\n    })\n\n    describe('when given a number', function () {\n      it('should invoke callback with a number', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.jsonp(42)\n        })\n\n        request(app)\n          .get('/?callback=cb')\n          .expect('Content-Type', 'text/javascript; charset=utf-8')\n          .expect(200, /cb\\(42\\)/, done)\n      })\n    })\n\n    describe('when given an array', function () {\n      it('should invoke callback with an array', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.jsonp(['foo', 'bar', 'baz'])\n        })\n\n        request(app)\n          .get('/?callback=cb')\n          .expect('Content-Type', 'text/javascript; charset=utf-8')\n          .expect(200, /cb\\(\\[\"foo\",\"bar\",\"baz\"\\]\\)/, done)\n      })\n    })\n\n    describe('when given an object', function () {\n      it('should invoke callback with an object', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.jsonp({ name: 'tobi' })\n        })\n\n        request(app)\n          .get('/?callback=cb')\n          .expect('Content-Type', 'text/javascript; charset=utf-8')\n          .expect(200, /cb\\(\\{\"name\":\"tobi\"\\}\\)/, done)\n      })\n    })\n\n    describe('\"json escape\" setting', function () {\n      it('should be undefined by default', function () {\n        var app = express()\n        assert.strictEqual(app.get('json escape'), undefined)\n      })\n\n      it('should unicode escape HTML-sniffing characters', function (done) {\n        var app = express()\n\n        app.enable('json escape')\n\n        app.use(function (req, res) {\n          res.jsonp({ '&': '\\u2028<script>\\u2029' })\n        })\n\n        request(app)\n        .get('/?callback=foo')\n        .expect('Content-Type', 'text/javascript; charset=utf-8')\n        .expect(200, /foo\\({\"\\\\u0026\":\"\\\\u2028\\\\u003cscript\\\\u003e\\\\u2029\"}\\)/, done)\n      })\n\n      it('should not break undefined escape', function (done) {\n        var app = express()\n\n        app.enable('json escape')\n\n        app.use(function (req, res) {\n          res.jsonp(undefined)\n        })\n\n        request(app)\n          .get('/?callback=cb')\n          .expect('Content-Type', 'text/javascript; charset=utf-8')\n          .expect(200, /cb\\(\\)/, done)\n      })\n    })\n\n    describe('\"json replacer\" setting', function(){\n      it('should be passed to JSON.stringify()', function(done){\n        var app = express();\n\n        app.set('json replacer', function(key, val){\n          return key[0] === '_'\n            ? undefined\n            : val;\n        });\n\n        app.use(function(req, res){\n          res.jsonp({ name: 'tobi', _id: 12345 });\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '{\"name\":\"tobi\"}', done)\n      })\n    })\n\n    describe('\"json spaces\" setting', function(){\n      it('should be undefined by default', function(){\n        var app = express();\n        assert(undefined === app.get('json spaces'));\n      })\n\n      it('should be passed to JSON.stringify()', function(done){\n        var app = express();\n\n        app.set('json spaces', 2);\n\n        app.use(function(req, res){\n          res.jsonp({ name: 'tobi', age: 2 });\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .expect(200, '{\\n  \"name\": \"tobi\",\\n  \"age\": 2\\n}', done)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.links.js",
    "content": "'use strict'\n\nvar express = require('..');\nvar request = require('supertest');\n\ndescribe('res', function(){\n  describe('.links(obj)', function(){\n    it('should set Link header field', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.links({\n          next: 'http://api.example.com/users?page=2',\n          last: 'http://api.example.com/users?page=5'\n        });\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Link', '<http://api.example.com/users?page=2>; rel=\"next\", <http://api.example.com/users?page=5>; rel=\"last\"')\n      .expect(200, done);\n    })\n\n    it('should set Link header field for multiple calls', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.links({\n          next: 'http://api.example.com/users?page=2',\n          last: 'http://api.example.com/users?page=5'\n        });\n\n        res.links({\n          prev: 'http://api.example.com/users?page=1'\n        });\n\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Link', '<http://api.example.com/users?page=2>; rel=\"next\", <http://api.example.com/users?page=5>; rel=\"last\", <http://api.example.com/users?page=1>; rel=\"prev\"')\n      .expect(200, done);\n    })\n\n    it('should set multiple links for single rel', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.links({\n          next: 'http://api.example.com/users?page=2',\n          last: ['http://api.example.com/users?page=5', 'http://api.example.com/users?page=1']\n        });\n\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Link', '<http://api.example.com/users?page=2>; rel=\"next\", <http://api.example.com/users?page=5>; rel=\"last\", <http://api.example.com/users?page=1>; rel=\"last\"')\n      .expect(200, done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.locals.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('res', function(){\n  describe('.locals', function(){\n    it('should be empty by default', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.json(res.locals)\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, {}, done)\n    })\n  })\n\n  it('should work when mounted', function(done){\n    var app = express();\n    var blog = express();\n\n    app.use(blog);\n\n    blog.use(function(req, res, next){\n      res.locals.foo = 'bar';\n      next();\n    });\n\n    app.use(function(req, res){\n      res.json(res.locals)\n    });\n\n    request(app)\n    .get('/')\n    .expect(200, { foo: 'bar' }, done)\n  })\n})\n"
  },
  {
    "path": "test/res.location.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest')\n  , assert = require('node:assert')\n  , url = require('node:url');\n\ndescribe('res', function(){\n  describe('.location(url)', function(){\n    it('should set the header', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.location('http://google.com/').end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Location', 'http://google.com/')\n      .expect(200, done)\n    })\n\n    it('should preserve trailing slashes when not present', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.location('http://google.com').end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Location', 'http://google.com')\n      .expect(200, done)\n    })\n\n    it('should encode \"url\"', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.location('https://google.com?q=\\u2603 §10').end()\n      })\n\n      request(app)\n      .get('/')\n      .expect('Location', 'https://google.com?q=%E2%98%83%20%C2%A710')\n      .expect(200, done)\n    })\n\n    it('should encode data uri', function (done) {\n      var app = express()\n      app.use(function (req, res) {\n        res.location('data:text/javascript,export default () => { }').end();\n      });\n\n      request(app)\n        .get('/')\n        .expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')\n        .expect(200, done)\n    })\n\n    it('should consistently handle non-string input: boolean', function (done) {\n      var app = express()\n      app.use(function (req, res) {\n        res.location(true).end();\n      });\n\n      request(app)\n        .get('/')\n        .expect('Location', 'true')\n        .expect(200, done)\n    });\n\n    it('should consistently handle non-string inputs: object', function (done) {\n      var app = express()\n      app.use(function (req, res) {\n        res.location({}).end();\n      });\n\n      request(app)\n        .get('/')\n        .expect('Location', '[object%20Object]')\n        .expect(200, done)\n    });\n\n    it('should consistently handle non-string inputs: array', function (done) {\n      var app = express()\n      app.use(function (req, res) {\n        res.location([]).end();\n      });\n\n      request(app)\n        .get('/')\n        .expect('Location', '')\n        .expect(200, done)\n    });\n\n    it('should consistently handle empty string input', function (done) {\n      var app = express()\n      app.use(function (req, res) {\n        res.location('').end();\n      });\n\n      request(app)\n        .get('/')\n        .expect('Location', '')\n        .expect(200, done)\n    });\n\n\n    if (typeof URL !== 'undefined') {\n      it('should accept an instance of URL', function (done) {\n        var app = express();\n\n        app.use(function(req, res){\n          res.location(new URL('http://google.com/')).end();\n        });\n\n        request(app)\n          .get('/')\n          .expect('Location', 'http://google.com/')\n          .expect(200, done);\n      });\n    }\n  })\n\n  describe('location header encoding', function() {\n    function createRedirectServerForDomain (domain) {\n      var app = express();\n      app.use(function (req, res) {\n        var host = url.parse(req.query.q, false, true).host;\n        // This is here to show a basic check one might do which\n        // would pass but then the location header would still be bad\n        if (host !== domain) {\n          res.status(400).end('Bad host: ' + host + ' !== ' + domain);\n        }\n        res.location(req.query.q).end();\n      });\n      return app;\n    }\n\n    function testRequestedRedirect (app, inputUrl, expected, expectedHost, done) {\n      return request(app)\n        // Encode uri because old supertest does not and is required\n        // to test older node versions. New supertest doesn't re-encode\n        // so this works in both.\n        .get('/?q=' + encodeURIComponent(inputUrl))\n        .expect('') // No body.\n        .expect(200)\n        .expect('Location', expected)\n        .end(function (err, res) {\n          if (err) {\n            console.log('headers:', res.headers)\n            console.error('error', res.error, err);\n            return done(err, res);\n          }\n\n          // Parse the hosts from the input URL and the Location header\n          var inputHost = url.parse(inputUrl, false, true).host;\n          var locationHost = url.parse(res.headers['location'], false, true).host;\n\n          assert.strictEqual(locationHost, expectedHost);\n\n          // Assert that the hosts are the same\n          if (inputHost !== locationHost) {\n            return done(new Error('Hosts do not match: ' + inputHost + \" !== \" +  locationHost));\n          }\n\n          return done(null, res);\n        });\n    }\n\n    it('should not touch already-encoded sequences in \"url\"', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        'https://google.com?q=%A710',\n        'https://google.com?q=%A710',\n        'google.com',\n        done\n      );\n    });\n\n    it('should consistently handle relative urls', function (done) {\n      var app = createRedirectServerForDomain(null);\n      testRequestedRedirect(\n        app,\n        '/foo/bar',\n        '/foo/bar',\n        null,\n        done\n      );\n    });\n\n    it('should not encode urls in such a way that they can bypass redirect allow lists', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        'http://google.com\\\\@apple.com',\n        'http://google.com\\\\@apple.com',\n        'google.com',\n        done\n      );\n    });\n\n    it('should not be case sensitive', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        'HTTP://google.com\\\\@apple.com',\n        'HTTP://google.com\\\\@apple.com',\n        'google.com',\n        done\n      );\n    });\n\n    it('should work with https', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        'https://google.com\\\\@apple.com',\n        'https://google.com\\\\@apple.com',\n        'google.com',\n        done\n      );\n    });\n\n    it('should correctly encode schemaless paths', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        '//google.com\\\\@apple.com/',\n        '//google.com\\\\@apple.com/',\n        'google.com',\n        done\n      );\n    });\n\n    it('should keep backslashes in the path', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        'https://google.com/foo\\\\bar\\\\baz',\n        'https://google.com/foo\\\\bar\\\\baz',\n        'google.com',\n        done\n      );\n    });\n\n    it('should escape header splitting for old node versions', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        'http://google.com\\\\@apple.com/%0d%0afoo:%20bar',\n        'http://google.com\\\\@apple.com/%0d%0afoo:%20bar',\n        'google.com',\n        done\n      );\n    });\n\n    it('should encode unicode correctly', function (done) {\n      var app = createRedirectServerForDomain(null);\n      testRequestedRedirect(\n        app,\n        '/%e2%98%83',\n        '/%e2%98%83',\n        null,\n        done\n      );\n    });\n\n    it('should encode unicode correctly even with a bad host', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        'http://google.com\\\\@apple.com/%e2%98%83',\n        'http://google.com\\\\@apple.com/%e2%98%83',\n        'google.com',\n        done\n      );\n    });\n\n    it('should work correctly despite using deprecated url.parse', function (done) {\n      var app = createRedirectServerForDomain('google.com');\n      testRequestedRedirect(\n        app,\n        'https://google.com\\'.bb.com/1.html',\n        'https://google.com\\'.bb.com/1.html',\n        'google.com',\n        done\n      );\n    });\n\n    it('should encode file uri path', function (done) {\n      var app = createRedirectServerForDomain('');\n      testRequestedRedirect(\n        app,\n        'file:///etc\\\\passwd',\n        'file:///etc\\\\passwd',\n        '',\n        done\n      );\n    });\n  });\n})\n"
  },
  {
    "path": "test/res.redirect.js",
    "content": "'use strict'\n\nvar express = require('..');\nvar request = require('supertest');\nvar utils = require('./support/utils');\n\ndescribe('res', function(){\n  describe('.redirect(url)', function(){\n    it('should default to a 302 redirect', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect('http://google.com');\n      });\n\n      request(app)\n      .get('/')\n      .expect('location', 'http://google.com')\n      .expect(302, done)\n    })\n\n    it('should encode \"url\"', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.redirect('https://google.com?q=\\u2603 §10')\n      })\n\n      request(app)\n      .get('/')\n      .expect('Location', 'https://google.com?q=%E2%98%83%20%C2%A710')\n      .expect(302, done)\n    })\n\n    it('should not touch already-encoded sequences in \"url\"', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.redirect('https://google.com?q=%A710')\n      })\n\n      request(app)\n      .get('/')\n      .expect('Location', 'https://google.com?q=%A710')\n      .expect(302, done)\n    })\n  })\n\n  describe('.redirect(status, url)', function(){\n    it('should set the response status', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect(303, 'http://google.com');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Location', 'http://google.com')\n      .expect(303, done)\n    })\n  })\n\n  describe('when the request method is HEAD', function(){\n    it('should ignore the body', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect('http://google.com');\n      });\n\n      request(app)\n        .head('/')\n        .expect(302)\n        .expect('Location', 'http://google.com')\n        .expect(utils.shouldNotHaveBody())\n        .end(done)\n    })\n  })\n\n  describe('when accepting html', function(){\n    it('should respond with html', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect('http://google.com');\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', 'text/html')\n      .expect('Content-Type', /html/)\n      .expect('Location', 'http://google.com')\n      .expect(302, '<!DOCTYPE html><head><title>Found</title></head><body><p>Found. Redirecting to http://google.com</p></body>', done)\n    })\n\n    it('should escape the url', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect('<la\\'me>');\n      });\n\n      request(app)\n      .get('/')\n      .set('Host', 'http://example.com')\n      .set('Accept', 'text/html')\n      .expect('Content-Type', /html/)\n      .expect('Location', '%3Cla\\'me%3E')\n      .expect(302, '<!DOCTYPE html><head><title>Found</title></head><body><p>Found. Redirecting to %3Cla&#39;me%3E</p></body>', done)\n    })\n\n    it('should not render evil javascript links in anchor href (prevent XSS)', function(done){\n      var app = express();\n      var xss = 'javascript:eval(document.body.innerHTML=`<p>XSS</p>`);';\n      var encodedXss = 'javascript:eval(document.body.innerHTML=%60%3Cp%3EXSS%3C/p%3E%60);';\n\n      app.use(function(req, res){\n        res.redirect(xss);\n      });\n\n      request(app)\n      .get('/')\n      .set('Host', 'http://example.com')\n      .set('Accept', 'text/html')\n      .expect('Content-Type', /html/)\n      .expect('Location', encodedXss)\n      .expect(302, '<!DOCTYPE html><head><title>Found</title></head><body><p>Found. Redirecting to ' + encodedXss +'</p></body>', done);\n    });\n\n    it('should include the redirect type', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect(301, 'http://google.com');\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', 'text/html')\n      .expect('Content-Type', /html/)\n      .expect('Location', 'http://google.com')\n      .expect(301, '<!DOCTYPE html><head><title>Moved Permanently</title></head><body><p>Moved Permanently. Redirecting to http://google.com</p></body>', done);\n    })\n  })\n\n  describe('when accepting text', function(){\n    it('should respond with text', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect('http://google.com');\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', 'text/plain, */*')\n      .expect('Content-Type', /plain/)\n      .expect('Location', 'http://google.com')\n      .expect(302, 'Found. Redirecting to http://google.com', done)\n    })\n\n    it('should encode the url', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect('http://example.com/?param=<script>alert(\"hax\");</script>');\n      });\n\n      request(app)\n      .get('/')\n      .set('Host', 'http://example.com')\n      .set('Accept', 'text/plain, */*')\n      .expect('Content-Type', /plain/)\n      .expect('Location', 'http://example.com/?param=%3Cscript%3Ealert(%22hax%22);%3C/script%3E')\n      .expect(302, 'Found. Redirecting to http://example.com/?param=%3Cscript%3Ealert(%22hax%22);%3C/script%3E', done)\n    })\n\n    it('should include the redirect type', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect(301, 'http://google.com');\n      });\n\n      request(app)\n      .get('/')\n      .set('Accept', 'text/plain, */*')\n      .expect('Content-Type', /plain/)\n      .expect('Location', 'http://google.com')\n      .expect(301, 'Moved Permanently. Redirecting to http://google.com', done);\n    })\n  })\n\n  describe('when accepting neither text or html', function(){\n    it('should respond with an empty body', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.redirect('http://google.com');\n      });\n\n      request(app)\n        .get('/')\n        .set('Accept', 'application/octet-stream')\n        .expect(302)\n        .expect('location', 'http://google.com')\n        .expect('content-length', '0')\n        .expect(utils.shouldNotHaveHeader('Content-Type'))\n        .expect(utils.shouldNotHaveBody())\n        .end(done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.render.js",
    "content": "'use strict'\n\nvar express = require('..');\nvar path = require('node:path')\nvar request = require('supertest');\nvar tmpl = require('./support/tmpl');\n\ndescribe('res', function(){\n  describe('.render(name)', function(){\n    it('should support absolute paths', function(done){\n      var app = createApp();\n\n      app.locals.user = { name: 'tobi' };\n\n      app.use(function(req, res){\n        res.render(path.join(__dirname, 'fixtures', 'user.tmpl'))\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>tobi</p>', done);\n    })\n\n    it('should support absolute paths with \"view engine\"', function(done){\n      var app = createApp();\n\n      app.locals.user = { name: 'tobi' };\n      app.set('view engine', 'tmpl');\n\n      app.use(function(req, res){\n        res.render(path.join(__dirname, 'fixtures', 'user'))\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>tobi</p>', done);\n    })\n\n    it('should error without \"view engine\" set and file extension to a non-engine module', function (done) {\n      var app = createApp()\n\n      app.locals.user = { name: 'tobi' }\n\n      app.use(function (req, res) {\n        res.render(path.join(__dirname, 'fixtures', 'broken.send'))\n      })\n\n      request(app)\n      .get('/')\n      .expect(500, /does not provide a view engine/, done)\n    })\n\n    it('should error without \"view engine\" set and no file extension', function (done) {\n      var app = createApp();\n\n      app.locals.user = { name: 'tobi' };\n\n      app.use(function(req, res){\n        res.render(path.join(__dirname, 'fixtures', 'user'))\n      });\n\n      request(app)\n      .get('/')\n      .expect(500, /No default engine was specified/, done);\n    })\n\n    it('should expose app.locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.user = { name: 'tobi' };\n\n      app.use(function(req, res){\n        res.render('user.tmpl');\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>tobi</p>', done);\n    })\n\n    it('should expose app.locals with `name` property', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.name = 'tobi';\n\n      app.use(function(req, res){\n        res.render('name.tmpl');\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>tobi</p>', done);\n    })\n\n    it('should support index.<engine>', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.set('view engine', 'tmpl');\n\n      app.use(function(req, res){\n        res.render('blog/post');\n      });\n\n      request(app)\n      .get('/')\n      .expect('<h1>blog post</h1>', done);\n    })\n\n    describe('when an error occurs', function(){\n      it('should next(err)', function(done){\n        var app = createApp();\n\n        app.set('views', path.join(__dirname, 'fixtures'))\n\n        app.use(function(req, res){\n          res.render('user.tmpl');\n        });\n\n        app.use(function(err, req, res, next){\n          res.status(500).send('got error: ' + err.name)\n        });\n\n        request(app)\n        .get('/')\n        .expect(500, 'got error: RenderError', done)\n      })\n    })\n\n    describe('when \"view engine\" is given', function(){\n      it('should render the template', function(done){\n        var app = createApp();\n\n        app.set('view engine', 'tmpl');\n        app.set('views', path.join(__dirname, 'fixtures'))\n\n        app.use(function(req, res){\n          res.render('email');\n        });\n\n        request(app)\n        .get('/')\n        .expect('<p>This is an email</p>', done);\n      })\n    })\n\n    describe('when \"views\" is given', function(){\n      it('should lookup the file in the path', function(done){\n        var app = createApp();\n\n        app.set('views', path.join(__dirname, 'fixtures', 'default_layout'))\n\n        app.use(function(req, res){\n          res.render('user.tmpl', { user: { name: 'tobi' } });\n        });\n\n        request(app)\n        .get('/')\n        .expect('<p>tobi</p>', done);\n      })\n\n      describe('when array of paths', function(){\n        it('should lookup the file in the path', function(done){\n          var app = createApp();\n          var views = [\n            path.join(__dirname, 'fixtures', 'local_layout'),\n            path.join(__dirname, 'fixtures', 'default_layout')\n          ]\n\n          app.set('views', views);\n\n          app.use(function(req, res){\n            res.render('user.tmpl', { user: { name: 'tobi' } });\n          });\n\n          request(app)\n          .get('/')\n          .expect('<span>tobi</span>', done);\n        })\n\n        it('should lookup in later paths until found', function(done){\n          var app = createApp();\n          var views = [\n            path.join(__dirname, 'fixtures', 'local_layout'),\n            path.join(__dirname, 'fixtures', 'default_layout')\n          ]\n\n          app.set('views', views);\n\n          app.use(function(req, res){\n            res.render('name.tmpl', { name: 'tobi' });\n          });\n\n          request(app)\n          .get('/')\n          .expect('<p>tobi</p>', done);\n        })\n      })\n    })\n  })\n\n  describe('.render(name, option)', function(){\n    it('should render the template', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n\n      var user = { name: 'tobi' };\n\n      app.use(function(req, res){\n        res.render('user.tmpl', { user: user });\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>tobi</p>', done);\n    })\n\n    it('should expose app.locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.user = { name: 'tobi' };\n\n      app.use(function(req, res){\n        res.render('user.tmpl');\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>tobi</p>', done);\n    })\n\n    it('should expose res.locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n\n      app.use(function(req, res){\n        res.locals.user = { name: 'tobi' };\n        res.render('user.tmpl');\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>tobi</p>', done);\n    })\n\n    it('should give precedence to res.locals over app.locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.user = { name: 'tobi' };\n\n      app.use(function(req, res){\n        res.locals.user = { name: 'jane' };\n        res.render('user.tmpl', {});\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>jane</p>', done);\n    })\n\n    it('should give precedence to res.render() locals over res.locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      var jane = { name: 'jane' };\n\n      app.use(function(req, res){\n        res.locals.user = { name: 'tobi' };\n        res.render('user.tmpl', { user: jane });\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>jane</p>', done);\n    })\n\n    it('should give precedence to res.render() locals over app.locals', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n      app.locals.user = { name: 'tobi' };\n      var jane = { name: 'jane' };\n\n      app.use(function(req, res){\n        res.render('user.tmpl', { user: jane });\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>jane</p>', done);\n    })\n  })\n\n  describe('.render(name, options, fn)', function(){\n    it('should pass the resulting string', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n\n      app.use(function(req, res){\n        var tobi = { name: 'tobi' };\n        res.render('user.tmpl', { user: tobi }, function (err, html) {\n          html = html.replace('tobi', 'loki');\n          res.end(html);\n        });\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>loki</p>', done);\n    })\n  })\n\n  describe('.render(name, fn)', function(){\n    it('should pass the resulting string', function(done){\n      var app = createApp();\n\n      app.set('views', path.join(__dirname, 'fixtures'))\n\n      app.use(function(req, res){\n        res.locals.user = { name: 'tobi' };\n        res.render('user.tmpl', function (err, html) {\n          html = html.replace('tobi', 'loki');\n          res.end(html);\n        });\n      });\n\n      request(app)\n      .get('/')\n      .expect('<p>loki</p>', done);\n    })\n\n    describe('when an error occurs', function(){\n      it('should pass it to the callback', function(done){\n        var app = createApp();\n\n        app.set('views', path.join(__dirname, 'fixtures'))\n\n        app.use(function(req, res){\n          res.render('user.tmpl', function (err) {\n            if (err) {\n              res.status(500).send('got error: ' + err.name)\n            }\n          });\n        });\n\n        request(app)\n        .get('/')\n        .expect(500, 'got error: RenderError', done)\n      })\n    })\n  })\n})\n\nfunction createApp() {\n  var app = express();\n\n  app.engine('.tmpl', tmpl);\n\n  return app;\n}\n"
  },
  {
    "path": "test/res.send.js",
    "content": "'use strict'\n\nvar assert = require('node:assert')\nconst { Buffer } = require('node:buffer');\nvar express = require('..');\nvar methods = require('../lib/utils').methods;\nvar request = require('supertest');\nvar utils = require('./support/utils');\n\nvar shouldSkipQuery = require('./support/utils').shouldSkipQuery\n\ndescribe('res', function(){\n  describe('.send()', function(){\n    it('should set body to \"\"', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.send();\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, '', done);\n    })\n  })\n\n  describe('.send(null)', function(){\n    it('should set body to \"\"', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.send(null);\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Length', '0')\n      .expect(200, '', done);\n    })\n  })\n\n  describe('.send(undefined)', function(){\n    it('should set body to \"\"', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.send(undefined);\n      });\n\n      request(app)\n      .get('/')\n      .expect(200, '', done);\n    })\n  })\n\n  describe('.send(Number)', function(){\n    it('should send as application/json', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.send(1000);\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'application/json; charset=utf-8')\n      .expect(200, '1000', done)\n    })\n  })\n\n  describe('.send(String)', function(){\n    it('should send as html', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.send('<p>hey</p>');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/html; charset=utf-8')\n      .expect(200, '<p>hey</p>', done);\n    })\n\n    it('should set ETag', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        var str = Array(1000).join('-');\n        res.send(str);\n      });\n\n      request(app)\n      .get('/')\n      .expect('ETag', 'W/\"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g\"')\n      .expect(200, done);\n    })\n\n    it('should not override Content-Type', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set('Content-Type', 'text/plain').send('hey');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/plain; charset=utf-8')\n      .expect(200, 'hey', done);\n    })\n\n    it('should override charset in Content-Type', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set('Content-Type', 'text/plain; charset=iso-8859-1').send('hey');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/plain; charset=utf-8')\n      .expect(200, 'hey', done);\n    })\n\n    it('should keep charset in Content-Type for Buffers', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(Buffer.from('hi'))\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/plain; charset=iso-8859-1')\n      .expect(200, 'hi', done);\n    })\n  })\n\n  describe('.send(Buffer)', function(){\n    it('should send as octet-stream', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.send(Buffer.from('hello'))\n      });\n\n      request(app)\n        .get('/')\n        .expect(200)\n        .expect('Content-Type', 'application/octet-stream')\n        .expect(utils.shouldHaveBody(Buffer.from('hello')))\n        .end(done)\n    })\n\n    it('should set ETag', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.send(Buffer.alloc(999, '-'))\n      });\n\n      request(app)\n      .get('/')\n      .expect('ETag', 'W/\"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g\"')\n      .expect(200, done);\n    })\n\n    it('should not override Content-Type', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set('Content-Type', 'text/plain').send(Buffer.from('hey'))\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/plain; charset=utf-8')\n      .expect(200, 'hey', done);\n    })\n\n    it('should accept Uint8Array', function(done){\n      var app = express();\n      app.use(function(req, res){\n        const encodedHey = new TextEncoder().encode(\"hey\");\n        res.set(\"Content-Type\", \"text/plain\").send(encodedHey);\n      })\n\n      request(app)\n        .get(\"/\")\n        .expect(\"Content-Type\", \"text/plain; charset=utf-8\")\n        .expect(200, \"hey\", done);\n    })\n\n    it('should not override ETag', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.type('text/plain').set('ETag', '\"foo\"').send(Buffer.from('hey'))\n      })\n\n      request(app)\n      .get('/')\n      .expect('ETag', '\"foo\"')\n      .expect(200, 'hey', done)\n    })\n  })\n\n  describe('.send(Object)', function(){\n    it('should send as application/json', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.send({ name: 'tobi' });\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'application/json; charset=utf-8')\n      .expect(200, '{\"name\":\"tobi\"}', done)\n    })\n  })\n\n  describe('when the request method is HEAD', function(){\n    it('should ignore the body', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.send('yay');\n      });\n\n      request(app)\n        .head('/')\n        .expect(200)\n        .expect(utils.shouldNotHaveBody())\n        .end(done)\n    })\n  })\n\n  describe('when .statusCode is 204', function(){\n    it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.status(204).set('Transfer-Encoding', 'chunked').send('foo');\n      });\n\n      request(app)\n      .get('/')\n      .expect(utils.shouldNotHaveHeader('Content-Type'))\n      .expect(utils.shouldNotHaveHeader('Content-Length'))\n      .expect(utils.shouldNotHaveHeader('Transfer-Encoding'))\n      .expect(204, '', done);\n    })\n  })\n\n  describe('when .statusCode is 205', function () {\n    it('should strip Transfer-Encoding field and body, set Content-Length', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.status(205).set('Transfer-Encoding', 'chunked').send('foo')\n      })\n\n      request(app)\n        .get('/')\n        .expect(utils.shouldNotHaveHeader('Transfer-Encoding'))\n        .expect('Content-Length', '0')\n        .expect(205, '', done)\n    })\n  })\n\n  describe('when .statusCode is 304', function(){\n    it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.status(304).set('Transfer-Encoding', 'chunked').send('foo');\n      });\n\n      request(app)\n      .get('/')\n      .expect(utils.shouldNotHaveHeader('Content-Type'))\n      .expect(utils.shouldNotHaveHeader('Content-Length'))\n      .expect(utils.shouldNotHaveHeader('Transfer-Encoding'))\n      .expect(304, '', done);\n    })\n  })\n\n  it('should always check regardless of length', function(done){\n    var app = express();\n    var etag = '\"asdf\"';\n\n    app.use(function(req, res, next){\n      res.set('ETag', etag);\n      res.send('hey');\n    });\n\n    request(app)\n    .get('/')\n    .set('If-None-Match', etag)\n    .expect(304, done);\n  })\n\n  it('should respond with 304 Not Modified when fresh', function(done){\n    var app = express();\n    var etag = '\"asdf\"';\n\n    app.use(function(req, res){\n      var str = Array(1000).join('-');\n      res.set('ETag', etag);\n      res.send(str);\n    });\n\n    request(app)\n    .get('/')\n    .set('If-None-Match', etag)\n    .expect(304, done);\n  })\n\n  it('should not perform freshness check unless 2xx or 304', function(done){\n    var app = express();\n    var etag = '\"asdf\"';\n\n    app.use(function(req, res, next){\n      res.status(500);\n      res.set('ETag', etag);\n      res.send('hey');\n    });\n\n    request(app)\n    .get('/')\n    .set('If-None-Match', etag)\n    .expect('hey')\n    .expect(500, done);\n  })\n\n  it('should not support jsonp callbacks', function(done){\n    var app = express();\n\n    app.use(function(req, res){\n      res.send({ foo: 'bar' });\n    });\n\n    request(app)\n    .get('/?callback=foo')\n    .expect('{\"foo\":\"bar\"}', done);\n  })\n\n  it('should be chainable', function (done) {\n    var app = express()\n\n    app.use(function (req, res) {\n      assert.equal(res.send('hey'), res)\n    })\n\n    request(app)\n    .get('/')\n    .expect(200, 'hey', done)\n  })\n\n  describe('\"etag\" setting', function () {\n    describe('when enabled', function () {\n      it('should send ETag', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.send('kajdslfkasdf');\n        });\n\n        app.enable('etag');\n\n        request(app)\n        .get('/')\n        .expect('ETag', 'W/\"c-IgR/L5SF7CJQff4wxKGF/vfPuZ0\"')\n        .expect(200, done);\n      });\n\n      methods.forEach(function (method) {\n        if (method === 'connect') return;\n\n        it('should send ETag in response to ' + method.toUpperCase() + ' request', function (done) {\n          if (method === 'query' && shouldSkipQuery(process.versions.node)) {\n            this.skip()\n          }\n          var app = express();\n\n          app[method]('/', function (req, res) {\n            res.send('kajdslfkasdf');\n          });\n\n          request(app)\n          [method]('/')\n          .expect('ETag', 'W/\"c-IgR/L5SF7CJQff4wxKGF/vfPuZ0\"')\n          .expect(200, done);\n        })\n      });\n\n      it('should send ETag for empty string response', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.send('');\n        });\n\n        app.enable('etag');\n\n        request(app)\n        .get('/')\n        .expect('ETag', 'W/\"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk\"')\n        .expect(200, done);\n      })\n\n      it('should send ETag for long response', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          var str = Array(1000).join('-');\n          res.send(str);\n        });\n\n        app.enable('etag');\n\n        request(app)\n        .get('/')\n        .expect('ETag', 'W/\"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g\"')\n        .expect(200, done);\n      });\n\n      it('should not override ETag when manually set', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.set('etag', '\"asdf\"');\n          res.send('hello!');\n        });\n\n        app.enable('etag');\n\n        request(app)\n        .get('/')\n        .expect('ETag', '\"asdf\"')\n        .expect(200, done);\n      });\n\n      it('should not send ETag for res.send()', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.send();\n        });\n\n        app.enable('etag');\n\n        request(app)\n        .get('/')\n        .expect(utils.shouldNotHaveHeader('ETag'))\n        .expect(200, done);\n      })\n    });\n\n    describe('when disabled', function () {\n      it('should send no ETag', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          var str = Array(1000).join('-');\n          res.send(str);\n        });\n\n        app.disable('etag');\n\n        request(app)\n        .get('/')\n        .expect(utils.shouldNotHaveHeader('ETag'))\n        .expect(200, done);\n      });\n\n      it('should send ETag when manually set', function (done) {\n        var app = express();\n\n        app.disable('etag');\n\n        app.use(function (req, res) {\n          res.set('etag', '\"asdf\"');\n          res.send('hello!');\n        });\n\n        request(app)\n        .get('/')\n        .expect('ETag', '\"asdf\"')\n        .expect(200, done);\n      });\n    });\n\n    describe('when \"strong\"', function () {\n      it('should send strong ETag', function (done) {\n        var app = express();\n\n        app.set('etag', 'strong');\n\n        app.use(function (req, res) {\n          res.send('hello, world!');\n        });\n\n        request(app)\n        .get('/')\n        .expect('ETag', '\"d-HwnTDHB9U/PRbFMN1z1wps51lqk\"')\n        .expect(200, done);\n      })\n    })\n\n    describe('when \"weak\"', function () {\n      it('should send weak ETag', function (done) {\n        var app = express();\n\n        app.set('etag', 'weak');\n\n        app.use(function (req, res) {\n          res.send('hello, world!');\n        });\n\n        request(app)\n        .get('/')\n        .expect('ETag', 'W/\"d-HwnTDHB9U/PRbFMN1z1wps51lqk\"')\n        .expect(200, done)\n      })\n    })\n\n    describe('when a function', function () {\n      it('should send custom ETag', function (done) {\n        var app = express();\n\n        app.set('etag', function (body, encoding) {\n          var chunk = !Buffer.isBuffer(body)\n            ? Buffer.from(body, encoding)\n            : body;\n          assert.strictEqual(chunk.toString(), 'hello, world!')\n          return '\"custom\"';\n        });\n\n        app.use(function (req, res) {\n          res.send('hello, world!');\n        });\n\n        request(app)\n        .get('/')\n        .expect('ETag', '\"custom\"')\n        .expect(200, done);\n      })\n\n      it('should not send falsy ETag', function (done) {\n        var app = express();\n\n        app.set('etag', function (body, encoding) {\n          return undefined;\n        });\n\n        app.use(function (req, res) {\n          res.send('hello, world!');\n        });\n\n        request(app)\n        .get('/')\n        .expect(utils.shouldNotHaveHeader('ETag'))\n        .expect(200, done);\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.sendFile.js",
    "content": "'use strict'\n\nvar after = require('after');\nvar assert = require('node:assert')\nvar AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage\nconst { Buffer } = require('node:buffer');\n\nvar express = require('../')\n  , request = require('supertest')\nvar onFinished = require('on-finished');\nvar path = require('node:path');\nvar fixtures = path.join(__dirname, 'fixtures');\nvar utils = require('./support/utils');\n\ndescribe('res', function(){\n  describe('.sendFile(path)', function () {\n    it('should error missing path', function (done) {\n      var app = createApp();\n\n      request(app)\n      .get('/')\n      .expect(500, /path.*required/, done);\n    });\n\n    it('should error for non-string path', function (done) {\n      var app = createApp(42)\n\n      request(app)\n      .get('/')\n      .expect(500, /TypeError: path must be a string to res.sendFile/, done)\n    })\n\n    it('should error for non-absolute path', function (done) {\n      var app = createApp('name.txt')\n\n      request(app)\n        .get('/')\n        .expect(500, /TypeError: path must be absolute/, done)\n    })\n\n    it('should transfer a file', function (done) {\n      var app = createApp(path.resolve(fixtures, 'name.txt'));\n\n      request(app)\n      .get('/')\n      .expect(200, 'tobi', done);\n    });\n\n    it('should transfer a file with special characters in string', function (done) {\n      var app = createApp(path.resolve(fixtures, '% of dogs.txt'));\n\n      request(app)\n      .get('/')\n      .expect(200, '20%', done);\n    });\n\n    it('should include ETag', function (done) {\n      var app = createApp(path.resolve(fixtures, 'name.txt'));\n\n      request(app)\n      .get('/')\n      .expect('ETag', /^(?:W\\/)?\"[^\"]+\"$/)\n      .expect(200, 'tobi', done);\n    });\n\n    it('should 304 when ETag matches', function (done) {\n      var app = createApp(path.resolve(fixtures, 'name.txt'));\n\n      request(app)\n      .get('/')\n      .expect('ETag', /^(?:W\\/)?\"[^\"]+\"$/)\n      .expect(200, 'tobi', function (err, res) {\n        if (err) return done(err);\n        var etag = res.headers.etag;\n        request(app)\n        .get('/')\n        .set('If-None-Match', etag)\n        .expect(304, done);\n      });\n    });\n\n    it('should disable the ETag function if requested', function (done) {\n      var app = createApp(path.resolve(fixtures, 'name.txt')).disable('etag');\n\n      request(app)\n      .get('/')\n      .expect(handleHeaders)\n      .expect(200, done);\n\n      function handleHeaders (res) {\n        assert(res.headers.etag === undefined);\n      }\n    });\n\n    it('should 404 for directory', function (done) {\n      var app = createApp(path.resolve(fixtures, 'blog'));\n\n      request(app)\n      .get('/')\n      .expect(404, done);\n    });\n\n    it('should 404 when not found', function (done) {\n      var app = createApp(path.resolve(fixtures, 'does-no-exist'));\n\n      app.use(function (req, res) {\n        res.statusCode = 200;\n        res.send('no!');\n      });\n\n      request(app)\n      .get('/')\n      .expect(404, done);\n    });\n\n    it('should send cache-control by default', function (done) {\n      var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'))\n\n      request(app)\n        .get('/')\n        .expect('Cache-Control', 'public, max-age=0')\n        .expect(200, done)\n    })\n\n    it('should not serve dotfiles by default', function (done) {\n      var app = createApp(path.resolve(__dirname, 'fixtures/.name'))\n\n      request(app)\n        .get('/')\n        .expect(404, done)\n    })\n\n    it('should not override manual content-types', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.contentType('application/x-bogus');\n        res.sendFile(path.resolve(fixtures, 'name.txt'));\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'application/x-bogus')\n      .end(done);\n    })\n\n    it('should not error if the client aborts', function (done) {\n      var app = express();\n      var cb = after(2, done)\n      var error = null\n\n      app.use(function (req, res) {\n        setImmediate(function () {\n          res.sendFile(path.resolve(fixtures, 'name.txt'));\n          setTimeout(function () {\n            cb(error)\n          }, 10)\n        })\n        test.req.abort()\n      });\n\n      app.use(function (err, req, res, next) {\n        error = err\n        next(err)\n      });\n\n      var server = app.listen()\n      var test = request(server).get('/')\n      test.end(function (err) {\n        assert.ok(err)\n        server.close(cb)\n      })\n    })\n  })\n\n  describe('.sendFile(path, fn)', function () {\n    it('should invoke the callback when complete', function (done) {\n      var cb = after(2, done);\n      var app = createApp(path.resolve(fixtures, 'name.txt'), cb);\n\n      request(app)\n      .get('/')\n      .expect(200, cb);\n    })\n\n    it('should invoke the callback when client aborts', function (done) {\n      var cb = after(2, done)\n      var app = express();\n\n      app.use(function (req, res) {\n        setImmediate(function () {\n          res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) {\n            assert.ok(err)\n            assert.strictEqual(err.code, 'ECONNABORTED')\n            cb()\n          });\n        });\n        test.req.abort()\n      });\n\n      var server = app.listen()\n      var test = request(server).get('/')\n      test.end(function (err) {\n        assert.ok(err)\n        server.close(cb)\n      })\n    })\n\n    it('should invoke the callback when client already aborted', function (done) {\n      var cb = after(2, done)\n      var app = express();\n\n      app.use(function (req, res) {\n        onFinished(res, function () {\n          res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) {\n            assert.ok(err)\n            assert.strictEqual(err.code, 'ECONNABORTED')\n            cb()\n          });\n        });\n        test.req.abort()\n      });\n\n      var server = app.listen()\n      var test = request(server).get('/')\n      test.end(function (err) {\n        assert.ok(err)\n        server.close(cb)\n      })\n    })\n\n    it('should invoke the callback without error when HEAD', function (done) {\n      var app = express();\n      var cb = after(2, done);\n\n      app.use(function (req, res) {\n        res.sendFile(path.resolve(fixtures, 'name.txt'), cb);\n      });\n\n      request(app)\n      .head('/')\n      .expect(200, cb);\n    });\n\n    it('should invoke the callback without error when 304', function (done) {\n      var app = express();\n      var cb = after(3, done);\n\n      app.use(function (req, res) {\n        res.sendFile(path.resolve(fixtures, 'name.txt'), cb);\n      });\n\n      request(app)\n      .get('/')\n      .expect('ETag', /^(?:W\\/)?\"[^\"]+\"$/)\n      .expect(200, 'tobi', function (err, res) {\n        if (err) return cb(err);\n        var etag = res.headers.etag;\n        request(app)\n        .get('/')\n        .set('If-None-Match', etag)\n        .expect(304, cb);\n      });\n    });\n\n    it('should invoke the callback on 404', function(done){\n      var app = express();\n\n      app.use(function (req, res) {\n        res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) {\n          res.send(err ? 'got ' + err.status + ' error' : 'no error')\n        });\n      });\n\n      request(app)\n        .get('/')\n        .expect(200, 'got 404 error', done)\n    })\n\n    describe('async local storage', function () {\n      it('should persist store', function (done) {\n        var app = express()\n        var cb = after(2, done)\n        var store = { foo: 'bar' }\n\n        app.use(function (req, res, next) {\n          req.asyncLocalStorage = new AsyncLocalStorage()\n          req.asyncLocalStorage.run(store, next)\n        })\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) {\n            if (err) return cb(err)\n\n            var local = req.asyncLocalStorage.getStore()\n\n            assert.strictEqual(local.foo, 'bar')\n            cb()\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect('Content-Type', 'text/plain; charset=utf-8')\n          .expect(200, 'tobi', cb)\n      })\n\n      it('should persist store on error', function (done) {\n        var app = express()\n        var store = { foo: 'bar' }\n\n        app.use(function (req, res, next) {\n          req.asyncLocalStorage = new AsyncLocalStorage()\n          req.asyncLocalStorage.run(store, next)\n        })\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) {\n            var local = req.asyncLocalStorage.getStore()\n\n            if (local) {\n              res.setHeader('x-store-foo', String(local.foo))\n            }\n\n            res.send(err ? 'got ' + err.status + ' error' : 'no error')\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('x-store-foo', 'bar')\n          .expect('got 404 error')\n          .end(done)\n      })\n    })\n  })\n\n  describe('.sendFile(path, options)', function () {\n    it('should pass options to send module', function (done) {\n      request(createApp(path.resolve(fixtures, 'name.txt'), { start: 0, end: 1 }))\n      .get('/')\n      .expect(200, 'to', done)\n    })\n\n    describe('with \"acceptRanges\" option', function () {\n      describe('when true', function () {\n        it('should advertise byte range accepted', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'nums.txt'), {\n              acceptRanges: true\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Accept-Ranges', 'bytes')\n            .expect('123456789')\n            .end(done)\n        })\n\n        it('should respond to range request', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'nums.txt'), {\n              acceptRanges: true\n            })\n          })\n\n          request(app)\n            .get('/')\n            .set('Range', 'bytes=0-4')\n            .expect(206, '12345', done)\n        })\n      })\n\n      describe('when false', function () {\n        it('should not advertise accept-ranges', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'nums.txt'), {\n              acceptRanges: false\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect(utils.shouldNotHaveHeader('Accept-Ranges'))\n            .end(done)\n        })\n\n        it('should not honor range requests', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'nums.txt'), {\n              acceptRanges: false\n            })\n          })\n\n          request(app)\n            .get('/')\n            .set('Range', 'bytes=0-4')\n            .expect(200, '123456789', done)\n        })\n      })\n    })\n\n    describe('with \"cacheControl\" option', function () {\n      describe('when true', function () {\n        it('should send cache-control header', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              cacheControl: true\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Cache-Control', 'public, max-age=0')\n            .end(done)\n        })\n      })\n\n      describe('when false', function () {\n        it('should not send cache-control header', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              cacheControl: false\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect(utils.shouldNotHaveHeader('Cache-Control'))\n            .end(done)\n        })\n      })\n    })\n\n    describe('with \"dotfiles\" option', function () {\n      describe('when \"allow\"', function () {\n        it('should allow dotfiles', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, '.name'), {\n              dotfiles: 'allow'\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect(utils.shouldHaveBody(Buffer.from('tobi')))\n            .end(done)\n        })\n      })\n\n      describe('when \"deny\"', function () {\n        it('should deny dotfiles', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, '.name'), {\n              dotfiles: 'deny'\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(403)\n            .expect(/Forbidden/)\n            .end(done)\n        })\n      })\n\n      describe('when \"ignore\"', function () {\n        it('should ignore dotfiles', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, '.name'), {\n              dotfiles: 'ignore'\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(404)\n            .expect(/Not Found/)\n            .end(done)\n        })\n      })\n    })\n\n    describe('with \"headers\" option', function () {\n      it('should set headers on response', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'user.html'), {\n            headers: {\n              'X-Foo': 'Bar',\n              'X-Bar': 'Foo'\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('X-Foo', 'Bar')\n          .expect('X-Bar', 'Foo')\n          .end(done)\n      })\n\n      it('should use last header when duplicated', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'user.html'), {\n            headers: {\n              'X-Foo': 'Bar',\n              'x-foo': 'bar'\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('X-Foo', 'bar')\n          .end(done)\n      })\n\n      it('should override Content-Type', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'user.html'), {\n            headers: {\n              'Content-Type': 'text/x-custom'\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Content-Type', 'text/x-custom')\n          .end(done)\n      })\n\n      it('should not set headers on 404', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'does-not-exist'), {\n            headers: {\n              'X-Foo': 'Bar'\n            }\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(404)\n          .expect(utils.shouldNotHaveHeader('X-Foo'))\n          .end(done)\n      })\n    })\n\n    describe('with \"immutable\" option', function () {\n      describe('when true', function () {\n        it('should send cache-control header with immutable', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              immutable: true\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Cache-Control', 'public, max-age=0, immutable')\n            .end(done)\n        })\n      })\n\n      describe('when false', function () {\n        it('should not send cache-control header with immutable', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              immutable: false\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Cache-Control', 'public, max-age=0')\n            .end(done)\n        })\n      })\n    })\n\n    describe('with \"lastModified\" option', function () {\n      describe('when true', function () {\n        it('should send last-modified header', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              lastModified: true\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect(utils.shouldHaveHeader('Last-Modified'))\n            .end(done)\n        })\n\n        it('should conditionally respond with if-modified-since', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              lastModified: true\n            })\n          })\n\n          request(app)\n            .get('/')\n            .set('If-Modified-Since', (new Date(Date.now() + 99999).toUTCString()))\n            .expect(304, done)\n        })\n      })\n\n      describe('when false', function () {\n        it('should not have last-modified header', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              lastModified: false\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect(utils.shouldNotHaveHeader('Last-Modified'))\n            .end(done)\n        })\n\n        it('should not honor if-modified-since', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              lastModified: false\n            })\n          })\n\n          request(app)\n            .get('/')\n            .set('If-Modified-Since', (new Date(Date.now() + 99999).toUTCString()))\n            .expect(200)\n            .expect(utils.shouldNotHaveHeader('Last-Modified'))\n            .end(done)\n        })\n      })\n    })\n\n    describe('with \"maxAge\" option', function () {\n      it('should set cache-control max-age to milliseconds', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'user.html'), {\n            maxAge: 20000\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Cache-Control', 'public, max-age=20')\n          .end(done)\n      })\n\n      it('should cap cache-control max-age to 1 year', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'user.html'), {\n            maxAge: 99999999999\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Cache-Control', 'public, max-age=31536000')\n          .end(done)\n      })\n\n      it('should min cache-control max-age to 0', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'user.html'), {\n            maxAge: -20000\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Cache-Control', 'public, max-age=0')\n          .end(done)\n      })\n\n      it('should floor cache-control max-age', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile(path.resolve(fixtures, 'user.html'), {\n            maxAge: 21911.23\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200)\n          .expect('Cache-Control', 'public, max-age=21')\n          .end(done)\n      })\n\n      describe('when cacheControl: false', function () {\n        it('should not send cache-control', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              cacheControl: false,\n              maxAge: 20000\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect(utils.shouldNotHaveHeader('Cache-Control'))\n            .end(done)\n        })\n      })\n\n      describe('when string', function () {\n        it('should accept plain number as milliseconds', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              maxAge: '20000'\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Cache-Control', 'public, max-age=20')\n            .end(done)\n        })\n\n        it('should accept suffix \"s\" for seconds', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              maxAge: '20s'\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Cache-Control', 'public, max-age=20')\n            .end(done)\n        })\n\n        it('should accept suffix \"m\" for minutes', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              maxAge: '20m'\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Cache-Control', 'public, max-age=1200')\n            .end(done)\n        })\n\n        it('should accept suffix \"d\" for days', function (done) {\n          var app = express()\n\n          app.use(function (req, res) {\n            res.sendFile(path.resolve(fixtures, 'user.html'), {\n              maxAge: '20d'\n            })\n          })\n\n          request(app)\n            .get('/')\n            .expect(200)\n            .expect('Cache-Control', 'public, max-age=1728000')\n            .end(done)\n        })\n      })\n    })\n\n    describe('with \"root\" option', function () {\n      it('should allow relative path', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile('name.txt', {\n            root: fixtures\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200, 'tobi', done)\n      })\n\n      it('should allow up within root', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile('fake/../name.txt', {\n            root: fixtures\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(200, 'tobi', done)\n      })\n\n      it('should reject up outside root', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile('..' + path.sep + path.relative(path.dirname(fixtures), path.join(fixtures, 'name.txt')), {\n            root: fixtures\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(403, done)\n      })\n\n      it('should reject reading outside root', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.sendFile('../name.txt', {\n            root: fixtures\n          })\n        })\n\n        request(app)\n          .get('/')\n          .expect(403, done)\n      })\n    })\n  })\n})\n\nfunction createApp(path, options, fn) {\n  var app = express();\n\n  app.use(function (req, res) {\n    res.sendFile(path, options, fn);\n  });\n\n  return app;\n}\n"
  },
  {
    "path": "test/res.sendStatus.js",
    "content": "'use strict'\n\nvar express = require('..')\nvar request = require('supertest')\n\ndescribe('res', function () {\n  describe('.sendStatus(statusCode)', function () {\n    it('should send the status code and message as body', function (done) {\n      var app = express();\n\n      app.use(function(req, res){\n        res.sendStatus(201);\n      });\n\n      request(app)\n      .get('/')\n      .expect(201, 'Created', done);\n    })\n\n    it('should work with unknown code', function (done) {\n      var app = express();\n\n      app.use(function(req, res){\n        res.sendStatus(599);\n      });\n\n      request(app)\n      .get('/')\n      .expect(599, '599', done);\n    })\n\n    it('should raise error for invalid status code', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.sendStatus(undefined).end()\n      })\n\n      request(app)\n        .get('/')\n        .expect(500, /TypeError: Invalid status code/, done)\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.set.js",
    "content": "'use strict'\n\nvar express = require('..');\nvar request = require('supertest');\n\ndescribe('res', function(){\n  describe('.set(field, value)', function(){\n    it('should set the response header field', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set('Content-Type', 'text/x-foo; charset=utf-8').end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/x-foo; charset=utf-8')\n      .end(done);\n    })\n\n    it('should coerce to a string', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.set('X-Number', 123);\n        res.end(typeof res.get('X-Number'));\n      });\n\n      request(app)\n      .get('/')\n      .expect('X-Number', '123')\n      .expect(200, 'string', done);\n    })\n  })\n\n  describe('.set(field, values)', function(){\n    it('should set multiple response header fields', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set('Set-Cookie', [\"type=ninja\", \"language=javascript\"]);\n        res.send(res.get('Set-Cookie'));\n      });\n\n      request(app)\n      .get('/')\n      .expect('[\"type=ninja\",\"language=javascript\"]', done);\n    })\n\n    it('should coerce to an array of strings', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.set('X-Numbers', [123, 456]);\n        res.end(JSON.stringify(res.get('X-Numbers')));\n      });\n\n      request(app)\n      .get('/')\n      .expect('X-Numbers', '123, 456')\n      .expect(200, '[\"123\",\"456\"]', done);\n    })\n\n    it('should not set a charset of one is already set', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.set('Content-Type', 'text/html; charset=lol');\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/html; charset=lol')\n      .expect(200, done);\n    })\n\n    it('should throw when Content-Type is an array', function (done) {\n      var app = express()\n\n      app.use(function (req, res) {\n        res.set('Content-Type', ['text/html'])\n        res.end()\n      });\n\n      request(app)\n      .get('/')\n      .expect(500, /TypeError: Content-Type cannot be set to an Array/, done)\n    })\n  })\n\n  describe('.set(object)', function(){\n    it('should set multiple fields', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.set({\n          'X-Foo': 'bar',\n          'X-Bar': 'baz'\n        }).end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('X-Foo', 'bar')\n      .expect('X-Bar', 'baz')\n      .end(done);\n    })\n\n    it('should coerce to a string', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.set({ 'X-Number': 123 });\n        res.end(typeof res.get('X-Number'));\n      });\n\n      request(app)\n      .get('/')\n      .expect('X-Number', '123')\n      .expect(200, 'string', done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/res.status.js",
    "content": "'use strict'\nconst express = require('../.');\nconst request = require('supertest');\n\ndescribe('res', function () {\n  describe('.status(code)', function () {\n\n    it('should set the status code when valid', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.status(200).end();\n      });\n\n      request(app)\n        .get('/')\n        .expect(200, done);\n    });\n\n    describe('accept valid ranges', function() {\n      // not testing w/ 100, because that has specific meaning and behavior in Node as Expect: 100-continue\n      it('should set the response status code to 101', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.status(101).end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(101, done)\n      })\n\n      it('should set the response status code to 201', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.status(201).end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(201, done)\n      })\n\n      it('should set the response status code to 302', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.status(302).end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(302, done)\n      })\n\n      it('should set the response status code to 403', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.status(403).end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(403, done)\n      })\n\n      it('should set the response status code to 501', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.status(501).end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(501, done)\n      })\n\n      it('should set the response status code to 700', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.status(700).end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(700, done)\n      })\n\n      it('should set the response status code to 800', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.status(800).end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(800, done)\n      })\n\n      it('should set the response status code to 900', function (done) {\n        var app = express()\n\n        app.use(function (req, res) {\n          res.status(900).end()\n        })\n\n        request(app)\n          .get('/')\n          .expect(900, done)\n      })\n    })\n\n    describe('invalid status codes', function () {\n      it('should raise error for status code below 100', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.status(99).end();\n        });\n\n        request(app)\n          .get('/')\n          .expect(500, /Invalid status code/, done);\n      });\n\n      it('should raise error for status code above 999', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.status(1000).end();\n        });\n\n        request(app)\n          .get('/')\n          .expect(500, /Invalid status code/, done);\n      });\n\n      it('should raise error for non-integer status codes', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.status(200.1).end();\n        });\n\n        request(app)\n          .get('/')\n          .expect(500, /Invalid status code/, done);\n      });\n\n      it('should raise error for undefined status code', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.status(undefined).end();\n        });\n\n        request(app)\n          .get('/')\n          .expect(500, /Invalid status code/, done);\n      });\n\n      it('should raise error for null status code', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.status(null).end();\n        });\n\n        request(app)\n          .get('/')\n          .expect(500, /Invalid status code/, done);\n      });\n\n      it('should raise error for string status code', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.status(\"200\").end();\n        });\n\n        request(app)\n          .get('/')\n          .expect(500, /Invalid status code/, done);\n      });\n\n      it('should raise error for NaN status code', function (done) {\n        var app = express();\n\n        app.use(function (req, res) {\n          res.status(NaN).end();\n        });\n\n        request(app)\n          .get('/')\n          .expect(500, /Invalid status code/, done);\n      });\n    });\n  });\n});\n\n"
  },
  {
    "path": "test/res.type.js",
    "content": "'use strict'\n\nvar express = require('../')\n  , request = require('supertest');\n\ndescribe('res', function(){\n  describe('.type(str)', function(){\n    it('should set the Content-Type based on a filename', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.type('foo.js').end('var name = \"tj\";');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'text/javascript; charset=utf-8')\n      .end(done)\n    })\n\n    it('should default to application/octet-stream', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.type('rawr').end('var name = \"tj\";');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'application/octet-stream', done);\n    })\n\n    it('should set the Content-Type with type/subtype', function(done){\n      var app = express();\n\n      app.use(function(req, res){\n        res.type('application/vnd.amazon.ebook')\n          .end('var name = \"tj\";');\n      });\n\n      request(app)\n      .get('/')\n      .expect('Content-Type', 'application/vnd.amazon.ebook', done);\n    })\n\n    describe('edge cases', function(){\n      it('should handle empty string gracefully', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.type('').end('test');\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/octet-stream')\n        .end(done);\n      })\n\n      it('should handle file extension with dots', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.type('.json').end('{\"test\": true}');\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .end(done);\n      })\n\n      it('should handle multiple file extensions', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.type('file.tar.gz').end('compressed');\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/gzip')\n        .end(done);\n      })\n\n      it('should handle uppercase extensions', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.type('FILE.JSON').end('{\"test\": true}');\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .end(done);\n      })\n\n      it('should handle extension with special characters', function(done){\n        var app = express();\n\n        app.use(function(req, res){\n          res.type('file@test.json').end('{\"test\": true}');\n        });\n\n        request(app)\n        .get('/')\n        .expect('Content-Type', 'application/json; charset=utf-8')\n        .end(done);\n      })\n    })\n  })\n})\n\n\n"
  },
  {
    "path": "test/res.vary.js",
    "content": "'use strict'\n\nvar express = require('..');\nvar request = require('supertest');\nvar utils = require('./support/utils');\n\ndescribe('res.vary()', function(){\n  describe('with no arguments', function(){\n    it('should throw error', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.vary();\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect(500, /field.*required/, done)\n    })\n  })\n\n  describe('with an empty array', function(){\n    it('should not set Vary', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.vary([]);\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect(utils.shouldNotHaveHeader('Vary'))\n      .expect(200, done);\n    })\n  })\n\n  describe('with an array', function(){\n    it('should set the values', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.vary(['Accept', 'Accept-Language', 'Accept-Encoding']);\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Vary', 'Accept, Accept-Language, Accept-Encoding')\n      .expect(200, done);\n    })\n  })\n\n  describe('with a string', function(){\n    it('should set the value', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.vary('Accept');\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Vary', 'Accept')\n      .expect(200, done);\n    })\n  })\n\n  describe('when the value is present', function(){\n    it('should not add it again', function (done) {\n      var app = express();\n\n      app.use(function (req, res) {\n        res.vary('Accept');\n        res.vary('Accept-Encoding');\n        res.vary('Accept-Encoding');\n        res.vary('Accept-Encoding');\n        res.vary('Accept');\n        res.end();\n      });\n\n      request(app)\n      .get('/')\n      .expect('Vary', 'Accept, Accept-Encoding')\n      .expect(200, done);\n    })\n  })\n})\n"
  },
  {
    "path": "test/support/env.js",
    "content": "\nprocess.env.NODE_ENV = 'test';\nprocess.env.NO_DEPRECATION = 'body-parser,express';\n"
  },
  {
    "path": "test/support/tmpl.js",
    "content": "var fs = require('node:fs');\n\nvar variableRegExp = /\\$([0-9a-zA-Z\\.]+)/g;\n\nmodule.exports = function renderFile(fileName, options, callback) {\n  function onReadFile(err, str) {\n    if (err) {\n      callback(err);\n      return;\n    }\n\n    try {\n      str = str.replace(variableRegExp, generateVariableLookup(options));\n    } catch (e) {\n      err = e;\n      err.name = 'RenderError'\n    }\n\n    callback(err, str);\n  }\n\n  fs.readFile(fileName, 'utf8', onReadFile);\n};\n\nfunction generateVariableLookup(data) {\n  return function variableLookup(str, path) {\n    var parts = path.split('.');\n    var value = data;\n\n    for (var i = 0; i < parts.length; i++) {\n      value = value[parts[i]];\n    }\n\n    return value;\n  };\n}\n"
  },
  {
    "path": "test/support/utils.js",
    "content": "\n/**\n * Module dependencies.\n * @private\n */\n\nvar assert = require('node:assert');\nconst { Buffer } = require('node:buffer');\n\n/**\n * Module exports.\n * @public\n */\n\nexports.shouldHaveBody = shouldHaveBody\nexports.shouldHaveHeader = shouldHaveHeader\nexports.shouldNotHaveBody = shouldNotHaveBody\nexports.shouldNotHaveHeader = shouldNotHaveHeader;\nexports.shouldSkipQuery = shouldSkipQuery\n\n/**\n * Assert that a supertest response has a specific body.\n *\n * @param {Buffer} buf\n * @returns {function}\n */\n\nfunction shouldHaveBody (buf) {\n  return function (res) {\n    var body = !Buffer.isBuffer(res.body)\n      ? Buffer.from(res.text)\n      : res.body\n    assert.ok(body, 'response has body')\n    assert.strictEqual(body.toString('hex'), buf.toString('hex'))\n  }\n}\n\n/**\n * Assert that a supertest response does have a header.\n *\n * @param {string} header Header name to check\n * @returns {function}\n */\n\nfunction shouldHaveHeader (header) {\n  return function (res) {\n    assert.ok((header.toLowerCase() in res.headers), 'should have header ' + header)\n  }\n}\n\n/**\n * Assert that a supertest response does not have a body.\n *\n * @returns {function}\n */\n\nfunction shouldNotHaveBody () {\n  return function (res) {\n    assert.ok(res.text === '' || res.text === undefined)\n  }\n}\n\n/**\n * Assert that a supertest response does not have a header.\n *\n * @param {string} header Header name to check\n * @returns {function}\n */\nfunction shouldNotHaveHeader(header) {\n  return function (res) {\n    assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header);\n  };\n}\n\nfunction getMajorVersion(versionString) {\n  return versionString.split('.')[0];\n}\n\nfunction shouldSkipQuery(versionString) {\n  // Skipping HTTP QUERY tests below Node 22, QUERY wasn't fully supported by Node until 22\n  // we could update this implementation to run on supported versions of 21 once they exist\n  // upstream tracking https://github.com/nodejs/node/issues/51562\n  // express tracking issue: https://github.com/expressjs/express/issues/5615\n  return Number(getMajorVersion(versionString)) < 22\n}\n\n"
  },
  {
    "path": "test/utils.js",
    "content": "'use strict'\n\nvar assert = require('node:assert');\nconst { Buffer } = require('node:buffer');\nvar utils = require('../lib/utils');\n\ndescribe('utils.etag(body, encoding)', function(){\n  it('should support strings', function(){\n    assert.strictEqual(utils.etag('express!'),\n      '\"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg\"')\n  })\n\n  it('should support utf8 strings', function(){\n    assert.strictEqual(utils.etag('express❤', 'utf8'),\n      '\"a-JBiXf7GyzxwcrxY4hVXUwa7tmks\"')\n  })\n\n  it('should support buffer', function(){\n    assert.strictEqual(utils.etag(Buffer.from('express!')),\n      '\"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg\"')\n  })\n\n  it('should support empty string', function(){\n    assert.strictEqual(utils.etag(''),\n      '\"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk\"')\n  })\n})\n\ndescribe('utils.normalizeType acceptParams method', () => {\n  it('should handle a type with a malformed parameter and break the loop in acceptParams', () => {\n    const result = utils.normalizeType('text/plain;invalid');\n    assert.deepEqual(result,{\n      value: 'text/plain',\n      quality: 1,\n      params: {} // No parameters are added since \"invalid\" has no \"=\"\n    });\n  });\n\n  it('should default to application/octet-stream when mime lookup fails', () => {\n    const result = utils.normalizeType('unknown-extension-xyz');\n    assert.deepEqual(result, {\n      value: 'application/octet-stream',\n      params: {}\n    });\n  });\n});\n\ndescribe('utils.setCharset(type, charset)', function () {\n  it('should do anything without type', function () {\n    assert.strictEqual(utils.setCharset(), undefined);\n  });\n\n  it('should return type if not given charset', function () {\n    assert.strictEqual(utils.setCharset('text/html'), 'text/html');\n  });\n\n  it('should keep charset if not given charset', function () {\n    assert.strictEqual(utils.setCharset('text/html; charset=utf-8'), 'text/html; charset=utf-8');\n  });\n\n  it('should set charset', function () {\n    assert.strictEqual(utils.setCharset('text/html', 'utf-8'), 'text/html; charset=utf-8');\n  });\n\n  it('should override charset', function () {\n    assert.strictEqual(utils.setCharset('text/html; charset=iso-8859-1', 'utf-8'), 'text/html; charset=utf-8');\n  });\n});\n\ndescribe('utils.wetag(body, encoding)', function(){\n  it('should support strings', function(){\n    assert.strictEqual(utils.wetag('express!'),\n      'W/\"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg\"')\n  })\n\n  it('should support utf8 strings', function(){\n    assert.strictEqual(utils.wetag('express❤', 'utf8'),\n      'W/\"a-JBiXf7GyzxwcrxY4hVXUwa7tmks\"')\n  })\n\n  it('should support buffer', function(){\n    assert.strictEqual(utils.wetag(Buffer.from('express!')),\n      'W/\"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg\"')\n  })\n\n  it('should support empty string', function(){\n    assert.strictEqual(utils.wetag(''),\n      'W/\"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk\"')\n  })\n})\n\ndescribe('utils.compileETag()', function () {\n  it('should return generateETag for true', function () {\n    const fn = utils.compileETag(true);\n    assert.strictEqual(fn('express!'), utils.wetag('express!'));\n  });\n\n  it('should return undefined for false', function () {\n    assert.strictEqual(utils.compileETag(false), undefined);\n  });\n\n  it('should return generateETag for string values \"strong\" and \"weak\"', function () {\n    assert.strictEqual(utils.compileETag('strong')(\"express\"), utils.etag(\"express\"));\n    assert.strictEqual(utils.compileETag('weak')(\"express\"), utils.wetag(\"express\"));\n  });\n\n  it('should throw for unknown string values', function () {\n    assert.throws(() => utils.compileETag('foo'), TypeError);\n  });\n\n  it('should throw for unsupported types like arrays and objects', function () {\n    assert.throws(() => utils.compileETag([]), TypeError);\n    assert.throws(() => utils.compileETag({}), TypeError);\n  });\n});\n"
  }
]