├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── codeql.yml │ ├── legacy.yml │ └── scorecard.yml ├── .gitignore ├── .npmrc ├── Charter.md ├── Code-Of-Conduct.md ├── Collaborator-Guide.md ├── Contributing.md ├── History.md ├── LICENSE ├── Readme-Guide.md ├── Readme.md ├── Release-Process.md ├── Security.md ├── Triager-Guide.md ├── benchmarks ├── Makefile ├── README.md ├── middleware.js └── run ├── examples ├── README.md ├── auth │ ├── index.js │ └── views │ │ ├── foot.ejs │ │ ├── head.ejs │ │ └── login.ejs ├── content-negotiation │ ├── db.js │ ├── index.js │ └── users.js ├── cookie-sessions │ └── index.js ├── cookies │ └── index.js ├── downloads │ ├── files │ │ ├── CCTV大赛上海分赛区.txt │ │ ├── amazing.txt │ │ └── notes │ │ │ └── groceries.txt │ └── index.js ├── ejs │ ├── index.js │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ └── views │ │ ├── footer.html │ │ ├── header.html │ │ └── users.html ├── error-pages │ ├── index.js │ └── views │ │ ├── 404.ejs │ │ ├── 500.ejs │ │ ├── error_header.ejs │ │ ├── footer.ejs │ │ └── index.ejs ├── error │ └── index.js ├── hello-world │ └── index.js ├── markdown │ ├── index.js │ └── views │ │ └── index.md ├── multi-router │ ├── controllers │ │ ├── api_v1.js │ │ └── api_v2.js │ └── index.js ├── mvc │ ├── controllers │ │ ├── main │ │ │ └── index.js │ │ ├── pet │ │ │ ├── index.js │ │ │ └── views │ │ │ │ ├── edit.ejs │ │ │ │ └── show.ejs │ │ ├── user-pet │ │ │ └── index.js │ │ └── user │ │ │ ├── index.js │ │ │ └── views │ │ │ ├── edit.hbs │ │ │ ├── list.hbs │ │ │ └── show.hbs │ ├── db.js │ ├── index.js │ ├── lib │ │ └── boot.js │ ├── public │ │ └── style.css │ └── views │ │ ├── 404.ejs │ │ └── 5xx.ejs ├── online │ └── index.js ├── params │ └── index.js ├── resource │ └── index.js ├── route-map │ └── index.js ├── route-middleware │ └── index.js ├── route-separation │ ├── index.js │ ├── post.js │ ├── public │ │ └── style.css │ ├── site.js │ ├── user.js │ └── views │ │ ├── footer.ejs │ │ ├── header.ejs │ │ ├── index.ejs │ │ ├── posts │ │ └── index.ejs │ │ └── users │ │ ├── edit.ejs │ │ ├── index.ejs │ │ └── view.ejs ├── search │ ├── index.js │ └── public │ │ ├── client.js │ │ └── index.html ├── session │ ├── index.js │ └── redis.js ├── static-files │ ├── index.js │ └── public │ │ ├── css │ │ └── style.css │ │ ├── hello.txt │ │ └── js │ │ └── app.js ├── vhost │ └── index.js ├── view-constructor │ ├── github-view.js │ └── index.js ├── view-locals │ ├── index.js │ ├── user.js │ └── views │ │ └── index.ejs └── web-service │ └── index.js ├── index.js ├── lib ├── application.js ├── express.js ├── request.js ├── response.js ├── utils.js └── view.js ├── package.json └── test ├── Route.js ├── Router.js ├── acceptance ├── auth.js ├── content-negotiation.js ├── cookie-sessions.js ├── cookies.js ├── downloads.js ├── ejs.js ├── error-pages.js ├── error.js ├── hello-world.js ├── markdown.js ├── multi-router.js ├── mvc.js ├── params.js ├── resource.js ├── route-map.js ├── route-separation.js ├── vhost.js └── web-service.js ├── app.all.js ├── app.engine.js ├── app.head.js ├── app.js ├── app.listen.js ├── app.locals.js ├── app.options.js ├── app.param.js ├── app.render.js ├── app.request.js ├── app.response.js ├── app.route.js ├── app.router.js ├── app.routes.error.js ├── app.use.js ├── config.js ├── exports.js ├── express.json.js ├── express.raw.js ├── express.static.js ├── express.text.js ├── express.urlencoded.js ├── fixtures ├── % of dogs.txt ├── .name ├── blog │ ├── index.html │ └── post │ │ └── index.tmpl ├── broken.send ├── default_layout │ ├── name.tmpl │ └── user.tmpl ├── email.tmpl ├── empty.txt ├── local_layout │ └── user.tmpl ├── name.tmpl ├── name.txt ├── nums.txt ├── pets │ └── names.txt ├── snow ☃ │ └── .gitkeep ├── todo.html ├── todo.txt ├── user.html ├── user.tmpl └── users │ ├── index.html │ └── tobi.txt ├── middleware.basic.js ├── regression.js ├── req.accepts.js ├── req.acceptsCharsets.js ├── req.acceptsEncodings.js ├── req.acceptsLanguages.js ├── req.baseUrl.js ├── req.fresh.js ├── req.get.js ├── req.host.js ├── req.hostname.js ├── req.ip.js ├── req.ips.js ├── req.is.js ├── req.path.js ├── req.protocol.js ├── req.query.js ├── req.range.js ├── req.route.js ├── req.secure.js ├── req.signedCookies.js ├── req.stale.js ├── req.subdomains.js ├── req.xhr.js ├── res.append.js ├── res.attachment.js ├── res.clearCookie.js ├── res.cookie.js ├── res.download.js ├── res.format.js ├── res.get.js ├── res.json.js ├── res.jsonp.js ├── res.links.js ├── res.locals.js ├── res.location.js ├── res.redirect.js ├── res.render.js ├── res.send.js ├── res.sendFile.js ├── res.sendStatus.js ├── res.set.js ├── res.status.js ├── res.type.js ├── res.vary.js ├── support ├── env.js ├── tmpl.js └── utils.js └── utils.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [{*.js,*.json,*.yml}] 10 | indent_size = 2 11 | indent_style = space 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | env: 3 | es2022: true 4 | node: true 5 | rules: 6 | eol-last: error 7 | eqeqeq: [error, allow-null] 8 | indent: [error, 2, { MemberExpression: "off", SwitchCase: 1 }] 9 | no-trailing-spaces: error 10 | no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }] 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | 8 | - package-ecosystem: npm 9 | directory: / 10 | schedule: 11 | interval: monthly 12 | time: "23:00" 13 | timezone: Europe/London 14 | open-pull-requests-limit: 10 15 | ignore: 16 | - dependency-name: "*" 17 | update-types: ["version-update:semver-major"] -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: ["master"] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: ["master"] 20 | schedule: 21 | - cron: "0 0 * * 1" 22 | workflow_dispatch: 23 | 24 | permissions: 25 | contents: read 26 | 27 | jobs: 28 | analyze: 29 | name: Analyze 30 | runs-on: ubuntu-latest 31 | permissions: 32 | actions: read 33 | contents: read 34 | security-events: write 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | language: [javascript, actions] 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 43 | with: 44 | persist-credentials: false 45 | 46 | # Initializes the CodeQL tools for scanning. 47 | - name: Initialize CodeQL 48 | uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 49 | with: 50 | languages: ${{ matrix.language }} 51 | config: | 52 | paths-ignore: 53 | - test 54 | # If you wish to specify custom queries, you can do so here or in a config file. 55 | # By default, queries listed here will override any specified in a config file. 56 | # Prefix the list here with "+" to use these queries and those in the config file. 57 | 58 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 59 | # If this step fails, then you should remove it and run the build manually (see below) 60 | # - name: Autobuild 61 | # uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 62 | 63 | # ℹ️ Command-line programs to run using the OS shell. 64 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 65 | 66 | # If the Autobuild fails above, remove it and uncomment the following three lines. 67 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 68 | 69 | # - run: | 70 | # echo "Run, Build Application using script" 71 | # ./location_of_script_within_repo/buildscript.sh 72 | 73 | - name: Perform CodeQL Analysis 74 | uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 75 | -------------------------------------------------------------------------------- /.github/workflows/legacy.yml: -------------------------------------------------------------------------------- 1 | name: legacy 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - develop 8 | - '4.x' 9 | - '5.x' 10 | - '5.0' 11 | paths-ignore: 12 | - '*.md' 13 | pull_request: 14 | paths-ignore: 15 | - '*.md' 16 | workflow_dispatch: 17 | 18 | permissions: 19 | contents: read 20 | 21 | # Cancel in progress workflows 22 | # in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run 23 | concurrency: 24 | group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" 25 | cancel-in-progress: true 26 | 27 | jobs: 28 | test: 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | os: [ubuntu-latest, windows-latest] 33 | node-version: [16, 17] 34 | # Node.js release schedule: https://nodejs.org/en/about/releases/ 35 | 36 | name: Node.js ${{ matrix.node-version }} - ${{matrix.os}} 37 | 38 | runs-on: ${{ matrix.os }} 39 | steps: 40 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 41 | with: 42 | persist-credentials: false 43 | 44 | - name: Setup Node.js ${{ matrix.node-version }} 45 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 46 | with: 47 | node-version: ${{ matrix.node-version }} 48 | 49 | - name: Configure npm loglevel 50 | run: | 51 | npm config set loglevel error 52 | shell: bash 53 | 54 | - name: Install dependencies 55 | run: npm install 56 | 57 | - name: Output Node and NPM versions 58 | run: | 59 | echo "Node.js version: $(node -v)" 60 | echo "NPM version: $(npm -v)" 61 | 62 | - name: Run tests 63 | shell: bash 64 | run: npm run test-ci 65 | 66 | - name: Upload code coverage 67 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 68 | with: 69 | name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }} 70 | path: ./coverage/lcov.info 71 | retention-days: 1 72 | 73 | coverage: 74 | needs: test 75 | runs-on: ubuntu-latest 76 | permissions: 77 | contents: read 78 | checks: write 79 | steps: 80 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 81 | with: 82 | persist-credentials: false 83 | 84 | - name: Install lcov 85 | shell: bash 86 | run: sudo apt-get -y install lcov 87 | 88 | - name: Collect coverage reports 89 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 90 | with: 91 | path: ./coverage 92 | pattern: coverage-node-* 93 | 94 | - name: Merge coverage reports 95 | shell: bash 96 | run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info 97 | 98 | - name: Upload coverage report 99 | uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6 100 | with: 101 | file: ./lcov.info 102 | -------------------------------------------------------------------------------- /.github/workflows/scorecard.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecard supply-chain security 6 | on: 7 | # For Branch-Protection check. Only the default branch is supported. See 8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 9 | branch_protection_rule: 10 | # To guarantee Maintained check is occasionally updated. See 11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 12 | schedule: 13 | - cron: '16 21 * * 1' 14 | push: 15 | branches: [ "master" ] 16 | 17 | # Declare default permissions as read only. 18 | permissions: read-all 19 | 20 | jobs: 21 | analysis: 22 | name: Scorecard analysis 23 | runs-on: ubuntu-latest 24 | permissions: 25 | # Needed to upload the results to code-scanning dashboard. 26 | security-events: write 27 | # Needed to publish results and get a badge (see publish_results below). 28 | id-token: write 29 | # Uncomment the permissions below if installing in a private repository. 30 | # contents: read 31 | # actions: read 32 | 33 | steps: 34 | - name: "Checkout code" 35 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 36 | with: 37 | persist-credentials: false 38 | 39 | - name: "Run analysis" 40 | uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 41 | with: 42 | results_file: results.sarif 43 | results_format: sarif 44 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 45 | # - you want to enable the Branch-Protection check on a *public* repository, or 46 | # - you are installing Scorecard on a *private* repository 47 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 48 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 49 | 50 | # Public repositories: 51 | # - Publish results to OpenSSF REST API for easy access by consumers 52 | # - Allows the repository to include the Scorecard badge. 53 | # - See https://github.com/ossf/scorecard-action#publishing-results. 54 | # For private repositories: 55 | # - `publish_results` will always be set to `false`, regardless 56 | # of the value entered here. 57 | publish_results: true 58 | 59 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 60 | # format to the repository Actions tab. 61 | - name: "Upload artifact" 62 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 63 | with: 64 | name: SARIF file 65 | path: results.sarif 66 | retention-days: 5 67 | 68 | # Upload the results to GitHub's code scanning dashboard. 69 | - name: "Upload to code-scanning" 70 | uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 71 | with: 72 | sarif_file: results.sarif 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # npm 2 | node_modules 3 | package-lock.json 4 | npm-shrinkwrap.json 5 | *.log 6 | *.gz 7 | 8 | # Coveralls 9 | .nyc_output 10 | coverage 11 | 12 | # Benchmarking 13 | benchmarks/graphs 14 | 15 | # ignore additional files using core.excludesFile 16 | # https://git-scm.com/docs/gitignore 17 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /Collaborator-Guide.md: -------------------------------------------------------------------------------- 1 | # Express Collaborator Guide 2 | 3 | ## Website Issues 4 | 5 | Open issues for the expressjs.com website in https://github.com/expressjs/expressjs.com. 6 | 7 | ## PRs and Code contributions 8 | 9 | * Tests must pass. 10 | * Follow the [JavaScript Standard Style](https://standardjs.com/) and `npm run lint`. 11 | * If you fix a bug, add a test. 12 | 13 | ## Branches 14 | 15 | Use the `master` branch for bug fixes or minor work that is intended for the 16 | current release stream. 17 | 18 | Use the correspondingly named branch, e.g. `5.0`, for anything intended for 19 | a future release of Express. 20 | 21 | ## Steps for contributing 22 | 23 | 1. [Create an issue](https://github.com/expressjs/express/issues/new) for the 24 | bug you want to fix or the feature that you want to add. 25 | 2. Create your own [fork](https://github.com/expressjs/express) on GitHub, then 26 | checkout your fork. 27 | 3. Write your code in your local copy. It's good practice to create a branch for 28 | each new issue you work on, although not compulsory. 29 | 4. To run the test suite, first install the dependencies by running `npm install`, 30 | then run `npm test`. 31 | 5. Ensure your code is linted by running `npm run lint` -- fix any issue you 32 | see listed. 33 | 6. If the tests pass, you can commit your changes to your fork and then create 34 | a pull request from there. Make sure to reference your issue from the pull 35 | request comments by including the issue number e.g. `#123`. 36 | 37 | ## Issues which are questions 38 | 39 | We will typically close any vague issues or questions that are specific to some 40 | app you are writing. Please double check the docs and other references before 41 | being trigger happy with posting a question issue. 42 | 43 | Things that will help get your question issue looked at: 44 | 45 | * Full and runnable JS code. 46 | * Clear description of the problem or unexpected behavior. 47 | * Clear description of the expected result. 48 | * Steps you have taken to debug it yourself. 49 | 50 | If you post a question and do not outline the above items or make it easy for 51 | us to understand and reproduce your issue, it will be closed. 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2009-2014 TJ Holowaychuk 4 | Copyright (c) 2013-2014 Roman Shtylman 5 | Copyright (c) 2014-2015 Douglas Christopher Wilson 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | 'Software'), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /Security.md: -------------------------------------------------------------------------------- 1 | # Security Policies and Procedures 2 | 3 | This document outlines security procedures and general policies for the Express 4 | project. 5 | 6 | * [Reporting a Bug](#reporting-a-bug) 7 | * [Disclosure Policy](#disclosure-policy) 8 | * [Comments on this Policy](#comments-on-this-policy) 9 | 10 | ## Reporting a Bug 11 | 12 | The Express team and community take all security bugs in Express seriously. 13 | Thank you for improving the security of Express. We appreciate your efforts and 14 | responsible disclosure and will make every effort to acknowledge your 15 | contributions. 16 | 17 | Report security bugs by emailing `express-security@lists.openjsf.org`. 18 | 19 | To ensure the timely response to your report, please ensure that the entirety 20 | of the report is contained within the email body and not solely behind a web 21 | link or an attachment. 22 | 23 | The lead maintainer will acknowledge your email within 48 hours, and will send a 24 | more detailed response within 48 hours indicating the next steps in handling 25 | your report. After the initial reply to your report, the security team will 26 | endeavor to keep you informed of the progress towards a fix and full 27 | announcement, and may ask for additional information or guidance. 28 | 29 | Report security bugs in third-party modules to the person or team maintaining 30 | the module. 31 | 32 | ## Pre-release Versions 33 | 34 | Alpha and Beta releases are unstable and **not suitable for production use**. 35 | Vulnerabilities found in pre-releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section. 36 | Due to the unstable nature of the branch it is not guaranteed that any fixes will be released in the next pre-release. 37 | 38 | ## Disclosure Policy 39 | 40 | When the security team receives a security bug report, they will assign it to a 41 | primary handler. This person will coordinate the fix and release process, 42 | involving the following steps: 43 | 44 | * Confirm the problem and determine the affected versions. 45 | * Audit code to find any potential similar problems. 46 | * Prepare fixes for all releases still under maintenance. These fixes will be 47 | released as fast as possible to npm. 48 | 49 | ## The Express Threat Model 50 | 51 | We are currently working on a new version of the security model, the most updated version can be found [here](https://github.com/expressjs/security-wg/blob/main/docs/ThreatModel.md) 52 | 53 | ## Comments on this Policy 54 | 55 | If you have suggestions on how this process could be improved please submit a 56 | pull request. 57 | -------------------------------------------------------------------------------- /benchmarks/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | @./run 1 middleware 50 4 | @./run 5 middleware 50 5 | @./run 10 middleware 50 6 | @./run 15 middleware 50 7 | @./run 20 middleware 50 8 | @./run 30 middleware 50 9 | @./run 50 middleware 50 10 | @./run 100 middleware 50 11 | @./run 10 middleware 100 12 | @./run 10 middleware 250 13 | @./run 10 middleware 500 14 | @./run 10 middleware 1000 15 | @echo 16 | 17 | .PHONY: all 18 | -------------------------------------------------------------------------------- /benchmarks/README.md: -------------------------------------------------------------------------------- 1 | # Express Benchmarks 2 | 3 | ## Installation 4 | 5 | You will need to install [wrk](https://github.com/wg/wrk/blob/master/INSTALL) in order to run the benchmarks. 6 | 7 | ## Running 8 | 9 | To run the benchmarks, first install the dependencies `npm i`, then run `make` 10 | 11 | The output will look something like this: 12 | 13 | ``` 14 | 50 connections 15 | 1 middleware 16 | 7.15ms 17 | 6784.01 18 | 19 | [...redacted...] 20 | 21 | 1000 connections 22 | 10 middleware 23 | 139.21ms 24 | 6155.19 25 | 26 | ``` 27 | 28 | ### Tip: Include Node.js version in output 29 | 30 | You can use `make && node -v` to include the node.js version in the output. 31 | 32 | ### Tip: Save the results to a file 33 | 34 | You can use `make > results.log` to save the results to a file `results.log`. 35 | -------------------------------------------------------------------------------- /benchmarks/middleware.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('..'); 3 | var app = express(); 4 | 5 | // number of middleware 6 | 7 | var n = parseInt(process.env.MW || '1', 10); 8 | console.log(' %s middleware', n); 9 | 10 | while (n--) { 11 | app.use(function(req, res, next){ 12 | next(); 13 | }); 14 | } 15 | 16 | app.use(function(req, res){ 17 | res.send('Hello World') 18 | }); 19 | 20 | app.listen(3333); 21 | -------------------------------------------------------------------------------- /benchmarks/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo 4 | MW=$1 node $2 & 5 | pid=$! 6 | 7 | echo " $3 connections" 8 | 9 | sleep 2 10 | 11 | wrk 'http://localhost:3333/?foo[bar]=baz' \ 12 | -d 3 \ 13 | -c $3 \ 14 | -t 8 \ 15 | | grep 'Requests/sec\|Latency' \ 16 | | awk '{ print " " $2 }' 17 | 18 | kill $pid 19 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Express examples 2 | 3 | This page contains list of examples using Express. 4 | 5 | - [auth](./auth) - Authentication with login and password 6 | - [content-negotiation](./content-negotiation) - HTTP content negotiation 7 | - [cookie-sessions](./cookie-sessions) - Working with cookie-based sessions 8 | - [cookies](./cookies) - Working with cookies 9 | - [downloads](./downloads) - Transferring files to client 10 | - [ejs](./ejs) - Working with Embedded JavaScript templating (ejs) 11 | - [error-pages](./error-pages) - Creating error pages 12 | - [error](./error) - Working with error middleware 13 | - [hello-world](./hello-world) - Simple request handler 14 | - [markdown](./markdown) - Markdown as template engine 15 | - [multi-router](./multi-router) - Working with multiple Express routers 16 | - [mvc](./mvc) - MVC-style controllers 17 | - [online](./online) - Tracking online user activity with `online` and `redis` packages 18 | - [params](./params) - Working with route parameters 19 | - [resource](./resource) - Multiple HTTP operations on the same resource 20 | - [route-map](./route-map) - Organizing routes using a map 21 | - [route-middleware](./route-middleware) - Working with route middleware 22 | - [route-separation](./route-separation) - Organizing routes per each resource 23 | - [search](./search) - Search API 24 | - [session](./session) - User sessions 25 | - [static-files](./static-files) - Serving static files 26 | - [vhost](./vhost) - Working with virtual hosts 27 | - [view-constructor](./view-constructor) - Rendering views dynamically 28 | - [view-locals](./view-locals) - Saving data in request object between middleware calls 29 | - [web-service](./web-service) - Simple API service 30 | -------------------------------------------------------------------------------- /examples/auth/views/foot.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/auth/views/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/auth/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | <%- include('head', { title: 'Authentication Example' }) -%> 3 | 4 |

Login

5 | <%- message %> 6 | Try accessing /restricted, then authenticate with "tj" and "foobar". 7 |
8 |

9 | 10 | 11 |

12 |

13 | 14 | 15 |

16 |

17 | 18 |

19 |
20 | 21 | <%- include('foot') -%> 22 | -------------------------------------------------------------------------------- /examples/content-negotiation/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var users = []; 4 | 5 | users.push({ name: 'Tobi' }); 6 | users.push({ name: 'Loki' }); 7 | users.push({ name: 'Jane' }); 8 | 9 | module.exports = users; 10 | -------------------------------------------------------------------------------- /examples/content-negotiation/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../../'); 4 | var app = module.exports = express(); 5 | var users = require('./db'); 6 | 7 | // so either you can deal with different types of formatting 8 | // for expected response in index.js 9 | app.get('/', function(req, res){ 10 | res.format({ 11 | html: function(){ 12 | res.send('
    ' + users.map(function(user){ 13 | return '
  • ' + user.name + '
  • '; 14 | }).join('') + '
'); 15 | }, 16 | 17 | text: function(){ 18 | res.send(users.map(function(user){ 19 | return ' - ' + user.name + '\n'; 20 | }).join('')); 21 | }, 22 | 23 | json: function(){ 24 | res.json(users); 25 | } 26 | }); 27 | }); 28 | 29 | // or you could write a tiny middleware like 30 | // this to add a layer of abstraction 31 | // and make things a bit more declarative: 32 | 33 | function format(path) { 34 | var obj = require(path); 35 | return function(req, res){ 36 | res.format(obj); 37 | }; 38 | } 39 | 40 | app.get('/users', format('./users')); 41 | 42 | /* istanbul ignore next */ 43 | if (!module.parent) { 44 | app.listen(3000); 45 | console.log('Express started on port 3000'); 46 | } 47 | -------------------------------------------------------------------------------- /examples/content-negotiation/users.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var users = require('./db'); 4 | 5 | exports.html = function(req, res){ 6 | res.send('
    ' + users.map(function(user){ 7 | return '
  • ' + user.name + '
  • '; 8 | }).join('') + '
'); 9 | }; 10 | 11 | exports.text = function(req, res){ 12 | res.send(users.map(function(user){ 13 | return ' - ' + user.name + '\n'; 14 | }).join('')); 15 | }; 16 | 17 | exports.json = function(req, res){ 18 | res.json(users); 19 | }; 20 | -------------------------------------------------------------------------------- /examples/cookie-sessions/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var cookieSession = require('cookie-session'); 8 | var express = require('../../'); 9 | 10 | var app = module.exports = express(); 11 | 12 | // add req.session cookie support 13 | app.use(cookieSession({ secret: 'manny is cool' })); 14 | 15 | // do something with the session 16 | app.get('/', function (req, res) { 17 | req.session.count = (req.session.count || 0) + 1 18 | res.send('viewed ' + req.session.count + ' times\n') 19 | }) 20 | 21 | /* istanbul ignore next */ 22 | if (!module.parent) { 23 | app.listen(3000); 24 | console.log('Express started on port 3000'); 25 | } 26 | -------------------------------------------------------------------------------- /examples/cookies/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var app = module.exports = express(); 9 | var logger = require('morgan'); 10 | var cookieParser = require('cookie-parser'); 11 | 12 | // custom log format 13 | if (process.env.NODE_ENV !== 'test') app.use(logger(':method :url')) 14 | 15 | // parses request cookies, populating 16 | // req.cookies and req.signedCookies 17 | // when the secret is passed, used 18 | // for signing the cookies. 19 | app.use(cookieParser('my secret here')); 20 | 21 | // parses x-www-form-urlencoded 22 | app.use(express.urlencoded()) 23 | 24 | app.get('/', function(req, res){ 25 | if (req.cookies.remember) { 26 | res.send('Remembered :). Click to forget!.'); 27 | } else { 28 | res.send('

Check to ' 30 | + '.

'); 31 | } 32 | }); 33 | 34 | app.get('/forget', function(req, res){ 35 | res.clearCookie('remember'); 36 | res.redirect(req.get('Referrer') || '/'); 37 | }); 38 | 39 | app.post('/', function(req, res){ 40 | var minute = 60000; 41 | 42 | if (req.body && req.body.remember) { 43 | res.cookie('remember', 1, { maxAge: minute }) 44 | } 45 | 46 | res.redirect(req.get('Referrer') || '/'); 47 | }); 48 | 49 | /* istanbul ignore next */ 50 | if (!module.parent) { 51 | app.listen(3000); 52 | console.log('Express started on port 3000'); 53 | } 54 | -------------------------------------------------------------------------------- /examples/downloads/files/CCTV大赛上海分赛区.txt: -------------------------------------------------------------------------------- 1 | Only for test. 2 | The file name is faked. -------------------------------------------------------------------------------- /examples/downloads/files/amazing.txt: -------------------------------------------------------------------------------- 1 | what an amazing download -------------------------------------------------------------------------------- /examples/downloads/files/notes/groceries.txt: -------------------------------------------------------------------------------- 1 | * milk 2 | * eggs 3 | * bread 4 | -------------------------------------------------------------------------------- /examples/downloads/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var path = require('node:path'); 9 | 10 | var app = module.exports = express(); 11 | 12 | // path to where the files are stored on disk 13 | var FILES_DIR = path.join(__dirname, 'files') 14 | 15 | app.get('/', function(req, res){ 16 | res.send('') 22 | }); 23 | 24 | // /files/* is accessed via req.params[0] 25 | // but here we name it :file 26 | app.get('/files/*file', function (req, res, next) { 27 | res.download(req.params.file.join('/'), { root: FILES_DIR }, function (err) { 28 | if (!err) return; // file sent 29 | if (err.status !== 404) return next(err); // non-404 error 30 | // file for download not found 31 | res.statusCode = 404; 32 | res.send('Cant find that file, sorry!'); 33 | }); 34 | }); 35 | 36 | /* istanbul ignore next */ 37 | if (!module.parent) { 38 | app.listen(3000); 39 | console.log('Express started on port 3000'); 40 | } 41 | -------------------------------------------------------------------------------- /examples/ejs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var path = require('node:path'); 9 | 10 | var app = module.exports = express(); 11 | 12 | // Register ejs as .html. If we did 13 | // not call this, we would need to 14 | // name our views foo.ejs instead 15 | // of foo.html. The __express method 16 | // is simply a function that engines 17 | // use to hook into the Express view 18 | // system by default, so if we want 19 | // to change "foo.ejs" to "foo.html" 20 | // we simply pass _any_ function, in this 21 | // case `ejs.__express`. 22 | 23 | app.engine('.html', require('ejs').__express); 24 | 25 | // Optional since express defaults to CWD/views 26 | 27 | app.set('views', path.join(__dirname, 'views')); 28 | 29 | // Path to our public directory 30 | 31 | app.use(express.static(path.join(__dirname, 'public'))); 32 | 33 | // Without this you would need to 34 | // supply the extension to res.render() 35 | // ex: res.render('users.html'). 36 | app.set('view engine', 'html'); 37 | 38 | // Dummy users 39 | var users = [ 40 | { name: 'tobi', email: 'tobi@learnboost.com' }, 41 | { name: 'loki', email: 'loki@learnboost.com' }, 42 | { name: 'jane', email: 'jane@learnboost.com' } 43 | ]; 44 | 45 | app.get('/', function(req, res){ 46 | res.render('users', { 47 | users: users, 48 | title: "EJS example", 49 | header: "Some users" 50 | }); 51 | }); 52 | 53 | /* istanbul ignore next */ 54 | if (!module.parent) { 55 | app.listen(3000); 56 | console.log('Express started on port 3000'); 57 | } 58 | -------------------------------------------------------------------------------- /examples/ejs/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px 80px; 3 | font: 14px "Helvetica Neue", "Lucida Grande", Arial, sans-serif; 4 | } 5 | -------------------------------------------------------------------------------- /examples/ejs/views/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/ejs/views/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/ejs/views/users.html: -------------------------------------------------------------------------------- 1 | <%- include('header.html') -%> 2 | 3 |

Users

4 |
    5 | <% users.forEach(function(user){ %> 6 |
  • <%= user.name %> <<%= user.email %>>
  • 7 | <% }) %> 8 |
9 | 10 | <%- include('footer.html') -%> 11 | -------------------------------------------------------------------------------- /examples/error-pages/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var path = require('node:path'); 9 | var app = module.exports = express(); 10 | var logger = require('morgan'); 11 | var silent = process.env.NODE_ENV === 'test' 12 | 13 | // general config 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'ejs'); 16 | 17 | // our custom "verbose errors" setting 18 | // which we can use in the templates 19 | // via settings['verbose errors'] 20 | app.enable('verbose errors'); 21 | 22 | // disable them in production 23 | // use $ NODE_ENV=production node examples/error-pages 24 | if (app.settings.env === 'production') app.disable('verbose errors') 25 | 26 | silent || app.use(logger('dev')); 27 | 28 | // Routes 29 | 30 | app.get('/', function(req, res){ 31 | res.render('index.ejs'); 32 | }); 33 | 34 | app.get('/404', function(req, res, next){ 35 | // trigger a 404 since no other middleware 36 | // will match /404 after this one, and we're not 37 | // responding here 38 | next(); 39 | }); 40 | 41 | app.get('/403', function(req, res, next){ 42 | // trigger a 403 error 43 | var err = new Error('not allowed!'); 44 | err.status = 403; 45 | next(err); 46 | }); 47 | 48 | app.get('/500', function(req, res, next){ 49 | // trigger a generic (500) error 50 | next(new Error('keyboard cat!')); 51 | }); 52 | 53 | // Error handlers 54 | 55 | // Since this is the last non-error-handling 56 | // middleware use()d, we assume 404, as nothing else 57 | // responded. 58 | 59 | // $ curl http://localhost:3000/notfound 60 | // $ curl http://localhost:3000/notfound -H "Accept: application/json" 61 | // $ curl http://localhost:3000/notfound -H "Accept: text/plain" 62 | 63 | app.use(function(req, res, next){ 64 | res.status(404); 65 | 66 | res.format({ 67 | html: function () { 68 | res.render('404', { url: req.url }) 69 | }, 70 | json: function () { 71 | res.json({ error: 'Not found' }) 72 | }, 73 | default: function () { 74 | res.type('txt').send('Not found') 75 | } 76 | }) 77 | }); 78 | 79 | // error-handling middleware, take the same form 80 | // as regular middleware, however they require an 81 | // arity of 4, aka the signature (err, req, res, next). 82 | // when connect has an error, it will invoke ONLY error-handling 83 | // middleware. 84 | 85 | // If we were to next() here any remaining non-error-handling 86 | // middleware would then be executed, or if we next(err) to 87 | // continue passing the error, only error-handling middleware 88 | // would remain being executed, however here 89 | // we simply respond with an error page. 90 | 91 | app.use(function(err, req, res, next){ 92 | // we may use properties of the error object 93 | // here and next(err) appropriately, or if 94 | // we possibly recovered from the error, simply next(). 95 | res.status(err.status || 500); 96 | res.render('500', { error: err }); 97 | }); 98 | 99 | /* istanbul ignore next */ 100 | if (!module.parent) { 101 | app.listen(3000); 102 | console.log('Express started on port 3000'); 103 | } 104 | -------------------------------------------------------------------------------- /examples/error-pages/views/404.ejs: -------------------------------------------------------------------------------- 1 | <%- include('error_header') -%> 2 |

Cannot find <%= url %>

3 | <%- include('footer') -%> 4 | -------------------------------------------------------------------------------- /examples/error-pages/views/500.ejs: -------------------------------------------------------------------------------- 1 | <%- include('error_header') -%> 2 |

Error: <%= error.message %>

3 | <% if (settings['verbose errors']) { %> 4 |
<%= error.stack %>
5 | <% } else { %> 6 |

An error occurred!

7 | <% } %> 8 | <%- include('footer') -%> 9 | -------------------------------------------------------------------------------- /examples/error-pages/views/error_header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Error 7 | 8 | 9 | 10 |

An error occurred!

11 | -------------------------------------------------------------------------------- /examples/error-pages/views/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/error-pages/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Custom Pages Example 7 | 8 | 9 | 10 |

My Site

11 |

Pages Example

12 | 13 |
    14 |
  • visit 500
  • 15 |
  • visit 404
  • 16 |
  • visit 403
  • 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/error/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var logger = require('morgan'); 9 | var app = module.exports = express(); 10 | var test = app.get('env') === 'test' 11 | 12 | if (!test) app.use(logger('dev')); 13 | 14 | // error handling middleware have an arity of 4 15 | // instead of the typical (req, res, next), 16 | // otherwise they behave exactly like regular 17 | // middleware, you may have several of them, 18 | // in different orders etc. 19 | 20 | function error(err, req, res, next) { 21 | // log it 22 | if (!test) console.error(err.stack); 23 | 24 | // respond with 500 "Internal Server Error". 25 | res.status(500); 26 | res.send('Internal Server Error'); 27 | } 28 | 29 | app.get('/', function () { 30 | // Caught and passed down to the errorHandler middleware 31 | throw new Error('something broke!'); 32 | }); 33 | 34 | app.get('/next', function(req, res, next){ 35 | // We can also pass exceptions to next() 36 | // The reason for process.nextTick() is to show that 37 | // next() can be called inside an async operation, 38 | // in real life it can be a DB read or HTTP request. 39 | process.nextTick(function(){ 40 | next(new Error('oh no!')); 41 | }); 42 | }); 43 | 44 | // the error handler is placed after routes 45 | // if it were above it would not receive errors 46 | // from app.get() etc 47 | app.use(error); 48 | 49 | /* istanbul ignore next */ 50 | if (!module.parent) { 51 | app.listen(3000); 52 | console.log('Express started on port 3000'); 53 | } 54 | -------------------------------------------------------------------------------- /examples/hello-world/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../../'); 4 | 5 | var app = module.exports = express() 6 | 7 | app.get('/', function(req, res){ 8 | res.send('Hello World'); 9 | }); 10 | 11 | /* istanbul ignore next */ 12 | if (!module.parent) { 13 | app.listen(3000); 14 | console.log('Express started on port 3000'); 15 | } 16 | -------------------------------------------------------------------------------- /examples/markdown/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var escapeHtml = require('escape-html'); 8 | var express = require('../..'); 9 | var fs = require('node:fs'); 10 | var marked = require('marked'); 11 | var path = require('node:path'); 12 | 13 | var app = module.exports = express(); 14 | 15 | // register .md as an engine in express view system 16 | 17 | app.engine('md', function(path, options, fn){ 18 | fs.readFile(path, 'utf8', function(err, str){ 19 | if (err) return fn(err); 20 | var html = marked.parse(str).replace(/\{([^}]+)\}/g, function(_, name){ 21 | return escapeHtml(options[name] || ''); 22 | }); 23 | fn(null, html); 24 | }); 25 | }); 26 | 27 | app.set('views', path.join(__dirname, 'views')); 28 | 29 | // make it the default, so we don't need .md 30 | app.set('view engine', 'md'); 31 | 32 | app.get('/', function(req, res){ 33 | res.render('index', { title: 'Markdown Example' }); 34 | }); 35 | 36 | app.get('/fail', function(req, res){ 37 | res.render('missing', { title: 'Markdown Example' }); 38 | }); 39 | 40 | /* istanbul ignore next */ 41 | if (!module.parent) { 42 | app.listen(3000); 43 | console.log('Express started on port 3000'); 44 | } 45 | -------------------------------------------------------------------------------- /examples/markdown/views/index.md: -------------------------------------------------------------------------------- 1 | 2 | # {title} 3 | 4 | Just an example view rendered with _markdown_. -------------------------------------------------------------------------------- /examples/multi-router/controllers/api_v1.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../../..'); 4 | 5 | var apiv1 = express.Router(); 6 | 7 | apiv1.get('/', function(req, res) { 8 | res.send('Hello from APIv1 root route.'); 9 | }); 10 | 11 | apiv1.get('/users', function(req, res) { 12 | res.send('List of APIv1 users.'); 13 | }); 14 | 15 | module.exports = apiv1; 16 | -------------------------------------------------------------------------------- /examples/multi-router/controllers/api_v2.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../../..'); 4 | 5 | var apiv2 = express.Router(); 6 | 7 | apiv2.get('/', function(req, res) { 8 | res.send('Hello from APIv2 root route.'); 9 | }); 10 | 11 | apiv2.get('/users', function(req, res) { 12 | res.send('List of APIv2 users.'); 13 | }); 14 | 15 | module.exports = apiv2; 16 | -------------------------------------------------------------------------------- /examples/multi-router/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../..'); 4 | 5 | var app = module.exports = express(); 6 | 7 | app.use('/api/v1', require('./controllers/api_v1')); 8 | app.use('/api/v2', require('./controllers/api_v2')); 9 | 10 | app.get('/', function(req, res) { 11 | res.send('Hello from root route.') 12 | }); 13 | 14 | /* istanbul ignore next */ 15 | if (!module.parent) { 16 | app.listen(3000); 17 | console.log('Express started on port 3000'); 18 | } 19 | -------------------------------------------------------------------------------- /examples/mvc/controllers/main/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | exports.index = function(req, res){ 4 | res.redirect('/users'); 5 | }; 6 | -------------------------------------------------------------------------------- /examples/mvc/controllers/pet/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var db = require('../../db'); 8 | 9 | exports.engine = 'ejs'; 10 | 11 | exports.before = function(req, res, next){ 12 | var pet = db.pets[req.params.pet_id]; 13 | if (!pet) return next('route'); 14 | req.pet = pet; 15 | next(); 16 | }; 17 | 18 | exports.show = function(req, res, next){ 19 | res.render('show', { pet: req.pet }); 20 | }; 21 | 22 | exports.edit = function(req, res, next){ 23 | res.render('edit', { pet: req.pet }); 24 | }; 25 | 26 | exports.update = function(req, res, next){ 27 | var body = req.body; 28 | req.pet.name = body.pet.name; 29 | res.message('Information updated!'); 30 | res.redirect('/pet/' + req.pet.id); 31 | }; 32 | -------------------------------------------------------------------------------- /examples/mvc/controllers/pet/views/edit.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Edit <%= pet.name %> 8 | 9 | 10 | 11 |

<%= pet.name %>

12 |
13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/mvc/controllers/pet/views/show.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= pet.name %> 8 | 9 | 10 | 11 |

<%= pet.name %> edit

12 | 13 |

You are viewing <%= pet.name %>

14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user-pet/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var db = require('../../db'); 8 | 9 | exports.name = 'pet'; 10 | exports.prefix = '/user/:user_id'; 11 | 12 | exports.create = function(req, res, next){ 13 | var id = req.params.user_id; 14 | var user = db.users[id]; 15 | var body = req.body; 16 | if (!user) return next('route'); 17 | var pet = { name: body.pet.name }; 18 | pet.id = db.pets.push(pet) - 1; 19 | user.pets.push(pet); 20 | res.message('Added pet ' + body.pet.name); 21 | res.redirect('/user/' + id); 22 | }; 23 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var db = require('../../db'); 8 | 9 | exports.engine = 'hbs'; 10 | 11 | exports.before = function(req, res, next){ 12 | var id = req.params.user_id; 13 | if (!id) return next(); 14 | // pretend to query a database... 15 | process.nextTick(function(){ 16 | req.user = db.users[id]; 17 | // cant find that user 18 | if (!req.user) return next('route'); 19 | // found it, move on to the routes 20 | next(); 21 | }); 22 | }; 23 | 24 | exports.list = function(req, res, next){ 25 | res.render('list', { users: db.users }); 26 | }; 27 | 28 | exports.edit = function(req, res, next){ 29 | res.render('edit', { user: req.user }); 30 | }; 31 | 32 | exports.show = function(req, res, next){ 33 | res.render('show', { user: req.user }); 34 | }; 35 | 36 | exports.update = function(req, res, next){ 37 | var body = req.body; 38 | req.user.name = body.user.name; 39 | res.message('Information updated!'); 40 | res.redirect('/user/' + req.user.id); 41 | }; 42 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/edit.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Edit {{user.name}} 8 | 9 | 10 |

{{user.name}}

11 |
12 | 15 | 16 | 17 |
18 | 19 |
20 | 23 | 24 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/list.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Users 8 | 9 | 10 |

Users

11 |

Click a user below to view their pets.

12 |
    13 | {{#each users}} 14 |
  • {{name}}
  • 15 | {{/each}} 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/show.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{user.name}} 8 | 9 | 10 |

{{user.name}} edit

11 | 12 | {{#if hasMessages}} 13 |
    14 | {{#each messages}} 15 |
  • {{this}}
  • 16 | {{/each}} 17 |
18 | {{/if}} 19 | 20 | {{#if user.pets.length}} 21 |

View {{user.name}}'s pets:

22 |
    23 | {{#each user.pets}} 24 |
  • {{name}}
  • 25 | {{/each}} 26 |
27 | {{else}} 28 |

No pets!

29 | {{/if}} 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/mvc/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // faux database 4 | 5 | var pets = exports.pets = []; 6 | 7 | pets.push({ name: 'Tobi', id: 0 }); 8 | pets.push({ name: 'Loki', id: 1 }); 9 | pets.push({ name: 'Jane', id: 2 }); 10 | pets.push({ name: 'Raul', id: 3 }); 11 | 12 | var users = exports.users = []; 13 | 14 | users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 }); 15 | users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 }); 16 | users.push({ name: 'Nathan', pets: [], id: 2 }); 17 | -------------------------------------------------------------------------------- /examples/mvc/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var logger = require('morgan'); 9 | var path = require('node:path'); 10 | var session = require('express-session'); 11 | var methodOverride = require('method-override'); 12 | 13 | var app = module.exports = express(); 14 | 15 | // set our default template engine to "ejs" 16 | // which prevents the need for using file extensions 17 | app.set('view engine', 'ejs'); 18 | 19 | // set views for error and 404 pages 20 | app.set('views', path.join(__dirname, 'views')); 21 | 22 | // define a custom res.message() method 23 | // which stores messages in the session 24 | app.response.message = function(msg){ 25 | // reference `req.session` via the `this.req` reference 26 | var sess = this.req.session; 27 | // simply add the msg to an array for later 28 | sess.messages = sess.messages || []; 29 | sess.messages.push(msg); 30 | return this; 31 | }; 32 | 33 | // log 34 | if (!module.parent) app.use(logger('dev')); 35 | 36 | // serve static files 37 | app.use(express.static(path.join(__dirname, 'public'))); 38 | 39 | // session support 40 | app.use(session({ 41 | resave: false, // don't save session if unmodified 42 | saveUninitialized: false, // don't create session until something stored 43 | secret: 'some secret here' 44 | })); 45 | 46 | // parse request bodies (req.body) 47 | app.use(express.urlencoded({ extended: true })) 48 | 49 | // allow overriding methods in query (?_method=put) 50 | app.use(methodOverride('_method')); 51 | 52 | // expose the "messages" local variable when views are rendered 53 | app.use(function(req, res, next){ 54 | var msgs = req.session.messages || []; 55 | 56 | // expose "messages" local variable 57 | res.locals.messages = msgs; 58 | 59 | // expose "hasMessages" 60 | res.locals.hasMessages = !! msgs.length; 61 | 62 | /* This is equivalent: 63 | res.locals({ 64 | messages: msgs, 65 | hasMessages: !! msgs.length 66 | }); 67 | */ 68 | 69 | next(); 70 | // empty or "flush" the messages so they 71 | // don't build up 72 | req.session.messages = []; 73 | }); 74 | 75 | // load controllers 76 | require('./lib/boot')(app, { verbose: !module.parent }); 77 | 78 | app.use(function(err, req, res, next){ 79 | // log it 80 | if (!module.parent) console.error(err.stack); 81 | 82 | // error page 83 | res.status(500).render('5xx'); 84 | }); 85 | 86 | // assume 404 since no middleware responded 87 | app.use(function(req, res, next){ 88 | res.status(404).render('404', { url: req.originalUrl }); 89 | }); 90 | 91 | /* istanbul ignore next */ 92 | if (!module.parent) { 93 | app.listen(3000); 94 | console.log('Express started on port 3000'); 95 | } 96 | -------------------------------------------------------------------------------- /examples/mvc/lib/boot.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../..'); 8 | var fs = require('node:fs'); 9 | var path = require('node:path'); 10 | 11 | module.exports = function(parent, options){ 12 | var dir = path.join(__dirname, '..', 'controllers'); 13 | var verbose = options.verbose; 14 | fs.readdirSync(dir).forEach(function(name){ 15 | var file = path.join(dir, name) 16 | if (!fs.statSync(file).isDirectory()) return; 17 | verbose && console.log('\n %s:', name); 18 | var obj = require(file); 19 | var name = obj.name || name; 20 | var prefix = obj.prefix || ''; 21 | var app = express(); 22 | var handler; 23 | var method; 24 | var url; 25 | 26 | // allow specifying the view engine 27 | if (obj.engine) app.set('view engine', obj.engine); 28 | app.set('views', path.join(__dirname, '..', 'controllers', name, 'views')); 29 | 30 | // generate routes based 31 | // on the exported methods 32 | for (var key in obj) { 33 | // "reserved" exports 34 | if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue; 35 | // route exports 36 | switch (key) { 37 | case 'show': 38 | method = 'get'; 39 | url = '/' + name + '/:' + name + '_id'; 40 | break; 41 | case 'list': 42 | method = 'get'; 43 | url = '/' + name + 's'; 44 | break; 45 | case 'edit': 46 | method = 'get'; 47 | url = '/' + name + '/:' + name + '_id/edit'; 48 | break; 49 | case 'update': 50 | method = 'put'; 51 | url = '/' + name + '/:' + name + '_id'; 52 | break; 53 | case 'create': 54 | method = 'post'; 55 | url = '/' + name; 56 | break; 57 | case 'index': 58 | method = 'get'; 59 | url = '/'; 60 | break; 61 | default: 62 | /* istanbul ignore next */ 63 | throw new Error('unrecognized route: ' + name + '.' + key); 64 | } 65 | 66 | // setup 67 | handler = obj[key]; 68 | url = prefix + url; 69 | 70 | // before middleware support 71 | if (obj.before) { 72 | app[method](url, obj.before, handler); 73 | verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), url, key); 74 | } else { 75 | app[method](url, handler); 76 | verbose && console.log(' %s %s -> %s', method.toUpperCase(), url, key); 77 | } 78 | } 79 | 80 | // mount the app 81 | parent.use(app); 82 | }); 83 | }; 84 | -------------------------------------------------------------------------------- /examples/mvc/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 16px "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | } 5 | a { 6 | color: #107aff; 7 | text-decoration: none; 8 | } 9 | a:hover { 10 | text-decoration: underline; 11 | } 12 | h1 a { 13 | font-size: 16px; 14 | } 15 | -------------------------------------------------------------------------------- /examples/mvc/views/404.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Not Found 7 | 8 | 9 | 10 |

404: Not Found

11 |

Sorry we can't find <%= url %>

12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/mvc/views/5xx.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Internal Server Error 7 | 8 | 9 | 10 |

500: Internal Server Error

11 |

Looks like something blew up!

12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/online/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // install redis first: 4 | // https://redis.io/ 5 | 6 | // then: 7 | // $ npm install redis online 8 | // $ redis-server 9 | 10 | /** 11 | * Module dependencies. 12 | */ 13 | 14 | var express = require('../..'); 15 | var online = require('online'); 16 | var redis = require('redis'); 17 | var db = redis.createClient(); 18 | 19 | // online 20 | 21 | online = online(db); 22 | 23 | // app 24 | 25 | var app = express(); 26 | 27 | // activity tracking, in this case using 28 | // the UA string, you would use req.user.id etc 29 | 30 | app.use(function(req, res, next){ 31 | // fire-and-forget 32 | online.add(req.headers['user-agent']); 33 | next(); 34 | }); 35 | 36 | /** 37 | * List helper. 38 | */ 39 | 40 | function list(ids) { 41 | return '
    ' + ids.map(function(id){ 42 | return '
  • ' + id + '
  • '; 43 | }).join('') + '
'; 44 | } 45 | 46 | /** 47 | * GET users online. 48 | */ 49 | 50 | app.get('/', function(req, res, next){ 51 | online.last(5, function(err, ids){ 52 | if (err) return next(err); 53 | res.send('

Users online: ' + ids.length + '

' + list(ids)); 54 | }); 55 | }); 56 | 57 | /* istanbul ignore next */ 58 | if (!module.parent) { 59 | app.listen(3000); 60 | console.log('Express started on port 3000'); 61 | } 62 | -------------------------------------------------------------------------------- /examples/params/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var createError = require('http-errors') 8 | var express = require('../../'); 9 | var app = module.exports = express(); 10 | 11 | // Faux database 12 | 13 | var users = [ 14 | { name: 'tj' } 15 | , { name: 'tobi' } 16 | , { name: 'loki' } 17 | , { name: 'jane' } 18 | , { name: 'bandit' } 19 | ]; 20 | 21 | // Convert :to and :from to integers 22 | 23 | app.param(['to', 'from'], function(req, res, next, num, name){ 24 | req.params[name] = parseInt(num, 10); 25 | if( isNaN(req.params[name]) ){ 26 | next(createError(400, 'failed to parseInt '+num)); 27 | } else { 28 | next(); 29 | } 30 | }); 31 | 32 | // Load user by id 33 | 34 | app.param('user', function(req, res, next, id){ 35 | req.user = users[id] 36 | if (req.user) { 37 | next(); 38 | } else { 39 | next(createError(404, 'failed to find user')); 40 | } 41 | }); 42 | 43 | /** 44 | * GET index. 45 | */ 46 | 47 | app.get('/', function(req, res){ 48 | res.send('Visit /user/0 or /users/0-2'); 49 | }); 50 | 51 | /** 52 | * GET :user. 53 | */ 54 | 55 | app.get('/user/:user', function (req, res) { 56 | res.send('user ' + req.user.name); 57 | }); 58 | 59 | /** 60 | * GET users :from - :to. 61 | */ 62 | 63 | app.get('/users/:from-:to', function (req, res) { 64 | var from = req.params.from; 65 | var to = req.params.to; 66 | var names = users.map(function(user){ return user.name; }); 67 | res.send('users ' + names.slice(from, to + 1).join(', ')); 68 | }); 69 | 70 | /* istanbul ignore next */ 71 | if (!module.parent) { 72 | app.listen(3000); 73 | console.log('Express started on port 3000'); 74 | } 75 | -------------------------------------------------------------------------------- /examples/resource/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | 9 | var app = module.exports = express(); 10 | 11 | // Ad-hoc example resource method 12 | 13 | app.resource = function(path, obj) { 14 | this.get(path, obj.index); 15 | this.get(path + '/:a..:b{.:format}', function(req, res){ 16 | var a = parseInt(req.params.a, 10); 17 | var b = parseInt(req.params.b, 10); 18 | var format = req.params.format; 19 | obj.range(req, res, a, b, format); 20 | }); 21 | this.get(path + '/:id', obj.show); 22 | this.delete(path + '/:id', function(req, res){ 23 | var id = parseInt(req.params.id, 10); 24 | obj.destroy(req, res, id); 25 | }); 26 | }; 27 | 28 | // Fake records 29 | 30 | var users = [ 31 | { name: 'tj' } 32 | , { name: 'ciaran' } 33 | , { name: 'aaron' } 34 | , { name: 'guillermo' } 35 | , { name: 'simon' } 36 | , { name: 'tobi' } 37 | ]; 38 | 39 | // Fake controller. 40 | 41 | var User = { 42 | index: function(req, res){ 43 | res.send(users); 44 | }, 45 | show: function(req, res){ 46 | res.send(users[req.params.id] || { error: 'Cannot find user' }); 47 | }, 48 | destroy: function(req, res, id){ 49 | var destroyed = id in users; 50 | delete users[id]; 51 | res.send(destroyed ? 'destroyed' : 'Cannot find user'); 52 | }, 53 | range: function(req, res, a, b, format){ 54 | var range = users.slice(a, b + 1); 55 | switch (format) { 56 | case 'json': 57 | res.send(range); 58 | break; 59 | case 'html': 60 | default: 61 | var html = '
    ' + range.map(function(user){ 62 | return '
  • ' + user.name + '
  • '; 63 | }).join('\n') + '
'; 64 | res.send(html); 65 | break; 66 | } 67 | } 68 | }; 69 | 70 | // curl http://localhost:3000/users -- responds with all users 71 | // curl http://localhost:3000/users/1 -- responds with user 1 72 | // curl http://localhost:3000/users/4 -- responds with error 73 | // curl http://localhost:3000/users/1..3 -- responds with several users 74 | // curl -X DELETE http://localhost:3000/users/1 -- deletes the user 75 | 76 | app.resource('/users', User); 77 | 78 | app.get('/', function(req, res){ 79 | res.send([ 80 | '

Examples:

    ' 81 | , '
  • GET /users
  • ' 82 | , '
  • GET /users/1
  • ' 83 | , '
  • GET /users/3
  • ' 84 | , '
  • GET /users/1..3
  • ' 85 | , '
  • GET /users/1..3.json
  • ' 86 | , '
  • DELETE /users/4
  • ' 87 | , '
' 88 | ].join('\n')); 89 | }); 90 | 91 | /* istanbul ignore next */ 92 | if (!module.parent) { 93 | app.listen(3000); 94 | console.log('Express started on port 3000'); 95 | } 96 | -------------------------------------------------------------------------------- /examples/route-map/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var escapeHtml = require('escape-html') 8 | var express = require('../../lib/express'); 9 | 10 | var verbose = process.env.NODE_ENV !== 'test' 11 | 12 | var app = module.exports = express(); 13 | 14 | app.map = function(a, route){ 15 | route = route || ''; 16 | for (var key in a) { 17 | switch (typeof a[key]) { 18 | // { '/path': { ... }} 19 | case 'object': 20 | app.map(a[key], route + key); 21 | break; 22 | // get: function(){ ... } 23 | case 'function': 24 | if (verbose) console.log('%s %s', key, route); 25 | app[key](route, a[key]); 26 | break; 27 | } 28 | } 29 | }; 30 | 31 | var users = { 32 | list: function(req, res){ 33 | res.send('user list'); 34 | }, 35 | 36 | get: function(req, res){ 37 | res.send('user ' + escapeHtml(req.params.uid)) 38 | }, 39 | 40 | delete: function(req, res){ 41 | res.send('delete users'); 42 | } 43 | }; 44 | 45 | var pets = { 46 | list: function(req, res){ 47 | res.send('user ' + escapeHtml(req.params.uid) + '\'s pets') 48 | }, 49 | 50 | delete: function(req, res){ 51 | res.send('delete ' + escapeHtml(req.params.uid) + '\'s pet ' + escapeHtml(req.params.pid)) 52 | } 53 | }; 54 | 55 | app.map({ 56 | '/users': { 57 | get: users.list, 58 | delete: users.delete, 59 | '/:uid': { 60 | get: users.get, 61 | '/pets': { 62 | get: pets.list, 63 | '/:pid': { 64 | delete: pets.delete 65 | } 66 | } 67 | } 68 | } 69 | }); 70 | 71 | /* istanbul ignore next */ 72 | if (!module.parent) { 73 | app.listen(3000); 74 | console.log('Express started on port 3000'); 75 | } 76 | -------------------------------------------------------------------------------- /examples/route-middleware/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../lib/express'); 8 | 9 | var app = express(); 10 | 11 | // Example requests: 12 | // curl http://localhost:3000/user/0 13 | // curl http://localhost:3000/user/0/edit 14 | // curl http://localhost:3000/user/1 15 | // curl http://localhost:3000/user/1/edit (unauthorized since this is not you) 16 | // curl -X DELETE http://localhost:3000/user/0 (unauthorized since you are not an admin) 17 | 18 | // Dummy users 19 | var users = [ 20 | { id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' } 21 | , { id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' } 22 | , { id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' } 23 | ]; 24 | 25 | function loadUser(req, res, next) { 26 | // You would fetch your user from the db 27 | var user = users[req.params.id]; 28 | if (user) { 29 | req.user = user; 30 | next(); 31 | } else { 32 | next(new Error('Failed to load user ' + req.params.id)); 33 | } 34 | } 35 | 36 | function andRestrictToSelf(req, res, next) { 37 | // If our authenticated user is the user we are viewing 38 | // then everything is fine :) 39 | if (req.authenticatedUser.id === req.user.id) { 40 | next(); 41 | } else { 42 | // You may want to implement specific exceptions 43 | // such as UnauthorizedError or similar so that you 44 | // can handle these can be special-cased in an error handler 45 | // (view ./examples/pages for this) 46 | next(new Error('Unauthorized')); 47 | } 48 | } 49 | 50 | function andRestrictTo(role) { 51 | return function(req, res, next) { 52 | if (req.authenticatedUser.role === role) { 53 | next(); 54 | } else { 55 | next(new Error('Unauthorized')); 56 | } 57 | } 58 | } 59 | 60 | // Middleware for faux authentication 61 | // you would of course implement something real, 62 | // but this illustrates how an authenticated user 63 | // may interact with middleware 64 | 65 | app.use(function(req, res, next){ 66 | req.authenticatedUser = users[0]; 67 | next(); 68 | }); 69 | 70 | app.get('/', function(req, res){ 71 | res.redirect('/user/0'); 72 | }); 73 | 74 | app.get('/user/:id', loadUser, function(req, res){ 75 | res.send('Viewing user ' + req.user.name); 76 | }); 77 | 78 | app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){ 79 | res.send('Editing user ' + req.user.name); 80 | }); 81 | 82 | app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){ 83 | res.send('Deleted user ' + req.user.name); 84 | }); 85 | 86 | /* istanbul ignore next */ 87 | if (!module.parent) { 88 | app.listen(3000); 89 | console.log('Express started on port 3000'); 90 | } 91 | -------------------------------------------------------------------------------- /examples/route-separation/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var path = require('node:path'); 9 | var app = express(); 10 | var logger = require('morgan'); 11 | var cookieParser = require('cookie-parser'); 12 | var methodOverride = require('method-override'); 13 | var site = require('./site'); 14 | var post = require('./post'); 15 | var user = require('./user'); 16 | 17 | module.exports = app; 18 | 19 | // Config 20 | 21 | app.set('view engine', 'ejs'); 22 | app.set('views', path.join(__dirname, 'views')); 23 | 24 | /* istanbul ignore next */ 25 | if (!module.parent) { 26 | app.use(logger('dev')); 27 | } 28 | 29 | app.use(methodOverride('_method')); 30 | app.use(cookieParser()); 31 | app.use(express.urlencoded({ extended: true })) 32 | app.use(express.static(path.join(__dirname, 'public'))); 33 | 34 | // General 35 | 36 | app.get('/', site.index); 37 | 38 | // User 39 | 40 | app.get('/users', user.list); 41 | app.all('/user/:id{/:op}', user.load); 42 | app.get('/user/:id', user.view); 43 | app.get('/user/:id/view', user.view); 44 | app.get('/user/:id/edit', user.edit); 45 | app.put('/user/:id/edit', user.update); 46 | 47 | // Posts 48 | 49 | app.get('/posts', post.list); 50 | 51 | /* istanbul ignore next */ 52 | if (!module.parent) { 53 | app.listen(3000); 54 | console.log('Express started on port 3000'); 55 | } 56 | -------------------------------------------------------------------------------- /examples/route-separation/post.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Fake posts database 4 | 5 | var posts = [ 6 | { title: 'Foo', body: 'some foo bar' }, 7 | { title: 'Foo bar', body: 'more foo bar' }, 8 | { title: 'Foo bar baz', body: 'more foo bar baz' } 9 | ]; 10 | 11 | exports.list = function(req, res){ 12 | res.render('posts', { title: 'Posts', posts: posts }); 13 | }; 14 | -------------------------------------------------------------------------------- /examples/route-separation/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Helvetica Neue", Arial, sans-serif; 4 | } 5 | a { 6 | color: #00AEFF; 7 | text-decoration: none; 8 | } 9 | a.edit { 10 | color: #000; 11 | opacity: .3; 12 | } 13 | a.edit::before { 14 | content: ' ['; 15 | } 16 | a.edit::after { 17 | content: ']'; 18 | } 19 | dt { 20 | font-weight: bold; 21 | } 22 | dd { 23 | margin: 15px; 24 | } -------------------------------------------------------------------------------- /examples/route-separation/site.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | exports.index = function(req, res){ 4 | res.render('index', { title: 'Route Separation Example' }); 5 | }; 6 | -------------------------------------------------------------------------------- /examples/route-separation/user.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Fake user database 4 | 5 | var users = [ 6 | { name: 'TJ', email: 'tj@vision-media.ca' }, 7 | { name: 'Tobi', email: 'tobi@vision-media.ca' } 8 | ]; 9 | 10 | exports.list = function(req, res){ 11 | res.render('users', { title: 'Users', users: users }); 12 | }; 13 | 14 | exports.load = function(req, res, next){ 15 | var id = req.params.id; 16 | req.user = users[id]; 17 | if (req.user) { 18 | next(); 19 | } else { 20 | var err = new Error('cannot find user ' + id); 21 | err.status = 404; 22 | next(err); 23 | } 24 | }; 25 | 26 | exports.view = function(req, res){ 27 | res.render('users/view', { 28 | title: 'Viewing user ' + req.user.name, 29 | user: req.user 30 | }); 31 | }; 32 | 33 | exports.edit = function(req, res){ 34 | res.render('users/edit', { 35 | title: 'Editing user ' + req.user.name, 36 | user: req.user 37 | }); 38 | }; 39 | 40 | exports.update = function(req, res){ 41 | // Normally you would handle all kinds of 42 | // validation and save back to the db 43 | var user = req.body.user; 44 | req.user.name = user.name; 45 | req.user.email = user.email; 46 | res.redirect(req.get('Referrer') || '/'); 47 | }; 48 | -------------------------------------------------------------------------------- /examples/route-separation/views/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/route-separation/views/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/route-separation/views/index.ejs: -------------------------------------------------------------------------------- 1 | <%- include('header') -%> 2 | 3 |

<%= title %>

4 | 5 |
    6 |
  • Visit the users page.
  • 7 |
  • Visit the posts page.
  • 8 |
9 | 10 | <%- include('footer') -%> 11 | -------------------------------------------------------------------------------- /examples/route-separation/views/posts/index.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../header') -%> 2 | 3 |

Posts

4 | 5 |
6 | <% posts.forEach(function(post) { %> 7 |
<%= post.title %>
8 |
<%= post.body %>
9 | <% }) %> 10 |
11 | 12 | <%- include('../footer') -%> 13 | -------------------------------------------------------------------------------- /examples/route-separation/views/users/edit.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../header') -%> 2 | 3 |

Editing <%= user.name %>

4 | 5 |
6 |
7 |

8 | Name: 9 | 10 |

11 | 12 |

13 | Email: 14 | 15 |

16 | 17 |

18 | 19 |

20 |
21 |
22 | 23 | <%- include('../footer') -%> 24 | -------------------------------------------------------------------------------- /examples/route-separation/views/users/index.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../header') -%> 2 | 3 |

<%= title %>

4 | 5 |
6 | <% users.forEach(function(user, index) { %> 7 |
  • 8 | <%= user.name %> 9 | edit 10 |
  • 11 | <% }) %> 12 |
    13 | 14 | <%- include('../footer') -%> 15 | -------------------------------------------------------------------------------- /examples/route-separation/views/users/view.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../header') -%> 2 | 3 |

    <%= user.name %>

    4 | 5 |
    6 |

    Email: <%= user.email %>

    7 |
    8 | 9 | <%- include('../footer') -%> 10 | -------------------------------------------------------------------------------- /examples/search/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // install redis first: 4 | // https://redis.io/ 5 | 6 | // then: 7 | // $ npm install redis 8 | // $ redis-server 9 | 10 | /** 11 | * Module dependencies. 12 | */ 13 | 14 | var express = require('../..'); 15 | var path = require('node:path'); 16 | var redis = require('redis'); 17 | 18 | var db = redis.createClient(); 19 | 20 | // npm install redis 21 | 22 | var app = express(); 23 | 24 | app.use(express.static(path.join(__dirname, 'public'))); 25 | 26 | // populate search 27 | 28 | db.sadd('ferret', 'tobi'); 29 | db.sadd('ferret', 'loki'); 30 | db.sadd('ferret', 'jane'); 31 | db.sadd('cat', 'manny'); 32 | db.sadd('cat', 'luna'); 33 | 34 | /** 35 | * GET search for :query. 36 | */ 37 | 38 | app.get('/search/:query?', function(req, res, next){ 39 | var query = req.params.query; 40 | db.smembers(query, function(err, vals){ 41 | if (err) return next(err); 42 | res.send(vals); 43 | }); 44 | }); 45 | 46 | /** 47 | * GET client javascript. Here we use sendFile() 48 | * because serving __dirname with the static() middleware 49 | * would also mean serving our server "index.js" and the "search.jade" 50 | * template. 51 | */ 52 | 53 | app.get('/client.js', function(req, res){ 54 | res.sendFile(path.join(__dirname, 'client.js')); 55 | }); 56 | 57 | /* istanbul ignore next */ 58 | if (!module.parent) { 59 | app.listen(3000); 60 | console.log('Express started on port 3000'); 61 | } 62 | -------------------------------------------------------------------------------- /examples/search/public/client.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var search = document.querySelector('[type=search]'); 4 | var code = document.querySelector('pre'); 5 | 6 | search.addEventListener('keyup', function(){ 7 | var xhr = new XMLHttpRequest; 8 | xhr.open('GET', '/search/' + search.value, true); 9 | xhr.onreadystatechange = function(){ 10 | if (xhr.readyState === 4) { 11 | code.textContent = xhr.responseText; 12 | } 13 | }; 14 | xhr.send(); 15 | }, false); 16 | -------------------------------------------------------------------------------- /examples/search/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Search example 7 | 13 | 14 | 15 |

    Search

    16 |

    Try searching for "ferret" or "cat".

    17 | 18 |
    
    19 |   
    20 | 
    21 | 
    22 | 
    
    
    --------------------------------------------------------------------------------
    /examples/session/index.js:
    --------------------------------------------------------------------------------
     1 | 'use strict'
     2 | 
     3 | // install redis first:
     4 | // https://redis.io/
     5 | 
     6 | // then:
     7 | // $ npm install redis
     8 | // $ redis-server
     9 | 
    10 | var express = require('../..');
    11 | var session = require('express-session');
    12 | 
    13 | var app = express();
    14 | 
    15 | // Populates req.session
    16 | app.use(session({
    17 |   resave: false, // don't save session if unmodified
    18 |   saveUninitialized: false, // don't create session until something stored
    19 |   secret: 'keyboard cat'
    20 | }));
    21 | 
    22 | app.get('/', function(req, res){
    23 |   var body = '';
    24 |   if (req.session.views) {
    25 |     ++req.session.views;
    26 |   } else {
    27 |     req.session.views = 1;
    28 |     body += '

    First time visiting? view this page in several browsers :)

    '; 29 | } 30 | res.send(body + '

    viewed ' + req.session.views + ' times.

    '); 31 | }); 32 | 33 | /* istanbul ignore next */ 34 | if (!module.parent) { 35 | app.listen(3000); 36 | console.log('Express started on port 3000'); 37 | } 38 | -------------------------------------------------------------------------------- /examples/session/redis.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var logger = require('morgan'); 9 | var session = require('express-session'); 10 | 11 | // pass the express to the connect redis module 12 | // allowing it to inherit from session.Store 13 | var RedisStore = require('connect-redis')(session); 14 | 15 | var app = express(); 16 | 17 | app.use(logger('dev')); 18 | 19 | // Populates req.session 20 | app.use(session({ 21 | resave: false, // don't save session if unmodified 22 | saveUninitialized: false, // don't create session until something stored 23 | secret: 'keyboard cat', 24 | store: new RedisStore 25 | })); 26 | 27 | app.get('/', function(req, res){ 28 | var body = ''; 29 | if (req.session.views) { 30 | ++req.session.views; 31 | } else { 32 | req.session.views = 1; 33 | body += '

    First time visiting? view this page in several browsers :)

    '; 34 | } 35 | res.send(body + '

    viewed ' + req.session.views + ' times.

    '); 36 | }); 37 | 38 | app.listen(3000); 39 | console.log('Express app started on port 3000'); 40 | -------------------------------------------------------------------------------- /examples/static-files/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var logger = require('morgan'); 9 | var path = require('node:path'); 10 | var app = express(); 11 | 12 | // log requests 13 | app.use(logger('dev')); 14 | 15 | // express on its own has no notion 16 | // of a "file". The express.static() 17 | // middleware checks for a file matching 18 | // the `req.path` within the directory 19 | // that you pass it. In this case "GET /js/app.js" 20 | // will look for "./public/js/app.js". 21 | 22 | app.use(express.static(path.join(__dirname, 'public'))); 23 | 24 | // if you wanted to "prefix" you may use 25 | // the mounting feature of Connect, for example 26 | // "GET /static/js/app.js" instead of "GET /js/app.js". 27 | // The mount-path "/static" is simply removed before 28 | // passing control to the express.static() middleware, 29 | // thus it serves the file correctly by ignoring "/static" 30 | app.use('/static', express.static(path.join(__dirname, 'public'))); 31 | 32 | // if for some reason you want to serve files from 33 | // several directories, you can use express.static() 34 | // multiple times! Here we're passing "./public/css", 35 | // this will allow "GET /style.css" instead of "GET /css/style.css": 36 | app.use(express.static(path.join(__dirname, 'public', 'css'))); 37 | 38 | app.listen(3000); 39 | console.log('listening on port 3000'); 40 | console.log('try:'); 41 | console.log(' GET /hello.txt'); 42 | console.log(' GET /js/app.js'); 43 | console.log(' GET /css/style.css'); 44 | -------------------------------------------------------------------------------- /examples/static-files/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | 3 | } -------------------------------------------------------------------------------- /examples/static-files/public/hello.txt: -------------------------------------------------------------------------------- 1 | hey -------------------------------------------------------------------------------- /examples/static-files/public/js/app.js: -------------------------------------------------------------------------------- 1 | // foo 2 | -------------------------------------------------------------------------------- /examples/vhost/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var logger = require('morgan'); 9 | var vhost = require('vhost'); 10 | 11 | /* 12 | edit /etc/hosts: 13 | 14 | 127.0.0.1 foo.example.com 15 | 127.0.0.1 bar.example.com 16 | 127.0.0.1 example.com 17 | */ 18 | 19 | // Main server app 20 | 21 | var main = express(); 22 | 23 | if (!module.parent) main.use(logger('dev')); 24 | 25 | main.get('/', function(req, res){ 26 | res.send('Hello from main app!'); 27 | }); 28 | 29 | main.get('/:sub', function(req, res){ 30 | res.send('requested ' + req.params.sub); 31 | }); 32 | 33 | // Redirect app 34 | 35 | var redirect = express(); 36 | 37 | redirect.use(function(req, res){ 38 | if (!module.parent) console.log(req.vhost); 39 | res.redirect('http://example.com:3000/' + req.vhost[0]); 40 | }); 41 | 42 | // Vhost app 43 | 44 | var app = module.exports = express(); 45 | 46 | app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app 47 | app.use(vhost('example.com', main)); // Serves top level domain via Main server app 48 | 49 | /* istanbul ignore next */ 50 | if (!module.parent) { 51 | app.listen(3000); 52 | console.log('Express started on port 3000'); 53 | } 54 | -------------------------------------------------------------------------------- /examples/view-constructor/github-view.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var https = require('node:https'); 8 | var path = require('node:path'); 9 | var extname = path.extname; 10 | 11 | /** 12 | * Expose `GithubView`. 13 | */ 14 | 15 | module.exports = GithubView; 16 | 17 | /** 18 | * Custom view that fetches and renders 19 | * remove github templates. You could 20 | * render templates from a database etc. 21 | */ 22 | 23 | function GithubView(name, options){ 24 | this.name = name; 25 | options = options || {}; 26 | this.engine = options.engines[extname(name)]; 27 | // "root" is the app.set('views') setting, however 28 | // in your own implementation you could ignore this 29 | this.path = '/' + options.root + '/master/' + name; 30 | } 31 | 32 | /** 33 | * Render the view. 34 | */ 35 | 36 | GithubView.prototype.render = function(options, fn){ 37 | var self = this; 38 | var opts = { 39 | host: 'raw.githubusercontent.com', 40 | port: 443, 41 | path: this.path, 42 | method: 'GET' 43 | }; 44 | 45 | https.request(opts, function(res) { 46 | var buf = ''; 47 | res.setEncoding('utf8'); 48 | res.on('data', function(str){ buf += str }); 49 | res.on('end', function(){ 50 | self.engine(buf, options, fn); 51 | }); 52 | }).end(); 53 | }; 54 | -------------------------------------------------------------------------------- /examples/view-constructor/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var GithubView = require('./github-view'); 9 | var md = require('marked').parse; 10 | 11 | var app = module.exports = express(); 12 | 13 | // register .md as an engine in express view system 14 | app.engine('md', function(str, options, fn){ 15 | try { 16 | var html = md(str); 17 | html = html.replace(/\{([^}]+)\}/g, function(_, name){ 18 | return options[name] || ''; 19 | }); 20 | fn(null, html); 21 | } catch(err) { 22 | fn(err); 23 | } 24 | }); 25 | 26 | // pointing to a particular github repo to load files from it 27 | app.set('views', 'expressjs/express'); 28 | 29 | // register a new view constructor 30 | app.set('view', GithubView); 31 | 32 | app.get('/', function(req, res){ 33 | // rendering a view relative to the repo. 34 | // app.locals, res.locals, and locals passed 35 | // work like they normally would 36 | res.render('examples/markdown/views/index.md', { title: 'Example' }); 37 | }); 38 | 39 | app.get('/Readme.md', function(req, res){ 40 | // rendering a view from https://github.com/expressjs/express/blob/master/Readme.md 41 | res.render('Readme.md'); 42 | }); 43 | 44 | /* istanbul ignore next */ 45 | if (!module.parent) { 46 | app.listen(3000); 47 | console.log('Express started on port 3000'); 48 | } 49 | -------------------------------------------------------------------------------- /examples/view-locals/user.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = User; 4 | 5 | // faux model 6 | 7 | function User(name, age, species) { 8 | this.name = name; 9 | this.age = age; 10 | this.species = species; 11 | } 12 | 13 | User.all = function(fn){ 14 | // process.nextTick makes sure this function API 15 | // behaves in an asynchronous manner, like if it 16 | // was a real DB query to read all users. 17 | process.nextTick(function(){ 18 | fn(null, users); 19 | }); 20 | }; 21 | 22 | User.count = function(fn){ 23 | process.nextTick(function(){ 24 | fn(null, users.length); 25 | }); 26 | }; 27 | 28 | // faux database 29 | 30 | var users = []; 31 | 32 | users.push(new User('Tobi', 2, 'ferret')); 33 | users.push(new User('Loki', 1, 'ferret')); 34 | users.push(new User('Jane', 6, 'ferret')); 35 | users.push(new User('Luna', 1, 'cat')); 36 | users.push(new User('Manny', 1, 'cat')); 37 | -------------------------------------------------------------------------------- /examples/view-locals/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 13 | 14 | 15 |

    <%= title %>

    16 | <% users.forEach(function(user) { %> 17 |
  • <%= user.name %> is a <% user.age %> year old <%= user.species %>
  • 18 | <% }); %> 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/web-service/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | 9 | var app = module.exports = express(); 10 | 11 | // create an error with .status. we 12 | // can then use the property in our 13 | // custom error handler (Connect respects this prop as well) 14 | 15 | function error(status, msg) { 16 | var err = new Error(msg); 17 | err.status = status; 18 | return err; 19 | } 20 | 21 | // if we wanted to supply more than JSON, we could 22 | // use something similar to the content-negotiation 23 | // example. 24 | 25 | // here we validate the API key, 26 | // by mounting this middleware to /api 27 | // meaning only paths prefixed with "/api" 28 | // will cause this middleware to be invoked 29 | 30 | app.use('/api', function(req, res, next){ 31 | var key = req.query['api-key']; 32 | 33 | // key isn't present 34 | if (!key) return next(error(400, 'api key required')); 35 | 36 | // key is invalid 37 | if (apiKeys.indexOf(key) === -1) return next(error(401, 'invalid api key')) 38 | 39 | // all good, store req.key for route access 40 | req.key = key; 41 | next(); 42 | }); 43 | 44 | // map of valid api keys, typically mapped to 45 | // account info with some sort of database like redis. 46 | // api keys do _not_ serve as authentication, merely to 47 | // track API usage or help prevent malicious behavior etc. 48 | 49 | var apiKeys = ['foo', 'bar', 'baz']; 50 | 51 | // these two objects will serve as our faux database 52 | 53 | var repos = [ 54 | { name: 'express', url: 'https://github.com/expressjs/express' }, 55 | { name: 'stylus', url: 'https://github.com/learnboost/stylus' }, 56 | { name: 'cluster', url: 'https://github.com/learnboost/cluster' } 57 | ]; 58 | 59 | var users = [ 60 | { name: 'tobi' } 61 | , { name: 'loki' } 62 | , { name: 'jane' } 63 | ]; 64 | 65 | var userRepos = { 66 | tobi: [repos[0], repos[1]] 67 | , loki: [repos[1]] 68 | , jane: [repos[2]] 69 | }; 70 | 71 | // we now can assume the api key is valid, 72 | // and simply expose the data 73 | 74 | // example: http://localhost:3000/api/users/?api-key=foo 75 | app.get('/api/users', function (req, res) { 76 | res.send(users); 77 | }); 78 | 79 | // example: http://localhost:3000/api/repos/?api-key=foo 80 | app.get('/api/repos', function (req, res) { 81 | res.send(repos); 82 | }); 83 | 84 | // example: http://localhost:3000/api/user/tobi/repos/?api-key=foo 85 | app.get('/api/user/:name/repos', function(req, res, next){ 86 | var name = req.params.name; 87 | var user = userRepos[name]; 88 | 89 | if (user) res.send(user); 90 | else next(); 91 | }); 92 | 93 | // middleware with an arity of 4 are considered 94 | // error handling middleware. When you next(err) 95 | // it will be passed through the defined middleware 96 | // in order, but ONLY those with an arity of 4, ignoring 97 | // regular middleware. 98 | app.use(function(err, req, res, next){ 99 | // whatever you want here, feel free to populate 100 | // properties on `err` to treat it differently in here. 101 | res.status(err.status || 500); 102 | res.send({ error: err.message }); 103 | }); 104 | 105 | // our custom JSON 404 middleware. Since it's placed last 106 | // it will be the last middleware called, if all others 107 | // invoke next() and do not respond. 108 | app.use(function(req, res){ 109 | res.status(404); 110 | res.send({ error: "Sorry, can't find that" }) 111 | }); 112 | 113 | /* istanbul ignore next */ 114 | if (!module.parent) { 115 | app.listen(3000); 116 | console.log('Express started on port 3000'); 117 | } 118 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express 3 | * Copyright(c) 2009-2013 TJ Holowaychuk 4 | * Copyright(c) 2013 Roman Shtylman 5 | * Copyright(c) 2014-2015 Douglas Christopher Wilson 6 | * MIT Licensed 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = require('./lib/express'); 12 | -------------------------------------------------------------------------------- /lib/express.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express 3 | * Copyright(c) 2009-2013 TJ Holowaychuk 4 | * Copyright(c) 2013 Roman Shtylman 5 | * Copyright(c) 2014-2015 Douglas Christopher Wilson 6 | * MIT Licensed 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Module dependencies. 13 | */ 14 | 15 | var bodyParser = require('body-parser') 16 | var EventEmitter = require('node:events').EventEmitter; 17 | var mixin = require('merge-descriptors'); 18 | var proto = require('./application'); 19 | var Router = require('router'); 20 | var req = require('./request'); 21 | var res = require('./response'); 22 | 23 | /** 24 | * Expose `createApplication()`. 25 | */ 26 | 27 | exports = module.exports = createApplication; 28 | 29 | /** 30 | * Create an express application. 31 | * 32 | * @return {Function} 33 | * @api public 34 | */ 35 | 36 | function createApplication() { 37 | var app = function(req, res, next) { 38 | app.handle(req, res, next); 39 | }; 40 | 41 | mixin(app, EventEmitter.prototype, false); 42 | mixin(app, proto, false); 43 | 44 | // expose the prototype that will get set on requests 45 | app.request = Object.create(req, { 46 | app: { configurable: true, enumerable: true, writable: true, value: app } 47 | }) 48 | 49 | // expose the prototype that will get set on responses 50 | app.response = Object.create(res, { 51 | app: { configurable: true, enumerable: true, writable: true, value: app } 52 | }) 53 | 54 | app.init(); 55 | return app; 56 | } 57 | 58 | /** 59 | * Expose the prototypes. 60 | */ 61 | 62 | exports.application = proto; 63 | exports.request = req; 64 | exports.response = res; 65 | 66 | /** 67 | * Expose constructors. 68 | */ 69 | 70 | exports.Route = Router.Route; 71 | exports.Router = Router; 72 | 73 | /** 74 | * Expose middleware 75 | */ 76 | 77 | exports.json = bodyParser.json 78 | exports.raw = bodyParser.raw 79 | exports.static = require('serve-static'); 80 | exports.text = bodyParser.text 81 | exports.urlencoded = bodyParser.urlencoded 82 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express", 3 | "description": "Fast, unopinionated, minimalist web framework", 4 | "version": "5.1.0", 5 | "author": "TJ Holowaychuk ", 6 | "contributors": [ 7 | "Aaron Heckmann ", 8 | "Ciaran Jessup ", 9 | "Douglas Christopher Wilson ", 10 | "Guillermo Rauch ", 11 | "Jonathan Ong ", 12 | "Roman Shtylman ", 13 | "Young Jae Sim " 14 | ], 15 | "license": "MIT", 16 | "repository": "expressjs/express", 17 | "homepage": "https://expressjs.com/", 18 | "funding": { 19 | "type": "opencollective", 20 | "url": "https://opencollective.com/express" 21 | }, 22 | "keywords": [ 23 | "express", 24 | "framework", 25 | "sinatra", 26 | "web", 27 | "http", 28 | "rest", 29 | "restful", 30 | "router", 31 | "app", 32 | "api" 33 | ], 34 | "dependencies": { 35 | "accepts": "^2.0.0", 36 | "body-parser": "^2.2.0", 37 | "content-disposition": "^1.0.0", 38 | "content-type": "^1.0.5", 39 | "cookie": "^0.7.1", 40 | "cookie-signature": "^1.2.1", 41 | "debug": "^4.4.0", 42 | "encodeurl": "^2.0.0", 43 | "escape-html": "^1.0.3", 44 | "etag": "^1.8.1", 45 | "finalhandler": "^2.1.0", 46 | "fresh": "^2.0.0", 47 | "http-errors": "^2.0.0", 48 | "merge-descriptors": "^2.0.0", 49 | "mime-types": "^3.0.0", 50 | "on-finished": "^2.4.1", 51 | "once": "^1.4.0", 52 | "parseurl": "^1.3.3", 53 | "proxy-addr": "^2.0.7", 54 | "qs": "^6.14.0", 55 | "range-parser": "^1.2.1", 56 | "router": "^2.2.0", 57 | "send": "^1.1.0", 58 | "serve-static": "^2.2.0", 59 | "statuses": "^2.0.1", 60 | "type-is": "^2.0.1", 61 | "vary": "^1.1.2" 62 | }, 63 | "devDependencies": { 64 | "after": "0.8.2", 65 | "connect-redis": "^8.0.1", 66 | "cookie-parser": "1.4.7", 67 | "cookie-session": "2.1.0", 68 | "ejs": "^3.1.10", 69 | "eslint": "8.47.0", 70 | "express-session": "^1.18.1", 71 | "hbs": "4.2.0", 72 | "marked": "^15.0.3", 73 | "method-override": "3.0.0", 74 | "mocha": "^10.7.3", 75 | "morgan": "1.10.0", 76 | "nyc": "^17.1.0", 77 | "pbkdf2-password": "1.2.1", 78 | "supertest": "^6.3.0", 79 | "vhost": "~3.0.2" 80 | }, 81 | "engines": { 82 | "node": ">= 18" 83 | }, 84 | "files": [ 85 | "LICENSE", 86 | "History.md", 87 | "Readme.md", 88 | "index.js", 89 | "lib/" 90 | ], 91 | "scripts": { 92 | "lint": "eslint .", 93 | "test": "mocha --require test/support/env --reporter spec --check-leaks test/ test/acceptance/", 94 | "test-ci": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=lcovonly --reporter=text npm test", 95 | "test-cov": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=html --reporter=text npm test", 96 | "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /test/acceptance/auth.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/auth') 2 | var request = require('supertest') 3 | 4 | function getCookie(res) { 5 | return res.headers['set-cookie'][0].split(';')[0]; 6 | } 7 | 8 | describe('auth', function(){ 9 | describe('GET /',function(){ 10 | it('should redirect to /login', function(done){ 11 | request(app) 12 | .get('/') 13 | .expect('Location', '/login') 14 | .expect(302, done) 15 | }) 16 | }) 17 | 18 | describe('GET /login',function(){ 19 | it('should render login form', function(done){ 20 | request(app) 21 | .get('/login') 22 | .expect(200, /
  • Tobi
  • Loki
  • Jane
  • ', done) 11 | }) 12 | 13 | it('should accept to text/plain', function(done){ 14 | request(app) 15 | .get('/') 16 | .set('Accept', 'text/plain') 17 | .expect(200, ' - Tobi\n - Loki\n - Jane\n', done) 18 | }) 19 | 20 | it('should accept to application/json', function(done){ 21 | request(app) 22 | .get('/') 23 | .set('Accept', 'application/json') 24 | .expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done) 25 | }) 26 | }) 27 | 28 | describe('GET /users', function(){ 29 | it('should default to text/html', function(done){ 30 | request(app) 31 | .get('/users') 32 | .expect(200, '
    • Tobi
    • Loki
    • Jane
    ', done) 33 | }) 34 | 35 | it('should accept to text/plain', function(done){ 36 | request(app) 37 | .get('/users') 38 | .set('Accept', 'text/plain') 39 | .expect(200, ' - Tobi\n - Loki\n - Jane\n', done) 40 | }) 41 | 42 | it('should accept to application/json', function(done){ 43 | request(app) 44 | .get('/users') 45 | .set('Accept', 'application/json') 46 | .expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done) 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/acceptance/cookie-sessions.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/cookie-sessions') 3 | var request = require('supertest') 4 | 5 | describe('cookie-sessions', function () { 6 | describe('GET /', function () { 7 | it('should display no views', function (done) { 8 | request(app) 9 | .get('/') 10 | .expect(200, 'viewed 1 times\n', done) 11 | }) 12 | 13 | it('should set a session cookie', function (done) { 14 | request(app) 15 | .get('/') 16 | .expect('Set-Cookie', /session=/) 17 | .expect(200, done) 18 | }) 19 | 20 | it('should display 1 view on revisit', function (done) { 21 | request(app) 22 | .get('/') 23 | .expect(200, 'viewed 1 times\n', function (err, res) { 24 | if (err) return done(err) 25 | request(app) 26 | .get('/') 27 | .set('Cookie', getCookies(res)) 28 | .expect(200, 'viewed 2 times\n', done) 29 | }) 30 | }) 31 | }) 32 | }) 33 | 34 | function getCookies(res) { 35 | return res.headers['set-cookie'].map(function (val) { 36 | return val.split(';')[0] 37 | }).join('; '); 38 | } 39 | -------------------------------------------------------------------------------- /test/acceptance/cookies.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/cookies') 3 | , request = require('supertest'); 4 | var utils = require('../support/utils'); 5 | 6 | describe('cookies', function(){ 7 | describe('GET /', function(){ 8 | it('should have a form', function(done){ 9 | request(app) 10 | .get('/') 11 | .expect(/tobi <tobi@learnboost\.com><\/li>/) 12 | .expect(/
  • loki <loki@learnboost\.com><\/li>/) 13 | .expect(/
  • jane <jane@learnboost\.com><\/li>/) 14 | .expect(200, done) 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /test/acceptance/error-pages.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/error-pages') 3 | , request = require('supertest'); 4 | 5 | describe('error-pages', function(){ 6 | describe('GET /', function(){ 7 | it('should respond with page list', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect(/Pages Example/, done) 11 | }) 12 | }) 13 | 14 | describe('Accept: text/html',function(){ 15 | describe('GET /403', function(){ 16 | it('should respond with 403', function(done){ 17 | request(app) 18 | .get('/403') 19 | .expect(403, done) 20 | }) 21 | }) 22 | 23 | describe('GET /404', function(){ 24 | it('should respond with 404', function(done){ 25 | request(app) 26 | .get('/404') 27 | .expect(404, done) 28 | }) 29 | }) 30 | 31 | describe('GET /500', function(){ 32 | it('should respond with 500', function(done){ 33 | request(app) 34 | .get('/500') 35 | .expect(500, done) 36 | }) 37 | }) 38 | }) 39 | 40 | describe('Accept: application/json',function(){ 41 | describe('GET /403', function(){ 42 | it('should respond with 403', function(done){ 43 | request(app) 44 | .get('/403') 45 | .set('Accept','application/json') 46 | .expect(403, done) 47 | }) 48 | }) 49 | 50 | describe('GET /404', function(){ 51 | it('should respond with 404', function(done){ 52 | request(app) 53 | .get('/404') 54 | .set('Accept','application/json') 55 | .expect(404, { error: 'Not found' }, done) 56 | }) 57 | }) 58 | 59 | describe('GET /500', function(){ 60 | it('should respond with 500', function(done){ 61 | request(app) 62 | .get('/500') 63 | .set('Accept', 'application/json') 64 | .expect(500, done) 65 | }) 66 | }) 67 | }) 68 | 69 | 70 | describe('Accept: text/plain',function(){ 71 | describe('GET /403', function(){ 72 | it('should respond with 403', function(done){ 73 | request(app) 74 | .get('/403') 75 | .set('Accept','text/plain') 76 | .expect(403, done) 77 | }) 78 | }) 79 | 80 | describe('GET /404', function(){ 81 | it('should respond with 404', function(done){ 82 | request(app) 83 | .get('/404') 84 | .set('Accept', 'text/plain') 85 | .expect(404) 86 | .expect('Not found', done); 87 | }) 88 | }) 89 | 90 | describe('GET /500', function(){ 91 | it('should respond with 500', function(done){ 92 | request(app) 93 | .get('/500') 94 | .set('Accept','text/plain') 95 | .expect(500, done) 96 | }) 97 | }) 98 | }) 99 | }) 100 | -------------------------------------------------------------------------------- /test/acceptance/error.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/error') 3 | , request = require('supertest'); 4 | 5 | describe('error', function(){ 6 | describe('GET /', function(){ 7 | it('should respond with 500', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect(500,done) 11 | }) 12 | }) 13 | 14 | describe('GET /next', function(){ 15 | it('should respond with 500', function(done){ 16 | request(app) 17 | .get('/next') 18 | .expect(500,done) 19 | }) 20 | }) 21 | 22 | describe('GET /missing', function(){ 23 | it('should respond with 404', function(done){ 24 | request(app) 25 | .get('/missing') 26 | .expect(404,done) 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/acceptance/hello-world.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/hello-world') 3 | var request = require('supertest') 4 | 5 | describe('hello-world', function () { 6 | describe('GET /', function () { 7 | it('should respond with hello world', function (done) { 8 | request(app) 9 | .get('/') 10 | .expect(200, 'Hello World', done) 11 | }) 12 | }) 13 | 14 | describe('GET /missing', function () { 15 | it('should respond with 404', function (done) { 16 | request(app) 17 | .get('/missing') 18 | .expect(404, done) 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/acceptance/markdown.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/markdown') 3 | var request = require('supertest') 4 | 5 | describe('markdown', function(){ 6 | describe('GET /', function(){ 7 | it('should respond with html', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect(/]*>Markdown Example<\/h1>/,done) 11 | }) 12 | }) 13 | 14 | describe('GET /fail',function(){ 15 | it('should respond with an error', function(done){ 16 | request(app) 17 | .get('/fail') 18 | .expect(500,done) 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/acceptance/multi-router.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/multi-router') 2 | var request = require('supertest') 3 | 4 | describe('multi-router', function(){ 5 | describe('GET /',function(){ 6 | it('should respond with root handler', function(done){ 7 | request(app) 8 | .get('/') 9 | .expect(200, 'Hello from root route.', done) 10 | }) 11 | }) 12 | 13 | describe('GET /api/v1/',function(){ 14 | it('should respond with APIv1 root handler', function(done){ 15 | request(app) 16 | .get('/api/v1/') 17 | .expect(200, 'Hello from APIv1 root route.', done) 18 | }) 19 | }) 20 | 21 | describe('GET /api/v1/users',function(){ 22 | it('should respond with users from APIv1', function(done){ 23 | request(app) 24 | .get('/api/v1/users') 25 | .expect(200, 'List of APIv1 users.', done) 26 | }) 27 | }) 28 | 29 | describe('GET /api/v2/',function(){ 30 | it('should respond with APIv2 root handler', function(done){ 31 | request(app) 32 | .get('/api/v2/') 33 | .expect(200, 'Hello from APIv2 root route.', done) 34 | }) 35 | }) 36 | 37 | describe('GET /api/v2/users',function(){ 38 | it('should respond with users from APIv2', function(done){ 39 | request(app) 40 | .get('/api/v2/users') 41 | .expect(200, 'List of APIv2 users.', done) 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /test/acceptance/params.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/params') 2 | var request = require('supertest') 3 | 4 | describe('params', function(){ 5 | describe('GET /', function(){ 6 | it('should respond with instructions', function(done){ 7 | request(app) 8 | .get('/') 9 | .expect(/Visit/,done) 10 | }) 11 | }) 12 | 13 | describe('GET /user/0', function(){ 14 | it('should respond with a user', function(done){ 15 | request(app) 16 | .get('/user/0') 17 | .expect(/user tj/,done) 18 | }) 19 | }) 20 | 21 | describe('GET /user/9', function(){ 22 | it('should fail to find user', function(done){ 23 | request(app) 24 | .get('/user/9') 25 | .expect(404, /failed to find user/, done) 26 | }) 27 | }) 28 | 29 | describe('GET /users/0-2', function(){ 30 | it('should respond with three users', function(done){ 31 | request(app) 32 | .get('/users/0-2') 33 | .expect(/users tj, tobi, loki/, done) 34 | }) 35 | }) 36 | 37 | describe('GET /users/foo-bar', function(){ 38 | it('should fail integer parsing', function(done){ 39 | request(app) 40 | .get('/users/foo-bar') 41 | .expect(400, /failed to parseInt foo/, done) 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /test/acceptance/resource.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/resource') 2 | var request = require('supertest') 3 | 4 | describe('resource', function(){ 5 | describe('GET /', function(){ 6 | it('should respond with instructions', function(done){ 7 | request(app) 8 | .get('/') 9 | .expect(/^

    Examples:<\/h1>/,done) 10 | }) 11 | }) 12 | 13 | describe('GET /users', function(){ 14 | it('should respond with all users', function(done){ 15 | request(app) 16 | .get('/users') 17 | .expect(/^\[{"name":"tj"},{"name":"ciaran"},{"name":"aaron"},{"name":"guillermo"},{"name":"simon"},{"name":"tobi"}\]/,done) 18 | }) 19 | }) 20 | 21 | describe('GET /users/1', function(){ 22 | it('should respond with user 1', function(done){ 23 | request(app) 24 | .get('/users/1') 25 | .expect(/^{"name":"ciaran"}/,done) 26 | }) 27 | }) 28 | 29 | describe('GET /users/9', function(){ 30 | it('should respond with error', function(done){ 31 | request(app) 32 | .get('/users/9') 33 | .expect('{"error":"Cannot find user"}', done) 34 | }) 35 | }) 36 | 37 | describe('GET /users/1..3', function(){ 38 | it('should respond with users 1 through 3', function(done){ 39 | request(app) 40 | .get('/users/1..3') 41 | .expect(/^
    • ciaran<\/li>\n
    • aaron<\/li>\n
    • guillermo<\/li><\/ul>/,done) 42 | }) 43 | }) 44 | 45 | describe('DELETE /users/1', function(){ 46 | it('should delete user 1', function(done){ 47 | request(app) 48 | .del('/users/1') 49 | .expect(/^destroyed/,done) 50 | }) 51 | }) 52 | 53 | describe('DELETE /users/9', function(){ 54 | it('should fail', function(done){ 55 | request(app) 56 | .del('/users/9') 57 | .expect('Cannot find user', done) 58 | }) 59 | }) 60 | 61 | describe('GET /users/1..3.json', function(){ 62 | it('should respond with users 2 and 3 as json', function(done){ 63 | request(app) 64 | .get('/users/1..3.json') 65 | .expect(/^\[null,{"name":"aaron"},{"name":"guillermo"}\]/,done) 66 | }) 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /test/acceptance/route-map.js: -------------------------------------------------------------------------------- 1 | 2 | var request = require('supertest') 3 | , app = require('../../examples/route-map'); 4 | 5 | describe('route-map', function(){ 6 | describe('GET /users', function(){ 7 | it('should respond with users', function(done){ 8 | request(app) 9 | .get('/users') 10 | .expect('user list', done); 11 | }) 12 | }) 13 | 14 | describe('DELETE /users', function(){ 15 | it('should delete users', function(done){ 16 | request(app) 17 | .del('/users') 18 | .expect('delete users', done); 19 | }) 20 | }) 21 | 22 | describe('GET /users/:id', function(){ 23 | it('should get a user', function(done){ 24 | request(app) 25 | .get('/users/12') 26 | .expect('user 12', done); 27 | }) 28 | }) 29 | 30 | describe('GET /users/:id/pets', function(){ 31 | it('should get a users pets', function(done){ 32 | request(app) 33 | .get('/users/12/pets') 34 | .expect('user 12\'s pets', done); 35 | }) 36 | }) 37 | 38 | describe('GET /users/:id/pets/:pid', function(){ 39 | it('should get a users pet', function(done){ 40 | request(app) 41 | .del('/users/12/pets/2') 42 | .expect('delete 12\'s pet 2', done); 43 | }) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /test/acceptance/route-separation.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/route-separation') 3 | var request = require('supertest') 4 | 5 | describe('route-separation', function () { 6 | describe('GET /', function () { 7 | it('should respond with index', function (done) { 8 | request(app) 9 | .get('/') 10 | .expect(200, /Route Separation Example/, done) 11 | }) 12 | }) 13 | 14 | describe('GET /users', function () { 15 | it('should list users', function (done) { 16 | request(app) 17 | .get('/users') 18 | .expect(/TJ/) 19 | .expect(/Tobi/) 20 | .expect(200, done) 21 | }) 22 | }) 23 | 24 | describe('GET /user/:id', function () { 25 | it('should get a user', function (done) { 26 | request(app) 27 | .get('/user/0') 28 | .expect(200, /Viewing user TJ/, done) 29 | }) 30 | 31 | it('should 404 on missing user', function (done) { 32 | request(app) 33 | .get('/user/10') 34 | .expect(404, done) 35 | }) 36 | }) 37 | 38 | describe('GET /user/:id/view', function () { 39 | it('should get a user', function (done) { 40 | request(app) 41 | .get('/user/0/view') 42 | .expect(200, /Viewing user TJ/, done) 43 | }) 44 | 45 | it('should 404 on missing user', function (done) { 46 | request(app) 47 | .get('/user/10/view') 48 | .expect(404, done) 49 | }) 50 | }) 51 | 52 | describe('GET /user/:id/edit', function () { 53 | it('should get a user to edit', function (done) { 54 | request(app) 55 | .get('/user/0/edit') 56 | .expect(200, /Editing user TJ/, done) 57 | }) 58 | }) 59 | 60 | describe('PUT /user/:id/edit', function () { 61 | it('should edit a user', function (done) { 62 | request(app) 63 | .put('/user/0/edit') 64 | .set('Content-Type', 'application/x-www-form-urlencoded') 65 | .send({ user: { name: 'TJ', email: 'tj-invalid@vision-media.ca' } }) 66 | .expect(302, function (err) { 67 | if (err) return done(err) 68 | request(app) 69 | .get('/user/0') 70 | .expect(200, /tj-invalid@vision-media\.ca/, done) 71 | }) 72 | }) 73 | }) 74 | 75 | describe('POST /user/:id/edit?_method=PUT', function () { 76 | it('should edit a user', function (done) { 77 | request(app) 78 | .post('/user/1/edit?_method=PUT') 79 | .set('Content-Type', 'application/x-www-form-urlencoded') 80 | .send({ user: { name: 'Tobi', email: 'tobi-invalid@vision-media.ca' } }) 81 | .expect(302, function (err) { 82 | if (err) return done(err) 83 | request(app) 84 | .get('/user/1') 85 | .expect(200, /tobi-invalid@vision-media\.ca/, done) 86 | }) 87 | }) 88 | }) 89 | 90 | describe('GET /posts', function () { 91 | it('should get a list of posts', function (done) { 92 | request(app) 93 | .get('/posts') 94 | .expect(200, /Posts/, done) 95 | }) 96 | }) 97 | }) 98 | -------------------------------------------------------------------------------- /test/acceptance/vhost.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/vhost') 2 | var request = require('supertest') 3 | 4 | describe('vhost', function(){ 5 | describe('example.com', function(){ 6 | describe('GET /', function(){ 7 | it('should say hello', function(done){ 8 | request(app) 9 | .get('/') 10 | .set('Host', 'example.com') 11 | .expect(200, /hello/i, done) 12 | }) 13 | }) 14 | 15 | describe('GET /foo', function(){ 16 | it('should say foo', function(done){ 17 | request(app) 18 | .get('/foo') 19 | .set('Host', 'example.com') 20 | .expect(200, 'requested foo', done) 21 | }) 22 | }) 23 | }) 24 | 25 | describe('foo.example.com', function(){ 26 | describe('GET /', function(){ 27 | it('should redirect to /foo', function(done){ 28 | request(app) 29 | .get('/') 30 | .set('Host', 'foo.example.com') 31 | .expect(302, /Redirecting to http:\/\/example.com:3000\/foo/, done) 32 | }) 33 | }) 34 | }) 35 | 36 | describe('bar.example.com', function(){ 37 | describe('GET /', function(){ 38 | it('should redirect to /bar', function(done){ 39 | request(app) 40 | .get('/') 41 | .set('Host', 'bar.example.com') 42 | .expect(302, /Redirecting to http:\/\/example.com:3000\/bar/, done) 43 | }) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/acceptance/web-service.js: -------------------------------------------------------------------------------- 1 | 2 | var request = require('supertest') 3 | , app = require('../../examples/web-service'); 4 | 5 | describe('web-service', function(){ 6 | describe('GET /api/users', function(){ 7 | describe('without an api key', function(){ 8 | it('should respond with 400 bad request', function(done){ 9 | request(app) 10 | .get('/api/users') 11 | .expect(400, done); 12 | }) 13 | }) 14 | 15 | describe('with an invalid api key', function(){ 16 | it('should respond with 401 unauthorized', function(done){ 17 | request(app) 18 | .get('/api/users?api-key=rawr') 19 | .expect(401, done); 20 | }) 21 | }) 22 | 23 | describe('with a valid api key', function(){ 24 | it('should respond users json', function(done){ 25 | request(app) 26 | .get('/api/users?api-key=foo') 27 | .expect('Content-Type', 'application/json; charset=utf-8') 28 | .expect(200, '[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]', done) 29 | }) 30 | }) 31 | }) 32 | 33 | describe('GET /api/repos', function(){ 34 | describe('without an api key', function(){ 35 | it('should respond with 400 bad request', function(done){ 36 | request(app) 37 | .get('/api/repos') 38 | .expect(400, done); 39 | }) 40 | }) 41 | 42 | describe('with an invalid api key', function(){ 43 | it('should respond with 401 unauthorized', function(done){ 44 | request(app) 45 | .get('/api/repos?api-key=rawr') 46 | .expect(401, done); 47 | }) 48 | }) 49 | 50 | describe('with a valid api key', function(){ 51 | it('should respond repos json', function(done){ 52 | request(app) 53 | .get('/api/repos?api-key=foo') 54 | .expect('Content-Type', 'application/json; charset=utf-8') 55 | .expect(/"name":"express"/) 56 | .expect(/"url":"https:\/\/github.com\/expressjs\/express"/) 57 | .expect(200, done) 58 | }) 59 | }) 60 | }) 61 | 62 | describe('GET /api/user/:name/repos', function(){ 63 | describe('without an api key', function(){ 64 | it('should respond with 400 bad request', function(done){ 65 | request(app) 66 | .get('/api/user/loki/repos') 67 | .expect(400, done); 68 | }) 69 | }) 70 | 71 | describe('with an invalid api key', function(){ 72 | it('should respond with 401 unauthorized', function(done){ 73 | request(app) 74 | .get('/api/user/loki/repos?api-key=rawr') 75 | .expect(401, done); 76 | }) 77 | }) 78 | 79 | describe('with a valid api key', function(){ 80 | it('should respond user repos json', function(done){ 81 | request(app) 82 | .get('/api/user/loki/repos?api-key=foo') 83 | .expect('Content-Type', 'application/json; charset=utf-8') 84 | .expect(/"name":"stylus"/) 85 | .expect(/"url":"https:\/\/github.com\/learnboost\/stylus"/) 86 | .expect(200, done) 87 | }) 88 | 89 | it('should 404 with unknown user', function(done){ 90 | request(app) 91 | .get('/api/user/bob/repos?api-key=foo') 92 | .expect(404, done) 93 | }) 94 | }) 95 | }) 96 | 97 | describe('when requesting an invalid route', function(){ 98 | it('should respond with 404 json', function(done){ 99 | request(app) 100 | .get('/api/something?api-key=bar') 101 | .expect('Content-Type', /json/) 102 | .expect(404, '{"error":"Sorry, can\'t find that"}', done) 103 | }) 104 | }) 105 | }) 106 | -------------------------------------------------------------------------------- /test/app.all.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var after = require('after') 4 | var express = require('../') 5 | , request = require('supertest'); 6 | 7 | describe('app.all()', function(){ 8 | it('should add a router per method', function(done){ 9 | var app = express(); 10 | var cb = after(2, done) 11 | 12 | app.all('/tobi', function(req, res){ 13 | res.end(req.method); 14 | }); 15 | 16 | request(app) 17 | .put('/tobi') 18 | .expect(200, 'PUT', cb) 19 | 20 | request(app) 21 | .get('/tobi') 22 | .expect(200, 'GET', cb) 23 | }) 24 | 25 | it('should run the callback for a method just once', function(done){ 26 | var app = express() 27 | , n = 0; 28 | 29 | app.all('/*splat', function(req, res, next){ 30 | if (n++) return done(new Error('DELETE called several times')); 31 | next(); 32 | }); 33 | 34 | request(app) 35 | .del('/tobi') 36 | .expect(404, done); 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/app.engine.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert') 4 | var express = require('../') 5 | , fs = require('node:fs'); 6 | var path = require('node:path') 7 | 8 | function render(path, options, fn) { 9 | fs.readFile(path, 'utf8', function(err, str){ 10 | if (err) return fn(err); 11 | str = str.replace('{{user.name}}', options.user.name); 12 | fn(null, str); 13 | }); 14 | } 15 | 16 | describe('app', function(){ 17 | describe('.engine(ext, fn)', function(){ 18 | it('should map a template engine', function(done){ 19 | var app = express(); 20 | 21 | app.set('views', path.join(__dirname, 'fixtures')) 22 | app.engine('.html', render); 23 | app.locals.user = { name: 'tobi' }; 24 | 25 | app.render('user.html', function(err, str){ 26 | if (err) return done(err); 27 | assert.strictEqual(str, '

      tobi

      ') 28 | done(); 29 | }) 30 | }) 31 | 32 | it('should throw when the callback is missing', function(){ 33 | var app = express(); 34 | assert.throws(function () { 35 | app.engine('.html', null); 36 | }, /callback function required/) 37 | }) 38 | 39 | it('should work without leading "."', function(done){ 40 | var app = express(); 41 | 42 | app.set('views', path.join(__dirname, 'fixtures')) 43 | app.engine('html', render); 44 | app.locals.user = { name: 'tobi' }; 45 | 46 | app.render('user.html', function(err, str){ 47 | if (err) return done(err); 48 | assert.strictEqual(str, '

      tobi

      ') 49 | done(); 50 | }) 51 | }) 52 | 53 | it('should work "view engine" setting', function(done){ 54 | var app = express(); 55 | 56 | app.set('views', path.join(__dirname, 'fixtures')) 57 | app.engine('html', render); 58 | app.set('view engine', 'html'); 59 | app.locals.user = { name: 'tobi' }; 60 | 61 | app.render('user', function(err, str){ 62 | if (err) return done(err); 63 | assert.strictEqual(str, '

      tobi

      ') 64 | done(); 65 | }) 66 | }) 67 | 68 | it('should work "view engine" with leading "."', function(done){ 69 | var app = express(); 70 | 71 | app.set('views', path.join(__dirname, 'fixtures')) 72 | app.engine('.html', render); 73 | app.set('view engine', '.html'); 74 | app.locals.user = { name: 'tobi' }; 75 | 76 | app.render('user', function(err, str){ 77 | if (err) return done(err); 78 | assert.strictEqual(str, '

      tobi

      ') 79 | done(); 80 | }) 81 | }) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /test/app.head.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../'); 4 | var request = require('supertest'); 5 | var assert = require('node:assert'); 6 | 7 | describe('HEAD', function(){ 8 | it('should default to GET', function(done){ 9 | var app = express(); 10 | 11 | app.get('/tobi', function(req, res){ 12 | // send() detects HEAD 13 | res.send('tobi'); 14 | }); 15 | 16 | request(app) 17 | .head('/tobi') 18 | .expect(200, done); 19 | }) 20 | 21 | it('should output the same headers as GET requests', function(done){ 22 | var app = express(); 23 | 24 | app.get('/tobi', function(req, res){ 25 | // send() detects HEAD 26 | res.send('tobi'); 27 | }); 28 | 29 | request(app) 30 | .head('/tobi') 31 | .expect(200, function(err, res){ 32 | if (err) return done(err); 33 | var headers = res.headers; 34 | request(app) 35 | .get('/tobi') 36 | .expect(200, function(err, res){ 37 | if (err) return done(err); 38 | delete headers.date; 39 | delete res.headers.date; 40 | assert.deepEqual(res.headers, headers); 41 | done(); 42 | }); 43 | }); 44 | }) 45 | }) 46 | 47 | describe('app.head()', function(){ 48 | it('should override', function(done){ 49 | var app = express() 50 | 51 | app.head('/tobi', function(req, res){ 52 | res.header('x-method', 'head') 53 | res.end() 54 | }); 55 | 56 | app.get('/tobi', function(req, res){ 57 | res.header('x-method', 'get') 58 | res.send('tobi'); 59 | }); 60 | 61 | request(app) 62 | .head('/tobi') 63 | .expect('x-method', 'head') 64 | .expect(200, done) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /test/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert') 4 | var express = require('..') 5 | var request = require('supertest') 6 | 7 | describe('app', function(){ 8 | it('should inherit from event emitter', function(done){ 9 | var app = express(); 10 | app.on('foo', done); 11 | app.emit('foo'); 12 | }) 13 | 14 | it('should be callable', function(){ 15 | var app = express(); 16 | assert.equal(typeof app, 'function'); 17 | }) 18 | 19 | it('should 404 without routes', function(done){ 20 | request(express()) 21 | .get('/') 22 | .expect(404, done); 23 | }) 24 | }) 25 | 26 | describe('app.parent', function(){ 27 | it('should return the parent when mounted', function(){ 28 | var app = express() 29 | , blog = express() 30 | , blogAdmin = express(); 31 | 32 | app.use('/blog', blog); 33 | blog.use('/admin', blogAdmin); 34 | 35 | assert(!app.parent, 'app.parent'); 36 | assert.strictEqual(blog.parent, app) 37 | assert.strictEqual(blogAdmin.parent, blog) 38 | }) 39 | }) 40 | 41 | describe('app.mountpath', function(){ 42 | it('should return the mounted path', function(){ 43 | var admin = express(); 44 | var app = express(); 45 | var blog = express(); 46 | var fallback = express(); 47 | 48 | app.use('/blog', blog); 49 | app.use(fallback); 50 | blog.use('/admin', admin); 51 | 52 | assert.strictEqual(admin.mountpath, '/admin') 53 | assert.strictEqual(app.mountpath, '/') 54 | assert.strictEqual(blog.mountpath, '/blog') 55 | assert.strictEqual(fallback.mountpath, '/') 56 | }) 57 | }) 58 | 59 | describe('app.path()', function(){ 60 | it('should return the canonical', function(){ 61 | var app = express() 62 | , blog = express() 63 | , blogAdmin = express(); 64 | 65 | app.use('/blog', blog); 66 | blog.use('/admin', blogAdmin); 67 | 68 | assert.strictEqual(app.path(), '') 69 | assert.strictEqual(blog.path(), '/blog') 70 | assert.strictEqual(blogAdmin.path(), '/blog/admin') 71 | }) 72 | }) 73 | 74 | describe('in development', function(){ 75 | before(function () { 76 | this.env = process.env.NODE_ENV 77 | process.env.NODE_ENV = 'development' 78 | }) 79 | 80 | after(function () { 81 | process.env.NODE_ENV = this.env 82 | }) 83 | 84 | it('should disable "view cache"', function(){ 85 | var app = express(); 86 | assert.ok(!app.enabled('view cache')) 87 | }) 88 | }) 89 | 90 | describe('in production', function(){ 91 | before(function () { 92 | this.env = process.env.NODE_ENV 93 | process.env.NODE_ENV = 'production' 94 | }) 95 | 96 | after(function () { 97 | process.env.NODE_ENV = this.env 98 | }) 99 | 100 | it('should enable "view cache"', function(){ 101 | var app = express(); 102 | assert.ok(app.enabled('view cache')) 103 | }) 104 | }) 105 | 106 | describe('without NODE_ENV', function(){ 107 | before(function () { 108 | this.env = process.env.NODE_ENV 109 | process.env.NODE_ENV = '' 110 | }) 111 | 112 | after(function () { 113 | process.env.NODE_ENV = this.env 114 | }) 115 | 116 | it('should default to development', function(){ 117 | var app = express(); 118 | assert.strictEqual(app.get('env'), 'development') 119 | }) 120 | }) 121 | -------------------------------------------------------------------------------- /test/app.listen.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | var assert = require('node:assert') 5 | 6 | describe('app.listen()', function(){ 7 | it('should wrap with an HTTP server', function(done){ 8 | var app = express(); 9 | 10 | var server = app.listen(0, function () { 11 | server.close(done) 12 | }); 13 | }) 14 | it('should callback on HTTP server errors', function (done) { 15 | var app1 = express() 16 | var app2 = express() 17 | 18 | var server1 = app1.listen(0, function (err) { 19 | assert(!err) 20 | app2.listen(server1.address().port, function (err) { 21 | assert(err.code === 'EADDRINUSE') 22 | server1.close() 23 | done() 24 | }) 25 | }) 26 | }) 27 | it('accepts port + hostname + backlog + callback', function (done) { 28 | const app = express(); 29 | const server = app.listen(0, '127.0.0.1', 5, function () { 30 | const { address, port } = server.address(); 31 | assert.strictEqual(address, '127.0.0.1'); 32 | assert(Number.isInteger(port) && port > 0); 33 | // backlog isn’t directly inspectable, but if no error was thrown 34 | // we know it was accepted. 35 | server.close(done); 36 | }); 37 | }); 38 | it('accepts just a callback (no args)', function (done) { 39 | const app = express(); 40 | // same as app.listen(0, done) 41 | const server = app.listen(); 42 | server.close(done); 43 | }); 44 | it('server.address() gives a { address, port, family } object', function (done) { 45 | const app = express(); 46 | const server = app.listen(0, () => { 47 | const addr = server.address(); 48 | assert(addr && typeof addr === 'object'); 49 | assert.strictEqual(typeof addr.address, 'string'); 50 | assert(Number.isInteger(addr.port) && addr.port > 0); 51 | assert(typeof addr.family === 'string'); 52 | server.close(done); 53 | }); 54 | }); 55 | }) 56 | -------------------------------------------------------------------------------- /test/app.locals.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert') 4 | var express = require('../') 5 | 6 | describe('app', function(){ 7 | describe('.locals', function () { 8 | it('should default object with null prototype', function () { 9 | var app = express() 10 | assert.ok(app.locals) 11 | assert.strictEqual(typeof app.locals, 'object') 12 | assert.strictEqual(Object.getPrototypeOf(app.locals), null) 13 | }) 14 | 15 | describe('.settings', function () { 16 | it('should contain app settings ', function () { 17 | var app = express() 18 | app.set('title', 'Express') 19 | assert.ok(app.locals.settings) 20 | assert.strictEqual(typeof app.locals.settings, 'object') 21 | assert.strictEqual(app.locals.settings, app.settings) 22 | assert.strictEqual(app.locals.settings.title, 'Express') 23 | }) 24 | }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/app.options.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('OPTIONS', function(){ 7 | it('should default to the routes defined', function(done){ 8 | var app = express(); 9 | 10 | app.post('/', function(){}); 11 | app.get('/users', function(req, res){}); 12 | app.put('/users', function(req, res){}); 13 | 14 | request(app) 15 | .options('/users') 16 | .expect('Allow', 'GET, HEAD, PUT') 17 | .expect(200, 'GET, HEAD, PUT', done); 18 | }) 19 | 20 | it('should only include each method once', function(done){ 21 | var app = express(); 22 | 23 | app.delete('/', function(){}); 24 | app.get('/users', function(req, res){}); 25 | app.put('/users', function(req, res){}); 26 | app.get('/users', function(req, res){}); 27 | 28 | request(app) 29 | .options('/users') 30 | .expect('Allow', 'GET, HEAD, PUT') 31 | .expect(200, 'GET, HEAD, PUT', done); 32 | }) 33 | 34 | it('should not be affected by app.all', function(done){ 35 | var app = express(); 36 | 37 | app.get('/', function(){}); 38 | app.get('/users', function(req, res){}); 39 | app.put('/users', function(req, res){}); 40 | app.all('/users', function(req, res, next){ 41 | res.setHeader('x-hit', '1'); 42 | next(); 43 | }); 44 | 45 | request(app) 46 | .options('/users') 47 | .expect('x-hit', '1') 48 | .expect('Allow', 'GET, HEAD, PUT') 49 | .expect(200, 'GET, HEAD, PUT', done); 50 | }) 51 | 52 | it('should not respond if the path is not defined', function(done){ 53 | var app = express(); 54 | 55 | app.get('/users', function(req, res){}); 56 | 57 | request(app) 58 | .options('/other') 59 | .expect(404, done); 60 | }) 61 | 62 | it('should forward requests down the middleware chain', function(done){ 63 | var app = express(); 64 | var router = new express.Router(); 65 | 66 | router.get('/users', function(req, res){}); 67 | app.use(router); 68 | app.get('/other', function(req, res){}); 69 | 70 | request(app) 71 | .options('/other') 72 | .expect('Allow', 'GET, HEAD') 73 | .expect(200, 'GET, HEAD', done); 74 | }) 75 | 76 | describe('when error occurs in response handler', function () { 77 | it('should pass error to callback', function (done) { 78 | var app = express(); 79 | var router = express.Router(); 80 | 81 | router.get('/users', function(req, res){}); 82 | 83 | app.use(function (req, res, next) { 84 | res.writeHead(200); 85 | next(); 86 | }); 87 | app.use(router); 88 | app.use(function (err, req, res, next) { 89 | res.end('true'); 90 | }); 91 | 92 | request(app) 93 | .options('/users') 94 | .expect(200, 'true', done) 95 | }) 96 | }) 97 | }) 98 | 99 | describe('app.options()', function(){ 100 | it('should override the default behavior', function(done){ 101 | var app = express(); 102 | 103 | app.options('/users', function(req, res){ 104 | res.set('Allow', 'GET'); 105 | res.send('GET'); 106 | }); 107 | 108 | app.get('/users', function(req, res){}); 109 | app.put('/users', function(req, res){}); 110 | 111 | request(app) 112 | .options('/users') 113 | .expect('GET') 114 | .expect('Allow', 'GET', done); 115 | }) 116 | }) 117 | -------------------------------------------------------------------------------- /test/app.request.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var after = require('after') 4 | var express = require('../') 5 | , request = require('supertest'); 6 | 7 | describe('app', function(){ 8 | describe('.request', function(){ 9 | it('should extend the request prototype', function(done){ 10 | var app = express(); 11 | 12 | app.request.querystring = function(){ 13 | return require('node:url').parse(this.url).query; 14 | }; 15 | 16 | app.use(function(req, res){ 17 | res.end(req.querystring()); 18 | }); 19 | 20 | request(app) 21 | .get('/foo?name=tobi') 22 | .expect('name=tobi', done); 23 | }) 24 | 25 | it('should only extend for the referenced app', function (done) { 26 | var app1 = express() 27 | var app2 = express() 28 | var cb = after(2, done) 29 | 30 | app1.request.foobar = function () { 31 | return 'tobi' 32 | } 33 | 34 | app1.get('/', function (req, res) { 35 | res.send(req.foobar()) 36 | }) 37 | 38 | app2.get('/', function (req, res) { 39 | res.send(req.foobar()) 40 | }) 41 | 42 | request(app1) 43 | .get('/') 44 | .expect(200, 'tobi', cb) 45 | 46 | request(app2) 47 | .get('/') 48 | .expect(500, /(?:not a function|has no method)/, cb) 49 | }) 50 | 51 | it('should inherit to sub apps', function (done) { 52 | var app1 = express() 53 | var app2 = express() 54 | var cb = after(2, done) 55 | 56 | app1.request.foobar = function () { 57 | return 'tobi' 58 | } 59 | 60 | app1.use('/sub', app2) 61 | 62 | app1.get('/', function (req, res) { 63 | res.send(req.foobar()) 64 | }) 65 | 66 | app2.get('/', function (req, res) { 67 | res.send(req.foobar()) 68 | }) 69 | 70 | request(app1) 71 | .get('/') 72 | .expect(200, 'tobi', cb) 73 | 74 | request(app1) 75 | .get('/sub') 76 | .expect(200, 'tobi', cb) 77 | }) 78 | 79 | it('should allow sub app to override', function (done) { 80 | var app1 = express() 81 | var app2 = express() 82 | var cb = after(2, done) 83 | 84 | app1.request.foobar = function () { 85 | return 'tobi' 86 | } 87 | 88 | app2.request.foobar = function () { 89 | return 'loki' 90 | } 91 | 92 | app1.use('/sub', app2) 93 | 94 | app1.get('/', function (req, res) { 95 | res.send(req.foobar()) 96 | }) 97 | 98 | app2.get('/', function (req, res) { 99 | res.send(req.foobar()) 100 | }) 101 | 102 | request(app1) 103 | .get('/') 104 | .expect(200, 'tobi', cb) 105 | 106 | request(app1) 107 | .get('/sub') 108 | .expect(200, 'loki', cb) 109 | }) 110 | 111 | it('should not pollute parent app', function (done) { 112 | var app1 = express() 113 | var app2 = express() 114 | var cb = after(2, done) 115 | 116 | app1.request.foobar = function () { 117 | return 'tobi' 118 | } 119 | 120 | app2.request.foobar = function () { 121 | return 'loki' 122 | } 123 | 124 | app1.use('/sub', app2) 125 | 126 | app1.get('/sub/foo', function (req, res) { 127 | res.send(req.foobar()) 128 | }) 129 | 130 | app2.get('/', function (req, res) { 131 | res.send(req.foobar()) 132 | }) 133 | 134 | request(app1) 135 | .get('/sub') 136 | .expect(200, 'loki', cb) 137 | 138 | request(app1) 139 | .get('/sub/foo') 140 | .expect(200, 'tobi', cb) 141 | }) 142 | }) 143 | }) 144 | -------------------------------------------------------------------------------- /test/app.response.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var after = require('after') 4 | var express = require('../') 5 | , request = require('supertest'); 6 | 7 | describe('app', function(){ 8 | describe('.response', function(){ 9 | it('should extend the response prototype', function(done){ 10 | var app = express(); 11 | 12 | app.response.shout = function(str){ 13 | this.send(str.toUpperCase()); 14 | }; 15 | 16 | app.use(function(req, res){ 17 | res.shout('hey'); 18 | }); 19 | 20 | request(app) 21 | .get('/') 22 | .expect('HEY', done); 23 | }) 24 | 25 | it('should only extend for the referenced app', function (done) { 26 | var app1 = express() 27 | var app2 = express() 28 | var cb = after(2, done) 29 | 30 | app1.response.shout = function (str) { 31 | this.send(str.toUpperCase()) 32 | } 33 | 34 | app1.get('/', function (req, res) { 35 | res.shout('foo') 36 | }) 37 | 38 | app2.get('/', function (req, res) { 39 | res.shout('foo') 40 | }) 41 | 42 | request(app1) 43 | .get('/') 44 | .expect(200, 'FOO', cb) 45 | 46 | request(app2) 47 | .get('/') 48 | .expect(500, /(?:not a function|has no method)/, cb) 49 | }) 50 | 51 | it('should inherit to sub apps', function (done) { 52 | var app1 = express() 53 | var app2 = express() 54 | var cb = after(2, done) 55 | 56 | app1.response.shout = function (str) { 57 | this.send(str.toUpperCase()) 58 | } 59 | 60 | app1.use('/sub', app2) 61 | 62 | app1.get('/', function (req, res) { 63 | res.shout('foo') 64 | }) 65 | 66 | app2.get('/', function (req, res) { 67 | res.shout('foo') 68 | }) 69 | 70 | request(app1) 71 | .get('/') 72 | .expect(200, 'FOO', cb) 73 | 74 | request(app1) 75 | .get('/sub') 76 | .expect(200, 'FOO', cb) 77 | }) 78 | 79 | it('should allow sub app to override', function (done) { 80 | var app1 = express() 81 | var app2 = express() 82 | var cb = after(2, done) 83 | 84 | app1.response.shout = function (str) { 85 | this.send(str.toUpperCase()) 86 | } 87 | 88 | app2.response.shout = function (str) { 89 | this.send(str + '!') 90 | } 91 | 92 | app1.use('/sub', app2) 93 | 94 | app1.get('/', function (req, res) { 95 | res.shout('foo') 96 | }) 97 | 98 | app2.get('/', function (req, res) { 99 | res.shout('foo') 100 | }) 101 | 102 | request(app1) 103 | .get('/') 104 | .expect(200, 'FOO', cb) 105 | 106 | request(app1) 107 | .get('/sub') 108 | .expect(200, 'foo!', cb) 109 | }) 110 | 111 | it('should not pollute parent app', function (done) { 112 | var app1 = express() 113 | var app2 = express() 114 | var cb = after(2, done) 115 | 116 | app1.response.shout = function (str) { 117 | this.send(str.toUpperCase()) 118 | } 119 | 120 | app2.response.shout = function (str) { 121 | this.send(str + '!') 122 | } 123 | 124 | app1.use('/sub', app2) 125 | 126 | app1.get('/sub/foo', function (req, res) { 127 | res.shout('foo') 128 | }) 129 | 130 | app2.get('/', function (req, res) { 131 | res.shout('foo') 132 | }) 133 | 134 | request(app1) 135 | .get('/sub') 136 | .expect(200, 'foo!', cb) 137 | 138 | request(app1) 139 | .get('/sub/foo') 140 | .expect(200, 'FOO', cb) 141 | }) 142 | }) 143 | }) 144 | -------------------------------------------------------------------------------- /test/app.routes.error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert') 4 | var express = require('../') 5 | , request = require('supertest'); 6 | 7 | describe('app', function(){ 8 | describe('.VERB()', function(){ 9 | it('should not get invoked without error handler on error', function(done) { 10 | var app = express(); 11 | 12 | app.use(function(req, res, next){ 13 | next(new Error('boom!')) 14 | }); 15 | 16 | app.get('/bar', function(req, res){ 17 | res.send('hello, world!'); 18 | }); 19 | 20 | request(app) 21 | .post('/bar') 22 | .expect(500, /Error: boom!/, done); 23 | }); 24 | 25 | it('should only call an error handling routing callback when an error is propagated', function(done){ 26 | var app = express(); 27 | 28 | var a = false; 29 | var b = false; 30 | var c = false; 31 | var d = false; 32 | 33 | app.get('/', function(req, res, next){ 34 | next(new Error('fabricated error')); 35 | }, function(req, res, next) { 36 | a = true; 37 | next(); 38 | }, function(err, req, res, next){ 39 | b = true; 40 | assert.strictEqual(err.message, 'fabricated error') 41 | next(err); 42 | }, function(err, req, res, next){ 43 | c = true; 44 | assert.strictEqual(err.message, 'fabricated error') 45 | next(); 46 | }, function(err, req, res, next){ 47 | d = true; 48 | next(); 49 | }, function(req, res){ 50 | assert.ok(!a) 51 | assert.ok(b) 52 | assert.ok(c) 53 | assert.ok(!d) 54 | res.sendStatus(204); 55 | }); 56 | 57 | request(app) 58 | .get('/') 59 | .expect(204, done); 60 | }) 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /test/exports.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert') 4 | var express = require('../'); 5 | var request = require('supertest'); 6 | 7 | describe('exports', function(){ 8 | it('should expose Router', function(){ 9 | assert.strictEqual(typeof express.Router, 'function') 10 | }) 11 | 12 | it('should expose json middleware', function () { 13 | assert.equal(typeof express.json, 'function') 14 | assert.equal(express.json.length, 1) 15 | }) 16 | 17 | it('should expose raw middleware', function () { 18 | assert.equal(typeof express.raw, 'function') 19 | assert.equal(express.raw.length, 1) 20 | }) 21 | 22 | it('should expose static middleware', function () { 23 | assert.equal(typeof express.static, 'function') 24 | assert.equal(express.static.length, 2) 25 | }) 26 | 27 | it('should expose text middleware', function () { 28 | assert.equal(typeof express.text, 'function') 29 | assert.equal(express.text.length, 1) 30 | }) 31 | 32 | it('should expose urlencoded middleware', function () { 33 | assert.equal(typeof express.urlencoded, 'function') 34 | assert.equal(express.urlencoded.length, 1) 35 | }) 36 | 37 | it('should expose the application prototype', function(){ 38 | assert.strictEqual(typeof express.application, 'object') 39 | assert.strictEqual(typeof express.application.set, 'function') 40 | }) 41 | 42 | it('should expose the request prototype', function(){ 43 | assert.strictEqual(typeof express.request, 'object') 44 | assert.strictEqual(typeof express.request.accepts, 'function') 45 | }) 46 | 47 | it('should expose the response prototype', function(){ 48 | assert.strictEqual(typeof express.response, 'object') 49 | assert.strictEqual(typeof express.response.send, 'function') 50 | }) 51 | 52 | it('should permit modifying the .application prototype', function(){ 53 | express.application.foo = function(){ return 'bar'; }; 54 | assert.strictEqual(express().foo(), 'bar') 55 | }) 56 | 57 | it('should permit modifying the .request prototype', function(done){ 58 | express.request.foo = function(){ return 'bar'; }; 59 | var app = express(); 60 | 61 | app.use(function(req, res, next){ 62 | res.end(req.foo()); 63 | }); 64 | 65 | request(app) 66 | .get('/') 67 | .expect('bar', done); 68 | }) 69 | 70 | it('should permit modifying the .response prototype', function(done){ 71 | express.response.foo = function(){ this.send('bar'); }; 72 | var app = express(); 73 | 74 | app.use(function(req, res, next){ 75 | res.foo(); 76 | }); 77 | 78 | request(app) 79 | .get('/') 80 | .expect('bar', done); 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /test/fixtures/% of dogs.txt: -------------------------------------------------------------------------------- 1 | 20% -------------------------------------------------------------------------------- /test/fixtures/.name: -------------------------------------------------------------------------------- 1 | tobi -------------------------------------------------------------------------------- /test/fixtures/blog/index.html: -------------------------------------------------------------------------------- 1 | index -------------------------------------------------------------------------------- /test/fixtures/blog/post/index.tmpl: -------------------------------------------------------------------------------- 1 |

      blog post

      -------------------------------------------------------------------------------- /test/fixtures/broken.send: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/expressjs/express/b8ab46594da8d2626c59ba36f76264ad980c533d/test/fixtures/broken.send -------------------------------------------------------------------------------- /test/fixtures/default_layout/name.tmpl: -------------------------------------------------------------------------------- 1 |

      $name

      -------------------------------------------------------------------------------- /test/fixtures/default_layout/user.tmpl: -------------------------------------------------------------------------------- 1 |

      $user.name

      -------------------------------------------------------------------------------- /test/fixtures/email.tmpl: -------------------------------------------------------------------------------- 1 |

      This is an email

      -------------------------------------------------------------------------------- /test/fixtures/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/expressjs/express/b8ab46594da8d2626c59ba36f76264ad980c533d/test/fixtures/empty.txt -------------------------------------------------------------------------------- /test/fixtures/local_layout/user.tmpl: -------------------------------------------------------------------------------- 1 | $user.name -------------------------------------------------------------------------------- /test/fixtures/name.tmpl: -------------------------------------------------------------------------------- 1 |

      $name

      -------------------------------------------------------------------------------- /test/fixtures/name.txt: -------------------------------------------------------------------------------- 1 | tobi -------------------------------------------------------------------------------- /test/fixtures/nums.txt: -------------------------------------------------------------------------------- 1 | 123456789 -------------------------------------------------------------------------------- /test/fixtures/pets/names.txt: -------------------------------------------------------------------------------- 1 | tobi,loki -------------------------------------------------------------------------------- /test/fixtures/snow ☃/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/expressjs/express/b8ab46594da8d2626c59ba36f76264ad980c533d/test/fixtures/snow ☃/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/todo.html: -------------------------------------------------------------------------------- 1 |
    • groceries
    • -------------------------------------------------------------------------------- /test/fixtures/todo.txt: -------------------------------------------------------------------------------- 1 | - groceries -------------------------------------------------------------------------------- /test/fixtures/user.html: -------------------------------------------------------------------------------- 1 |

      {{user.name}}

      -------------------------------------------------------------------------------- /test/fixtures/user.tmpl: -------------------------------------------------------------------------------- 1 |

      $user.name

      -------------------------------------------------------------------------------- /test/fixtures/users/index.html: -------------------------------------------------------------------------------- 1 |

      tobi, loki, jane

      -------------------------------------------------------------------------------- /test/fixtures/users/tobi.txt: -------------------------------------------------------------------------------- 1 | ferret -------------------------------------------------------------------------------- /test/middleware.basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert') 4 | var express = require('../'); 5 | var request = require('supertest'); 6 | 7 | describe('middleware', function(){ 8 | describe('.next()', function(){ 9 | it('should behave like connect', function(done){ 10 | var app = express() 11 | , calls = []; 12 | 13 | app.use(function(req, res, next){ 14 | calls.push('one'); 15 | next(); 16 | }); 17 | 18 | app.use(function(req, res, next){ 19 | calls.push('two'); 20 | next(); 21 | }); 22 | 23 | app.use(function(req, res){ 24 | var buf = ''; 25 | res.setHeader('Content-Type', 'application/json'); 26 | req.setEncoding('utf8'); 27 | req.on('data', function(chunk){ buf += chunk }); 28 | req.on('end', function(){ 29 | res.end(buf); 30 | }); 31 | }); 32 | 33 | request(app) 34 | .get('/') 35 | .set('Content-Type', 'application/json') 36 | .send('{"foo":"bar"}') 37 | .expect('Content-Type', 'application/json') 38 | .expect(function () { assert.deepEqual(calls, ['one', 'two']) }) 39 | .expect(200, '{"foo":"bar"}', done) 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test/regression.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('throw after .end()', function(){ 7 | it('should fail gracefully', function(done){ 8 | var app = express(); 9 | 10 | app.get('/', function(req, res){ 11 | res.end('yay'); 12 | throw new Error('boom'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('yay') 18 | .expect(200, done); 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/req.accepts.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.accepts(type)', function(){ 8 | it('should return true when Accept is not present', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res, next){ 12 | res.end(req.accepts('json') ? 'yes' : 'no'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('yes', done); 18 | }) 19 | 20 | it('should return true when present', function(done){ 21 | var app = express(); 22 | 23 | app.use(function(req, res, next){ 24 | res.end(req.accepts('json') ? 'yes' : 'no'); 25 | }); 26 | 27 | request(app) 28 | .get('/') 29 | .set('Accept', 'application/json') 30 | .expect('yes', done); 31 | }) 32 | 33 | it('should return false otherwise', function(done){ 34 | var app = express(); 35 | 36 | app.use(function(req, res, next){ 37 | res.end(req.accepts('json') ? 'yes' : 'no'); 38 | }); 39 | 40 | request(app) 41 | .get('/') 42 | .set('Accept', 'text/html') 43 | .expect('no', done); 44 | }) 45 | }) 46 | 47 | it('should accept an argument list of type names', function(done){ 48 | var app = express(); 49 | 50 | app.use(function(req, res, next){ 51 | res.end(req.accepts('json', 'html')); 52 | }); 53 | 54 | request(app) 55 | .get('/') 56 | .set('Accept', 'application/json') 57 | .expect('json', done); 58 | }) 59 | 60 | describe('.accepts(types)', function(){ 61 | it('should return the first when Accept is not present', function(done){ 62 | var app = express(); 63 | 64 | app.use(function(req, res, next){ 65 | res.end(req.accepts(['json', 'html'])); 66 | }); 67 | 68 | request(app) 69 | .get('/') 70 | .expect('json', done); 71 | }) 72 | 73 | it('should return the first acceptable type', function(done){ 74 | var app = express(); 75 | 76 | app.use(function(req, res, next){ 77 | res.end(req.accepts(['json', 'html'])); 78 | }); 79 | 80 | request(app) 81 | .get('/') 82 | .set('Accept', 'text/html') 83 | .expect('html', done); 84 | }) 85 | 86 | it('should return false when no match is made', function(done){ 87 | var app = express(); 88 | 89 | app.use(function(req, res, next){ 90 | res.end(req.accepts(['text/html', 'application/json']) ? 'yup' : 'nope'); 91 | }); 92 | 93 | request(app) 94 | .get('/') 95 | .set('Accept', 'foo/bar, bar/baz') 96 | .expect('nope', done); 97 | }) 98 | 99 | it('should take quality into account', function(done){ 100 | var app = express(); 101 | 102 | app.use(function(req, res, next){ 103 | res.end(req.accepts(['text/html', 'application/json'])); 104 | }); 105 | 106 | request(app) 107 | .get('/') 108 | .set('Accept', '*/html; q=.5, application/json') 109 | .expect('application/json', done); 110 | }) 111 | 112 | it('should return the first acceptable type with canonical mime types', function(done){ 113 | var app = express(); 114 | 115 | app.use(function(req, res, next){ 116 | res.end(req.accepts(['application/json', 'text/html'])); 117 | }); 118 | 119 | request(app) 120 | .get('/') 121 | .set('Accept', '*/html') 122 | .expect('text/html', done); 123 | }) 124 | }) 125 | }) 126 | -------------------------------------------------------------------------------- /test/req.acceptsCharsets.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.acceptsCharsets(type)', function(){ 8 | describe('when Accept-Charset is not present', function(){ 9 | it('should return true', function(done){ 10 | var app = express(); 11 | 12 | app.use(function(req, res, next){ 13 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect('yes', done); 19 | }) 20 | }) 21 | 22 | describe('when Accept-Charset is present', function () { 23 | it('should return true', function (done) { 24 | var app = express(); 25 | 26 | app.use(function(req, res, next){ 27 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('Accept-Charset', 'foo, bar, utf-8') 33 | .expect('yes', done); 34 | }) 35 | 36 | it('should return false otherwise', function(done){ 37 | var app = express(); 38 | 39 | app.use(function(req, res, next){ 40 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 41 | }); 42 | 43 | request(app) 44 | .get('/') 45 | .set('Accept-Charset', 'foo, bar') 46 | .expect('no', done); 47 | }) 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/req.acceptsEncodings.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.acceptsEncodings', function () { 8 | it('should return encoding if accepted', function (done) { 9 | var app = express(); 10 | 11 | app.get('/', function (req, res) { 12 | res.send({ 13 | gzip: req.acceptsEncodings('gzip'), 14 | deflate: req.acceptsEncodings('deflate') 15 | }) 16 | }) 17 | 18 | request(app) 19 | .get('/') 20 | .set('Accept-Encoding', ' gzip, deflate') 21 | .expect(200, { gzip: 'gzip', deflate: 'deflate' }, done) 22 | }) 23 | 24 | it('should be false if encoding not accepted', function(done){ 25 | var app = express(); 26 | 27 | app.get('/', function (req, res) { 28 | res.send({ 29 | bogus: req.acceptsEncodings('bogus') 30 | }) 31 | }) 32 | 33 | request(app) 34 | .get('/') 35 | .set('Accept-Encoding', ' gzip, deflate') 36 | .expect(200, { bogus: false }, done) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/req.acceptsLanguages.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.acceptsLanguages', function(){ 8 | it('should return language if accepted', function (done) { 9 | var app = express(); 10 | 11 | app.get('/', function (req, res) { 12 | res.send({ 13 | 'en-us': req.acceptsLanguages('en-us'), 14 | en: req.acceptsLanguages('en') 15 | }) 16 | }) 17 | 18 | request(app) 19 | .get('/') 20 | .set('Accept-Language', 'en;q=.5, en-us') 21 | .expect(200, { 'en-us': 'en-us', en: 'en' }, done) 22 | }) 23 | 24 | it('should be false if language not accepted', function(done){ 25 | var app = express(); 26 | 27 | app.get('/', function (req, res) { 28 | res.send({ 29 | es: req.acceptsLanguages('es') 30 | }) 31 | }) 32 | 33 | request(app) 34 | .get('/') 35 | .set('Accept-Language', 'en;q=.5, en-us') 36 | .expect(200, { es: false }, done) 37 | }) 38 | 39 | describe('when Accept-Language is not present', function(){ 40 | it('should always return language', function (done) { 41 | var app = express(); 42 | 43 | app.get('/', function (req, res) { 44 | res.send({ 45 | en: req.acceptsLanguages('en'), 46 | es: req.acceptsLanguages('es'), 47 | jp: req.acceptsLanguages('jp') 48 | }) 49 | }) 50 | 51 | request(app) 52 | .get('/') 53 | .expect(200, { en: 'en', es: 'es', jp: 'jp' }, done) 54 | }) 55 | }) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /test/req.baseUrl.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..') 4 | var request = require('supertest') 5 | 6 | describe('req', function(){ 7 | describe('.baseUrl', function(){ 8 | it('should be empty for top-level route', function(done){ 9 | var app = express() 10 | 11 | app.get('/:a', function(req, res){ 12 | res.end(req.baseUrl) 13 | }) 14 | 15 | request(app) 16 | .get('/foo') 17 | .expect(200, '', done) 18 | }) 19 | 20 | it('should contain lower path', function(done){ 21 | var app = express() 22 | var sub = express.Router() 23 | 24 | sub.get('/:b', function(req, res){ 25 | res.end(req.baseUrl) 26 | }) 27 | app.use('/:a', sub) 28 | 29 | request(app) 30 | .get('/foo/bar') 31 | .expect(200, '/foo', done); 32 | }) 33 | 34 | it('should contain full lower path', function(done){ 35 | var app = express() 36 | var sub1 = express.Router() 37 | var sub2 = express.Router() 38 | var sub3 = express.Router() 39 | 40 | sub3.get('/:d', function(req, res){ 41 | res.end(req.baseUrl) 42 | }) 43 | sub2.use('/:c', sub3) 44 | sub1.use('/:b', sub2) 45 | app.use('/:a', sub1) 46 | 47 | request(app) 48 | .get('/foo/bar/baz/zed') 49 | .expect(200, '/foo/bar/baz', done); 50 | }) 51 | 52 | it('should travel through routers correctly', function(done){ 53 | var urls = [] 54 | var app = express() 55 | var sub1 = express.Router() 56 | var sub2 = express.Router() 57 | var sub3 = express.Router() 58 | 59 | sub3.get('/:d', function(req, res, next){ 60 | urls.push('0@' + req.baseUrl) 61 | next() 62 | }) 63 | sub2.use('/:c', sub3) 64 | sub1.use('/', function(req, res, next){ 65 | urls.push('1@' + req.baseUrl) 66 | next() 67 | }) 68 | sub1.use('/bar', sub2) 69 | sub1.use('/bar', function(req, res, next){ 70 | urls.push('2@' + req.baseUrl) 71 | next() 72 | }) 73 | app.use(function(req, res, next){ 74 | urls.push('3@' + req.baseUrl) 75 | next() 76 | }) 77 | app.use('/:a', sub1) 78 | app.use(function(req, res, next){ 79 | urls.push('4@' + req.baseUrl) 80 | res.end(urls.join(',')) 81 | }) 82 | 83 | request(app) 84 | .get('/foo/bar/baz/zed') 85 | .expect(200, '3@,1@/foo,0@/foo/bar/baz,2@/foo/bar,4@', done); 86 | }) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /test/req.fresh.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.fresh', function(){ 8 | it('should return true when the resource is not modified', function(done){ 9 | var app = express(); 10 | var etag = '"12345"'; 11 | 12 | app.use(function(req, res){ 13 | res.set('ETag', etag); 14 | res.send(req.fresh); 15 | }); 16 | 17 | request(app) 18 | .get('/') 19 | .set('If-None-Match', etag) 20 | .expect(304, done); 21 | }) 22 | 23 | it('should return false when the resource is modified', function(done){ 24 | var app = express(); 25 | 26 | app.use(function(req, res){ 27 | res.set('ETag', '"123"'); 28 | res.send(req.fresh); 29 | }); 30 | 31 | request(app) 32 | .get('/') 33 | .set('If-None-Match', '"12345"') 34 | .expect(200, 'false', done); 35 | }) 36 | 37 | it('should return false without response headers', function(done){ 38 | var app = express(); 39 | 40 | app.disable('x-powered-by') 41 | app.use(function(req, res){ 42 | res.send(req.fresh); 43 | }); 44 | 45 | request(app) 46 | .get('/') 47 | .expect(200, 'false', done); 48 | }) 49 | 50 | it('should ignore "If-Modified-Since" when "If-None-Match" is present', function(done) { 51 | var app = express(); 52 | const etag = '"FooBar"' 53 | const now = Date.now() 54 | 55 | app.disable('x-powered-by') 56 | app.use(function(req, res) { 57 | res.set('Etag', etag) 58 | res.set('Last-Modified', new Date(now).toUTCString()) 59 | res.send(req.fresh); 60 | }); 61 | 62 | request(app) 63 | .get('/') 64 | .set('If-Modified-Since', new Date(now - 1000).toUTCString) 65 | .set('If-None-Match', etag) 66 | .expect(304, done); 67 | }) 68 | 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /test/req.get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest') 5 | , assert = require('node:assert'); 6 | 7 | describe('req', function(){ 8 | describe('.get(field)', function(){ 9 | it('should return the header field value', function(done){ 10 | var app = express(); 11 | 12 | app.use(function(req, res){ 13 | assert(req.get('Something-Else') === undefined); 14 | res.end(req.get('Content-Type')); 15 | }); 16 | 17 | request(app) 18 | .post('/') 19 | .set('Content-Type', 'application/json') 20 | .expect('application/json', done); 21 | }) 22 | 23 | it('should special-case Referer', function(done){ 24 | var app = express(); 25 | 26 | app.use(function(req, res){ 27 | res.end(req.get('Referer')); 28 | }); 29 | 30 | request(app) 31 | .post('/') 32 | .set('Referrer', 'http://foobar.com') 33 | .expect('http://foobar.com', done); 34 | }) 35 | 36 | it('should throw missing header name', function (done) { 37 | var app = express() 38 | 39 | app.use(function (req, res) { 40 | res.end(req.get()) 41 | }) 42 | 43 | request(app) 44 | .get('/') 45 | .expect(500, /TypeError: name argument is required to req.get/, done) 46 | }) 47 | 48 | it('should throw for non-string header name', function (done) { 49 | var app = express() 50 | 51 | app.use(function (req, res) { 52 | res.end(req.get(42)) 53 | }) 54 | 55 | request(app) 56 | .get('/') 57 | .expect(500, /TypeError: name must be a string to req.get/, done) 58 | }) 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /test/req.ip.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.ip', function(){ 8 | describe('when X-Forwarded-For is present', function(){ 9 | describe('when "trust proxy" is enabled', function(){ 10 | it('should return the client addr', function(done){ 11 | var app = express(); 12 | 13 | app.enable('trust proxy'); 14 | 15 | app.use(function(req, res, next){ 16 | res.send(req.ip); 17 | }); 18 | 19 | request(app) 20 | .get('/') 21 | .set('X-Forwarded-For', 'client, p1, p2') 22 | .expect('client', done); 23 | }) 24 | 25 | it('should return the addr after trusted proxy based on count', function (done) { 26 | var app = express(); 27 | 28 | app.set('trust proxy', 2); 29 | 30 | app.use(function(req, res, next){ 31 | res.send(req.ip); 32 | }); 33 | 34 | request(app) 35 | .get('/') 36 | .set('X-Forwarded-For', 'client, p1, p2') 37 | .expect('p1', done); 38 | }) 39 | 40 | it('should return the addr after trusted proxy based on list', function (done) { 41 | var app = express() 42 | 43 | app.set('trust proxy', '10.0.0.1, 10.0.0.2, 127.0.0.1, ::1') 44 | 45 | app.get('/', function (req, res) { 46 | res.send(req.ip) 47 | }) 48 | 49 | request(app) 50 | .get('/') 51 | .set('X-Forwarded-For', '10.0.0.2, 10.0.0.3, 10.0.0.1', '10.0.0.4') 52 | .expect('10.0.0.3', done) 53 | }) 54 | 55 | it('should return the addr after trusted proxy, from sub app', function (done) { 56 | var app = express(); 57 | var sub = express(); 58 | 59 | app.set('trust proxy', 2); 60 | app.use(sub); 61 | 62 | sub.use(function (req, res, next) { 63 | res.send(req.ip); 64 | }); 65 | 66 | request(app) 67 | .get('/') 68 | .set('X-Forwarded-For', 'client, p1, p2') 69 | .expect(200, 'p1', done); 70 | }) 71 | }) 72 | 73 | describe('when "trust proxy" is disabled', function(){ 74 | it('should return the remote address', function(done){ 75 | var app = express(); 76 | 77 | app.use(function(req, res, next){ 78 | res.send(req.ip); 79 | }); 80 | 81 | var test = request(app).get('/') 82 | test.set('X-Forwarded-For', 'client, p1, p2') 83 | test.expect(200, getExpectedClientAddress(test._server), done); 84 | }) 85 | }) 86 | }) 87 | 88 | describe('when X-Forwarded-For is not present', function(){ 89 | it('should return the remote address', function(done){ 90 | var app = express(); 91 | 92 | app.enable('trust proxy'); 93 | 94 | app.use(function(req, res, next){ 95 | res.send(req.ip); 96 | }); 97 | 98 | var test = request(app).get('/') 99 | test.expect(200, getExpectedClientAddress(test._server), done) 100 | }) 101 | }) 102 | }) 103 | }) 104 | 105 | /** 106 | * Get the local client address depending on AF_NET of server 107 | */ 108 | 109 | function getExpectedClientAddress(server) { 110 | return server.address().address === '::' 111 | ? '::ffff:127.0.0.1' 112 | : '127.0.0.1'; 113 | } 114 | -------------------------------------------------------------------------------- /test/req.ips.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.ips', function(){ 8 | describe('when X-Forwarded-For is present', function(){ 9 | describe('when "trust proxy" is enabled', function(){ 10 | it('should return an array of the specified addresses', function(done){ 11 | var app = express(); 12 | 13 | app.enable('trust proxy'); 14 | 15 | app.use(function(req, res, next){ 16 | res.send(req.ips); 17 | }); 18 | 19 | request(app) 20 | .get('/') 21 | .set('X-Forwarded-For', 'client, p1, p2') 22 | .expect('["client","p1","p2"]', done); 23 | }) 24 | 25 | it('should stop at first untrusted', function(done){ 26 | var app = express(); 27 | 28 | app.set('trust proxy', 2); 29 | 30 | app.use(function(req, res, next){ 31 | res.send(req.ips); 32 | }); 33 | 34 | request(app) 35 | .get('/') 36 | .set('X-Forwarded-For', 'client, p1, p2') 37 | .expect('["p1","p2"]', done); 38 | }) 39 | }) 40 | 41 | describe('when "trust proxy" is disabled', function(){ 42 | it('should return an empty array', function(done){ 43 | var app = express(); 44 | 45 | app.use(function(req, res, next){ 46 | res.send(req.ips); 47 | }); 48 | 49 | request(app) 50 | .get('/') 51 | .set('X-Forwarded-For', 'client, p1, p2') 52 | .expect('[]', done); 53 | }) 54 | }) 55 | }) 56 | 57 | describe('when X-Forwarded-For is not present', function(){ 58 | it('should return []', function(done){ 59 | var app = express(); 60 | 61 | app.use(function(req, res, next){ 62 | res.send(req.ips); 63 | }); 64 | 65 | request(app) 66 | .get('/') 67 | .expect('[]', done); 68 | }) 69 | }) 70 | }) 71 | }) 72 | -------------------------------------------------------------------------------- /test/req.path.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.path', function(){ 8 | it('should return the parsed pathname', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.end(req.path); 13 | }); 14 | 15 | request(app) 16 | .get('/login?redirect=/post/1/comments') 17 | .expect('/login', done); 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/req.protocol.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.protocol', function(){ 8 | it('should return the protocol string', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.end(req.protocol); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('http', done); 18 | }) 19 | 20 | describe('when "trust proxy" is enabled', function(){ 21 | it('should respect X-Forwarded-Proto', function(done){ 22 | var app = express(); 23 | 24 | app.enable('trust proxy'); 25 | 26 | app.use(function(req, res){ 27 | res.end(req.protocol); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('X-Forwarded-Proto', 'https') 33 | .expect('https', done); 34 | }) 35 | 36 | it('should default to the socket addr if X-Forwarded-Proto not present', function(done){ 37 | var app = express(); 38 | 39 | app.enable('trust proxy'); 40 | 41 | app.use(function(req, res){ 42 | req.connection.encrypted = true; 43 | res.end(req.protocol); 44 | }); 45 | 46 | request(app) 47 | .get('/') 48 | .expect('https', done); 49 | }) 50 | 51 | it('should ignore X-Forwarded-Proto if socket addr not trusted', function(done){ 52 | var app = express(); 53 | 54 | app.set('trust proxy', '10.0.0.1'); 55 | 56 | app.use(function(req, res){ 57 | res.end(req.protocol); 58 | }); 59 | 60 | request(app) 61 | .get('/') 62 | .set('X-Forwarded-Proto', 'https') 63 | .expect('http', done); 64 | }) 65 | 66 | it('should default to http', function(done){ 67 | var app = express(); 68 | 69 | app.enable('trust proxy'); 70 | 71 | app.use(function(req, res){ 72 | res.end(req.protocol); 73 | }); 74 | 75 | request(app) 76 | .get('/') 77 | .expect('http', done); 78 | }) 79 | 80 | describe('when trusting hop count', function () { 81 | it('should respect X-Forwarded-Proto', function (done) { 82 | var app = express(); 83 | 84 | app.set('trust proxy', 1); 85 | 86 | app.use(function (req, res) { 87 | res.end(req.protocol); 88 | }); 89 | 90 | request(app) 91 | .get('/') 92 | .set('X-Forwarded-Proto', 'https') 93 | .expect('https', done); 94 | }) 95 | }) 96 | }) 97 | 98 | describe('when "trust proxy" is disabled', function(){ 99 | it('should ignore X-Forwarded-Proto', function(done){ 100 | var app = express(); 101 | 102 | app.use(function(req, res){ 103 | res.end(req.protocol); 104 | }); 105 | 106 | request(app) 107 | .get('/') 108 | .set('X-Forwarded-Proto', 'https') 109 | .expect('http', done); 110 | }) 111 | }) 112 | }) 113 | }) 114 | -------------------------------------------------------------------------------- /test/req.query.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert') 4 | var express = require('../') 5 | , request = require('supertest'); 6 | 7 | describe('req', function(){ 8 | describe('.query', function(){ 9 | it('should default to {}', function(done){ 10 | var app = createApp(); 11 | 12 | request(app) 13 | .get('/') 14 | .expect(200, '{}', done); 15 | }); 16 | 17 | it('should default to parse simple keys', function (done) { 18 | var app = createApp(); 19 | 20 | request(app) 21 | .get('/?user[name]=tj') 22 | .expect(200, '{"user[name]":"tj"}', done); 23 | }); 24 | 25 | describe('when "query parser" is extended', function () { 26 | it('should parse complex keys', function (done) { 27 | var app = createApp('extended'); 28 | 29 | request(app) 30 | .get('/?foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!') 31 | .expect(200, '{"foo":[{"bar":"baz","fizz":"buzz"},"done!"]}', done); 32 | }); 33 | 34 | it('should parse parameters with dots', function (done) { 35 | var app = createApp('extended'); 36 | 37 | request(app) 38 | .get('/?user.name=tj') 39 | .expect(200, '{"user.name":"tj"}', done); 40 | }); 41 | }); 42 | 43 | describe('when "query parser" is simple', function () { 44 | it('should not parse complex keys', function (done) { 45 | var app = createApp('simple'); 46 | 47 | request(app) 48 | .get('/?user%5Bname%5D=tj') 49 | .expect(200, '{"user[name]":"tj"}', done); 50 | }); 51 | }); 52 | 53 | describe('when "query parser" is a function', function () { 54 | it('should parse using function', function (done) { 55 | var app = createApp(function (str) { 56 | return {'length': (str || '').length}; 57 | }); 58 | 59 | request(app) 60 | .get('/?user%5Bname%5D=tj') 61 | .expect(200, '{"length":17}', done); 62 | }); 63 | }); 64 | 65 | describe('when "query parser" disabled', function () { 66 | it('should not parse query', function (done) { 67 | var app = createApp(false); 68 | 69 | request(app) 70 | .get('/?user%5Bname%5D=tj') 71 | .expect(200, '{}', done); 72 | }); 73 | }); 74 | 75 | describe('when "query parser" enabled', function () { 76 | it('should not parse complex keys', function (done) { 77 | var app = createApp(true); 78 | 79 | request(app) 80 | .get('/?user%5Bname%5D=tj') 81 | .expect(200, '{"user[name]":"tj"}', done); 82 | }); 83 | }); 84 | 85 | describe('when "query parser" an unknown value', function () { 86 | it('should throw', function () { 87 | assert.throws(createApp.bind(null, 'bogus'), 88 | /unknown value.*query parser/) 89 | }); 90 | }); 91 | }) 92 | }) 93 | 94 | function createApp(setting) { 95 | var app = express(); 96 | 97 | if (setting !== undefined) { 98 | app.set('query parser', setting); 99 | } 100 | 101 | app.use(function (req, res) { 102 | res.send(req.query); 103 | }); 104 | 105 | return app; 106 | } 107 | -------------------------------------------------------------------------------- /test/req.range.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..'); 4 | var request = require('supertest') 5 | 6 | describe('req', function(){ 7 | describe('.range(size)', function(){ 8 | it('should return parsed ranges', function (done) { 9 | var app = express() 10 | 11 | app.use(function (req, res) { 12 | res.json(req.range(120)) 13 | }) 14 | 15 | request(app) 16 | .get('/') 17 | .set('Range', 'bytes=0-50,51-100') 18 | .expect(200, '[{"start":0,"end":50},{"start":51,"end":100}]', done) 19 | }) 20 | 21 | it('should cap to the given size', function (done) { 22 | var app = express() 23 | 24 | app.use(function (req, res) { 25 | res.json(req.range(75)) 26 | }) 27 | 28 | request(app) 29 | .get('/') 30 | .set('Range', 'bytes=0-100') 31 | .expect(200, '[{"start":0,"end":74}]', done) 32 | }) 33 | 34 | it('should cap to the given size when open-ended', function (done) { 35 | var app = express() 36 | 37 | app.use(function (req, res) { 38 | res.json(req.range(75)) 39 | }) 40 | 41 | request(app) 42 | .get('/') 43 | .set('Range', 'bytes=0-') 44 | .expect(200, '[{"start":0,"end":74}]', done) 45 | }) 46 | 47 | it('should have a .type', function (done) { 48 | var app = express() 49 | 50 | app.use(function (req, res) { 51 | res.json(req.range(120).type) 52 | }) 53 | 54 | request(app) 55 | .get('/') 56 | .set('Range', 'bytes=0-100') 57 | .expect(200, '"bytes"', done) 58 | }) 59 | 60 | it('should accept any type', function (done) { 61 | var app = express() 62 | 63 | app.use(function (req, res) { 64 | res.json(req.range(120).type) 65 | }) 66 | 67 | request(app) 68 | .get('/') 69 | .set('Range', 'users=0-2') 70 | .expect(200, '"users"', done) 71 | }) 72 | 73 | it('should return undefined if no range', function (done) { 74 | var app = express() 75 | 76 | app.use(function (req, res) { 77 | res.send(String(req.range(120))) 78 | }) 79 | 80 | request(app) 81 | .get('/') 82 | .expect(200, 'undefined', done) 83 | }) 84 | }) 85 | 86 | describe('.range(size, options)', function(){ 87 | describe('with "combine: true" option', function(){ 88 | it('should return combined ranges', function (done) { 89 | var app = express() 90 | 91 | app.use(function (req, res) { 92 | res.json(req.range(120, { 93 | combine: true 94 | })) 95 | }) 96 | 97 | request(app) 98 | .get('/') 99 | .set('Range', 'bytes=0-50,51-100') 100 | .expect(200, '[{"start":0,"end":100}]', done) 101 | }) 102 | }) 103 | }) 104 | }) 105 | -------------------------------------------------------------------------------- /test/req.route.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.route', function(){ 8 | it('should be the executed Route', function(done){ 9 | var app = express(); 10 | 11 | app.get('/user/:id{/:op}', function(req, res, next){ 12 | res.header('path-1', req.route.path) 13 | next(); 14 | }); 15 | 16 | app.get('/user/:id/edit', function(req, res){ 17 | res.header('path-2', req.route.path) 18 | res.end(); 19 | }); 20 | 21 | request(app) 22 | .get('/user/12/edit') 23 | .expect('path-1', '/user/:id{/:op}') 24 | .expect('path-2', '/user/:id/edit') 25 | .expect(200, done) 26 | }) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/req.secure.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.secure', function(){ 8 | describe('when X-Forwarded-Proto is missing', function(){ 9 | it('should return false when http', function(done){ 10 | var app = express(); 11 | 12 | app.get('/', function(req, res){ 13 | res.send(req.secure ? 'yes' : 'no'); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect('no', done) 19 | }) 20 | }) 21 | }) 22 | 23 | describe('.secure', function(){ 24 | describe('when X-Forwarded-Proto is present', function(){ 25 | it('should return false when http', function(done){ 26 | var app = express(); 27 | 28 | app.get('/', function(req, res){ 29 | res.send(req.secure ? 'yes' : 'no'); 30 | }); 31 | 32 | request(app) 33 | .get('/') 34 | .set('X-Forwarded-Proto', 'https') 35 | .expect('no', done) 36 | }) 37 | 38 | it('should return true when "trust proxy" is enabled', function(done){ 39 | var app = express(); 40 | 41 | app.enable('trust proxy'); 42 | 43 | app.get('/', function(req, res){ 44 | res.send(req.secure ? 'yes' : 'no'); 45 | }); 46 | 47 | request(app) 48 | .get('/') 49 | .set('X-Forwarded-Proto', 'https') 50 | .expect('yes', done) 51 | }) 52 | 53 | it('should return false when initial proxy is http', function(done){ 54 | var app = express(); 55 | 56 | app.enable('trust proxy'); 57 | 58 | app.get('/', function(req, res){ 59 | res.send(req.secure ? 'yes' : 'no'); 60 | }); 61 | 62 | request(app) 63 | .get('/') 64 | .set('X-Forwarded-Proto', 'http, https') 65 | .expect('no', done) 66 | }) 67 | 68 | it('should return true when initial proxy is https', function(done){ 69 | var app = express(); 70 | 71 | app.enable('trust proxy'); 72 | 73 | app.get('/', function(req, res){ 74 | res.send(req.secure ? 'yes' : 'no'); 75 | }); 76 | 77 | request(app) 78 | .get('/') 79 | .set('X-Forwarded-Proto', 'https, http') 80 | .expect('yes', done) 81 | }) 82 | 83 | describe('when "trust proxy" trusting hop count', function () { 84 | it('should respect X-Forwarded-Proto', function (done) { 85 | var app = express(); 86 | 87 | app.set('trust proxy', 1); 88 | 89 | app.get('/', function (req, res) { 90 | res.send(req.secure ? 'yes' : 'no'); 91 | }); 92 | 93 | request(app) 94 | .get('/') 95 | .set('X-Forwarded-Proto', 'https') 96 | .expect('yes', done) 97 | }) 98 | }) 99 | }) 100 | }) 101 | }) 102 | -------------------------------------------------------------------------------- /test/req.signedCookies.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest') 5 | , cookieParser = require('cookie-parser') 6 | 7 | describe('req', function(){ 8 | describe('.signedCookies', function(){ 9 | it('should return a signed JSON cookie', function(done){ 10 | var app = express(); 11 | 12 | app.use(cookieParser('secret')); 13 | 14 | app.use(function(req, res){ 15 | if (req.path === '/set') { 16 | res.cookie('obj', { foo: 'bar' }, { signed: true }); 17 | res.end(); 18 | } else { 19 | res.send(req.signedCookies); 20 | } 21 | }); 22 | 23 | request(app) 24 | .get('/set') 25 | .end(function(err, res){ 26 | if (err) return done(err); 27 | var cookie = res.header['set-cookie']; 28 | 29 | request(app) 30 | .get('/') 31 | .set('Cookie', cookie) 32 | .expect(200, { obj: { foo: 'bar' } }, done) 33 | }); 34 | }) 35 | }) 36 | }) 37 | 38 | -------------------------------------------------------------------------------- /test/req.stale.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.stale', function(){ 8 | it('should return false when the resource is not modified', function(done){ 9 | var app = express(); 10 | var etag = '"12345"'; 11 | 12 | app.use(function(req, res){ 13 | res.set('ETag', etag); 14 | res.send(req.stale); 15 | }); 16 | 17 | request(app) 18 | .get('/') 19 | .set('If-None-Match', etag) 20 | .expect(304, done); 21 | }) 22 | 23 | it('should return true when the resource is modified', function(done){ 24 | var app = express(); 25 | 26 | app.use(function(req, res){ 27 | res.set('ETag', '"123"'); 28 | res.send(req.stale); 29 | }); 30 | 31 | request(app) 32 | .get('/') 33 | .set('If-None-Match', '"12345"') 34 | .expect(200, 'true', done); 35 | }) 36 | 37 | it('should return true without response headers', function(done){ 38 | var app = express(); 39 | 40 | app.disable('x-powered-by') 41 | app.use(function(req, res){ 42 | res.send(req.stale); 43 | }); 44 | 45 | request(app) 46 | .get('/') 47 | .expect(200, 'true', done); 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/req.xhr.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.xhr', function(){ 8 | before(function () { 9 | this.app = express() 10 | this.app.get('/', function (req, res) { 11 | res.send(req.xhr) 12 | }) 13 | }) 14 | 15 | it('should return true when X-Requested-With is xmlhttprequest', function(done){ 16 | request(this.app) 17 | .get('/') 18 | .set('X-Requested-With', 'xmlhttprequest') 19 | .expect(200, 'true', done) 20 | }) 21 | 22 | it('should case-insensitive', function(done){ 23 | request(this.app) 24 | .get('/') 25 | .set('X-Requested-With', 'XMLHttpRequest') 26 | .expect(200, 'true', done) 27 | }) 28 | 29 | it('should return false otherwise', function(done){ 30 | request(this.app) 31 | .get('/') 32 | .set('X-Requested-With', 'blahblah') 33 | .expect(200, 'false', done) 34 | }) 35 | 36 | it('should return false when not present', function(done){ 37 | request(this.app) 38 | .get('/') 39 | .expect(200, 'false', done) 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test/res.append.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert') 4 | var express = require('..') 5 | var request = require('supertest') 6 | 7 | describe('res', function () { 8 | describe('.append(field, val)', function () { 9 | it('should append multiple headers', function (done) { 10 | var app = express() 11 | 12 | app.use(function (req, res, next) { 13 | res.append('Set-Cookie', 'foo=bar') 14 | next() 15 | }) 16 | 17 | app.use(function (req, res) { 18 | res.append('Set-Cookie', 'fizz=buzz') 19 | res.end() 20 | }) 21 | 22 | request(app) 23 | .get('/') 24 | .expect(200) 25 | .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) 26 | .end(done) 27 | }) 28 | 29 | it('should accept array of values', function (done) { 30 | var app = express() 31 | 32 | app.use(function (req, res, next) { 33 | res.append('Set-Cookie', ['foo=bar', 'fizz=buzz']) 34 | res.end() 35 | }) 36 | 37 | request(app) 38 | .get('/') 39 | .expect(200) 40 | .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) 41 | .end(done) 42 | }) 43 | 44 | it('should get reset by res.set(field, val)', function (done) { 45 | var app = express() 46 | 47 | app.use(function (req, res, next) { 48 | res.append('Set-Cookie', 'foo=bar') 49 | res.append('Set-Cookie', 'fizz=buzz') 50 | next() 51 | }) 52 | 53 | app.use(function (req, res) { 54 | res.set('Set-Cookie', 'pet=tobi') 55 | res.end() 56 | }); 57 | 58 | request(app) 59 | .get('/') 60 | .expect(200) 61 | .expect(shouldHaveHeaderValues('Set-Cookie', ['pet=tobi'])) 62 | .end(done) 63 | }) 64 | 65 | it('should work with res.set(field, val) first', function (done) { 66 | var app = express() 67 | 68 | app.use(function (req, res, next) { 69 | res.set('Set-Cookie', 'foo=bar') 70 | next() 71 | }) 72 | 73 | app.use(function(req, res){ 74 | res.append('Set-Cookie', 'fizz=buzz') 75 | res.end() 76 | }) 77 | 78 | request(app) 79 | .get('/') 80 | .expect(200) 81 | .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) 82 | .end(done) 83 | }) 84 | 85 | it('should work together with res.cookie', function (done) { 86 | var app = express() 87 | 88 | app.use(function (req, res, next) { 89 | res.cookie('foo', 'bar') 90 | next() 91 | }) 92 | 93 | app.use(function (req, res) { 94 | res.append('Set-Cookie', 'fizz=buzz') 95 | res.end() 96 | }) 97 | 98 | request(app) 99 | .get('/') 100 | .expect(200) 101 | .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar; Path=/', 'fizz=buzz'])) 102 | .end(done) 103 | }) 104 | }) 105 | }) 106 | 107 | function shouldHaveHeaderValues (key, values) { 108 | return function (res) { 109 | var headers = res.headers[key.toLowerCase()] 110 | assert.ok(headers, 'should have header "' + key + '"') 111 | assert.strictEqual(headers.length, values.length, 'should have ' + values.length + ' occurances of "' + key + '"') 112 | for (var i = 0; i < values.length; i++) { 113 | assert.strictEqual(headers[i], values[i]) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /test/res.attachment.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.attachment()', function(){ 8 | it('should Content-Disposition to attachment', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.attachment().send('foo'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('Content-Disposition', 'attachment', done); 18 | }) 19 | }) 20 | 21 | describe('.attachment(filename)', function(){ 22 | it('should add the filename param', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | res.attachment('/path/to/image.png'); 27 | res.send('foo'); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .expect('Content-Disposition', 'attachment; filename="image.png"', done); 33 | }) 34 | 35 | it('should set the Content-Type', function(done){ 36 | var app = express(); 37 | 38 | app.use(function(req, res){ 39 | res.attachment('/path/to/image.png'); 40 | res.send(Buffer.alloc(4, '.')) 41 | }); 42 | 43 | request(app) 44 | .get('/') 45 | .expect('Content-Type', 'image/png', done); 46 | }) 47 | }) 48 | 49 | describe('.attachment(utf8filename)', function(){ 50 | it('should add the filename and filename* params', function(done){ 51 | var app = express(); 52 | 53 | app.use(function(req, res){ 54 | res.attachment('/locales/日本語.txt'); 55 | res.send('japanese'); 56 | }); 57 | 58 | request(app) 59 | .get('/') 60 | .expect('Content-Disposition', 'attachment; filename="???.txt"; filename*=UTF-8\'\'%E6%97%A5%E6%9C%AC%E8%AA%9E.txt') 61 | .expect(200, done); 62 | }) 63 | 64 | it('should set the Content-Type', function(done){ 65 | var app = express(); 66 | 67 | app.use(function(req, res){ 68 | res.attachment('/locales/日本語.txt'); 69 | res.send('japanese'); 70 | }); 71 | 72 | request(app) 73 | .get('/') 74 | .expect('Content-Type', 'text/plain; charset=utf-8', done); 75 | }) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /test/res.clearCookie.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.clearCookie(name)', function(){ 8 | it('should set a cookie passed expiry', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.clearCookie('sid').end(); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('Set-Cookie', 'sid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT') 18 | .expect(200, done) 19 | }) 20 | }) 21 | 22 | describe('.clearCookie(name, options)', function(){ 23 | it('should set the given params', function(done){ 24 | var app = express(); 25 | 26 | app.use(function(req, res){ 27 | res.clearCookie('sid', { path: '/admin' }).end(); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') 33 | .expect(200, done) 34 | }) 35 | 36 | it('should ignore maxAge', function(done){ 37 | var app = express(); 38 | 39 | app.use(function(req, res){ 40 | res.clearCookie('sid', { path: '/admin', maxAge: 1000 }).end(); 41 | }); 42 | 43 | request(app) 44 | .get('/') 45 | .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') 46 | .expect(200, done) 47 | }) 48 | 49 | it('should ignore user supplied expires param', function(done){ 50 | var app = express(); 51 | 52 | app.use(function(req, res){ 53 | res.clearCookie('sid', { path: '/admin', expires: new Date() }).end(); 54 | }); 55 | 56 | request(app) 57 | .get('/') 58 | .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') 59 | .expect(200, done) 60 | }) 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /test/res.get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..'); 4 | var request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.get(field)', function(){ 8 | it('should get the response header field', function (done) { 9 | var app = express(); 10 | 11 | app.use(function (req, res) { 12 | res.setHeader('Content-Type', 'text/x-foo'); 13 | res.send(res.get('Content-Type')); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect(200, 'text/x-foo', done); 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/res.links.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..'); 4 | var request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.links(obj)', function(){ 8 | it('should set Link header field', function (done) { 9 | var app = express(); 10 | 11 | app.use(function (req, res) { 12 | res.links({ 13 | next: 'http://api.example.com/users?page=2', 14 | last: 'http://api.example.com/users?page=5' 15 | }); 16 | res.end(); 17 | }); 18 | 19 | request(app) 20 | .get('/') 21 | .expect('Link', '; rel="next", ; rel="last"') 22 | .expect(200, done); 23 | }) 24 | 25 | it('should set Link header field for multiple calls', function (done) { 26 | var app = express(); 27 | 28 | app.use(function (req, res) { 29 | res.links({ 30 | next: 'http://api.example.com/users?page=2', 31 | last: 'http://api.example.com/users?page=5' 32 | }); 33 | 34 | res.links({ 35 | prev: 'http://api.example.com/users?page=1' 36 | }); 37 | 38 | res.end(); 39 | }); 40 | 41 | request(app) 42 | .get('/') 43 | .expect('Link', '; rel="next", ; rel="last", ; rel="prev"') 44 | .expect(200, done); 45 | }) 46 | 47 | it('should set multiple links for single rel', function (done) { 48 | var app = express(); 49 | 50 | app.use(function (req, res) { 51 | res.links({ 52 | next: 'http://api.example.com/users?page=2', 53 | last: ['http://api.example.com/users?page=5', 'http://api.example.com/users?page=1'] 54 | }); 55 | 56 | res.end(); 57 | }); 58 | 59 | request(app) 60 | .get('/') 61 | .expect('Link', '; rel="next", ; rel="last", ; rel="last"') 62 | .expect(200, done); 63 | }) 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /test/res.locals.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.locals', function(){ 8 | it('should be empty by default', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.json(res.locals) 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect(200, {}, done) 18 | }) 19 | }) 20 | 21 | it('should work when mounted', function(done){ 22 | var app = express(); 23 | var blog = express(); 24 | 25 | app.use(blog); 26 | 27 | blog.use(function(req, res, next){ 28 | res.locals.foo = 'bar'; 29 | next(); 30 | }); 31 | 32 | app.use(function(req, res){ 33 | res.json(res.locals) 34 | }); 35 | 36 | request(app) 37 | .get('/') 38 | .expect(200, { foo: 'bar' }, done) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /test/res.sendStatus.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..') 4 | var request = require('supertest') 5 | 6 | describe('res', function () { 7 | describe('.sendStatus(statusCode)', function () { 8 | it('should send the status code and message as body', function (done) { 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.sendStatus(201); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect(201, 'Created', done); 18 | }) 19 | 20 | it('should work with unknown code', function (done) { 21 | var app = express(); 22 | 23 | app.use(function(req, res){ 24 | res.sendStatus(599); 25 | }); 26 | 27 | request(app) 28 | .get('/') 29 | .expect(599, '599', done); 30 | }) 31 | 32 | it('should raise error for invalid status code', function (done) { 33 | var app = express() 34 | 35 | app.use(function (req, res) { 36 | res.sendStatus(undefined).end() 37 | }) 38 | 39 | request(app) 40 | .get('/') 41 | .expect(500, /TypeError: Invalid status code/, done) 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /test/res.set.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..'); 4 | var request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.set(field, value)', function(){ 8 | it('should set the response header field', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.set('Content-Type', 'text/x-foo; charset=utf-8').end(); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('Content-Type', 'text/x-foo; charset=utf-8') 18 | .end(done); 19 | }) 20 | 21 | it('should coerce to a string', function (done) { 22 | var app = express(); 23 | 24 | app.use(function (req, res) { 25 | res.set('X-Number', 123); 26 | res.end(typeof res.get('X-Number')); 27 | }); 28 | 29 | request(app) 30 | .get('/') 31 | .expect('X-Number', '123') 32 | .expect(200, 'string', done); 33 | }) 34 | }) 35 | 36 | describe('.set(field, values)', function(){ 37 | it('should set multiple response header fields', function(done){ 38 | var app = express(); 39 | 40 | app.use(function(req, res){ 41 | res.set('Set-Cookie', ["type=ninja", "language=javascript"]); 42 | res.send(res.get('Set-Cookie')); 43 | }); 44 | 45 | request(app) 46 | .get('/') 47 | .expect('["type=ninja","language=javascript"]', done); 48 | }) 49 | 50 | it('should coerce to an array of strings', function (done) { 51 | var app = express(); 52 | 53 | app.use(function (req, res) { 54 | res.set('X-Numbers', [123, 456]); 55 | res.end(JSON.stringify(res.get('X-Numbers'))); 56 | }); 57 | 58 | request(app) 59 | .get('/') 60 | .expect('X-Numbers', '123, 456') 61 | .expect(200, '["123","456"]', done); 62 | }) 63 | 64 | it('should not set a charset of one is already set', function (done) { 65 | var app = express(); 66 | 67 | app.use(function (req, res) { 68 | res.set('Content-Type', 'text/html; charset=lol'); 69 | res.end(); 70 | }); 71 | 72 | request(app) 73 | .get('/') 74 | .expect('Content-Type', 'text/html; charset=lol') 75 | .expect(200, done); 76 | }) 77 | 78 | it('should throw when Content-Type is an array', function (done) { 79 | var app = express() 80 | 81 | app.use(function (req, res) { 82 | res.set('Content-Type', ['text/html']) 83 | res.end() 84 | }); 85 | 86 | request(app) 87 | .get('/') 88 | .expect(500, /TypeError: Content-Type cannot be set to an Array/, done) 89 | }) 90 | }) 91 | 92 | describe('.set(object)', function(){ 93 | it('should set multiple fields', function(done){ 94 | var app = express(); 95 | 96 | app.use(function(req, res){ 97 | res.set({ 98 | 'X-Foo': 'bar', 99 | 'X-Bar': 'baz' 100 | }).end(); 101 | }); 102 | 103 | request(app) 104 | .get('/') 105 | .expect('X-Foo', 'bar') 106 | .expect('X-Bar', 'baz') 107 | .end(done); 108 | }) 109 | 110 | it('should coerce to a string', function (done) { 111 | var app = express(); 112 | 113 | app.use(function (req, res) { 114 | res.set({ 'X-Number': 123 }); 115 | res.end(typeof res.get('X-Number')); 116 | }); 117 | 118 | request(app) 119 | .get('/') 120 | .expect('X-Number', '123') 121 | .expect(200, 'string', done); 122 | }) 123 | }) 124 | }) 125 | -------------------------------------------------------------------------------- /test/res.type.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.type(str)', function(){ 8 | it('should set the Content-Type based on a filename', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.type('foo.js').end('var name = "tj";'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('Content-Type', 'text/javascript; charset=utf-8') 18 | .end(done) 19 | }) 20 | 21 | it('should default to application/octet-stream', function(done){ 22 | var app = express(); 23 | 24 | app.use(function(req, res){ 25 | res.type('rawr').end('var name = "tj";'); 26 | }); 27 | 28 | request(app) 29 | .get('/') 30 | .expect('Content-Type', 'application/octet-stream', done); 31 | }) 32 | 33 | it('should set the Content-Type with type/subtype', function(done){ 34 | var app = express(); 35 | 36 | app.use(function(req, res){ 37 | res.type('application/vnd.amazon.ebook') 38 | .end('var name = "tj";'); 39 | }); 40 | 41 | request(app) 42 | .get('/') 43 | .expect('Content-Type', 'application/vnd.amazon.ebook', done); 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/res.vary.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..'); 4 | var request = require('supertest'); 5 | var utils = require('./support/utils'); 6 | 7 | describe('res.vary()', function(){ 8 | describe('with no arguments', function(){ 9 | it('should throw error', function (done) { 10 | var app = express(); 11 | 12 | app.use(function (req, res) { 13 | res.vary(); 14 | res.end(); 15 | }); 16 | 17 | request(app) 18 | .get('/') 19 | .expect(500, /field.*required/, done) 20 | }) 21 | }) 22 | 23 | describe('with an empty array', function(){ 24 | it('should not set Vary', function (done) { 25 | var app = express(); 26 | 27 | app.use(function (req, res) { 28 | res.vary([]); 29 | res.end(); 30 | }); 31 | 32 | request(app) 33 | .get('/') 34 | .expect(utils.shouldNotHaveHeader('Vary')) 35 | .expect(200, done); 36 | }) 37 | }) 38 | 39 | describe('with an array', function(){ 40 | it('should set the values', function (done) { 41 | var app = express(); 42 | 43 | app.use(function (req, res) { 44 | res.vary(['Accept', 'Accept-Language', 'Accept-Encoding']); 45 | res.end(); 46 | }); 47 | 48 | request(app) 49 | .get('/') 50 | .expect('Vary', 'Accept, Accept-Language, Accept-Encoding') 51 | .expect(200, done); 52 | }) 53 | }) 54 | 55 | describe('with a string', function(){ 56 | it('should set the value', function (done) { 57 | var app = express(); 58 | 59 | app.use(function (req, res) { 60 | res.vary('Accept'); 61 | res.end(); 62 | }); 63 | 64 | request(app) 65 | .get('/') 66 | .expect('Vary', 'Accept') 67 | .expect(200, done); 68 | }) 69 | }) 70 | 71 | describe('when the value is present', function(){ 72 | it('should not add it again', function (done) { 73 | var app = express(); 74 | 75 | app.use(function (req, res) { 76 | res.vary('Accept'); 77 | res.vary('Accept-Encoding'); 78 | res.vary('Accept-Encoding'); 79 | res.vary('Accept-Encoding'); 80 | res.vary('Accept'); 81 | res.end(); 82 | }); 83 | 84 | request(app) 85 | .get('/') 86 | .expect('Vary', 'Accept, Accept-Encoding') 87 | .expect(200, done); 88 | }) 89 | }) 90 | }) 91 | -------------------------------------------------------------------------------- /test/support/env.js: -------------------------------------------------------------------------------- 1 | 2 | process.env.NODE_ENV = 'test'; 3 | process.env.NO_DEPRECATION = 'body-parser,express'; 4 | -------------------------------------------------------------------------------- /test/support/tmpl.js: -------------------------------------------------------------------------------- 1 | var fs = require('node:fs'); 2 | 3 | var variableRegExp = /\$([0-9a-zA-Z\.]+)/g; 4 | 5 | module.exports = function renderFile(fileName, options, callback) { 6 | function onReadFile(err, str) { 7 | if (err) { 8 | callback(err); 9 | return; 10 | } 11 | 12 | try { 13 | str = str.replace(variableRegExp, generateVariableLookup(options)); 14 | } catch (e) { 15 | err = e; 16 | err.name = 'RenderError' 17 | } 18 | 19 | callback(err, str); 20 | } 21 | 22 | fs.readFile(fileName, 'utf8', onReadFile); 23 | }; 24 | 25 | function generateVariableLookup(data) { 26 | return function variableLookup(str, path) { 27 | var parts = path.split('.'); 28 | var value = data; 29 | 30 | for (var i = 0; i < parts.length; i++) { 31 | value = value[parts[i]]; 32 | } 33 | 34 | return value; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /test/support/utils.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | * @private 5 | */ 6 | 7 | var assert = require('node:assert'); 8 | 9 | /** 10 | * Module exports. 11 | * @public 12 | */ 13 | 14 | exports.shouldHaveBody = shouldHaveBody 15 | exports.shouldHaveHeader = shouldHaveHeader 16 | exports.shouldNotHaveBody = shouldNotHaveBody 17 | exports.shouldNotHaveHeader = shouldNotHaveHeader; 18 | exports.shouldSkipQuery = shouldSkipQuery 19 | 20 | /** 21 | * Assert that a supertest response has a specific body. 22 | * 23 | * @param {Buffer} buf 24 | * @returns {function} 25 | */ 26 | 27 | function shouldHaveBody (buf) { 28 | return function (res) { 29 | var body = !Buffer.isBuffer(res.body) 30 | ? Buffer.from(res.text) 31 | : res.body 32 | assert.ok(body, 'response has body') 33 | assert.strictEqual(body.toString('hex'), buf.toString('hex')) 34 | } 35 | } 36 | 37 | /** 38 | * Assert that a supertest response does have a header. 39 | * 40 | * @param {string} header Header name to check 41 | * @returns {function} 42 | */ 43 | 44 | function shouldHaveHeader (header) { 45 | return function (res) { 46 | assert.ok((header.toLowerCase() in res.headers), 'should have header ' + header) 47 | } 48 | } 49 | 50 | /** 51 | * Assert that a supertest response does not have a body. 52 | * 53 | * @returns {function} 54 | */ 55 | 56 | function shouldNotHaveBody () { 57 | return function (res) { 58 | assert.ok(res.text === '' || res.text === undefined) 59 | } 60 | } 61 | 62 | /** 63 | * Assert that a supertest response does not have a header. 64 | * 65 | * @param {string} header Header name to check 66 | * @returns {function} 67 | */ 68 | function shouldNotHaveHeader(header) { 69 | return function (res) { 70 | assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header); 71 | }; 72 | } 73 | 74 | function getMajorVersion(versionString) { 75 | return versionString.split('.')[0]; 76 | } 77 | 78 | function shouldSkipQuery(versionString) { 79 | // Skipping HTTP QUERY tests below Node 22, QUERY wasn't fully supported by Node until 22 80 | // we could update this implementation to run on supported versions of 21 once they exist 81 | // upstream tracking https://github.com/nodejs/node/issues/51562 82 | // express tracking issue: https://github.com/expressjs/express/issues/5615 83 | return Number(getMajorVersion(versionString)) < 22 84 | } 85 | 86 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('node:assert'); 4 | var utils = require('../lib/utils'); 5 | 6 | describe('utils.etag(body, encoding)', function(){ 7 | it('should support strings', function(){ 8 | assert.strictEqual(utils.etag('express!'), 9 | '"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') 10 | }) 11 | 12 | it('should support utf8 strings', function(){ 13 | assert.strictEqual(utils.etag('express❤', 'utf8'), 14 | '"a-JBiXf7GyzxwcrxY4hVXUwa7tmks"') 15 | }) 16 | 17 | it('should support buffer', function(){ 18 | assert.strictEqual(utils.etag(Buffer.from('express!')), 19 | '"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') 20 | }) 21 | 22 | it('should support empty string', function(){ 23 | assert.strictEqual(utils.etag(''), 24 | '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"') 25 | }) 26 | }) 27 | 28 | describe('utils.normalizeType acceptParams method', () => { 29 | it('should handle a type with a malformed parameter and break the loop in acceptParams', () => { 30 | const result = utils.normalizeType('text/plain;invalid'); 31 | assert.deepEqual(result,{ 32 | value: 'text/plain', 33 | quality: 1, 34 | params: {} // No parameters are added since "invalid" has no "=" 35 | }); 36 | }); 37 | }); 38 | 39 | 40 | describe('utils.setCharset(type, charset)', function () { 41 | it('should do anything without type', function () { 42 | assert.strictEqual(utils.setCharset(), undefined); 43 | }); 44 | 45 | it('should return type if not given charset', function () { 46 | assert.strictEqual(utils.setCharset('text/html'), 'text/html'); 47 | }); 48 | 49 | it('should keep charset if not given charset', function () { 50 | assert.strictEqual(utils.setCharset('text/html; charset=utf-8'), 'text/html; charset=utf-8'); 51 | }); 52 | 53 | it('should set charset', function () { 54 | assert.strictEqual(utils.setCharset('text/html', 'utf-8'), 'text/html; charset=utf-8'); 55 | }); 56 | 57 | it('should override charset', function () { 58 | assert.strictEqual(utils.setCharset('text/html; charset=iso-8859-1', 'utf-8'), 'text/html; charset=utf-8'); 59 | }); 60 | }); 61 | 62 | describe('utils.wetag(body, encoding)', function(){ 63 | it('should support strings', function(){ 64 | assert.strictEqual(utils.wetag('express!'), 65 | 'W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') 66 | }) 67 | 68 | it('should support utf8 strings', function(){ 69 | assert.strictEqual(utils.wetag('express❤', 'utf8'), 70 | 'W/"a-JBiXf7GyzxwcrxY4hVXUwa7tmks"') 71 | }) 72 | 73 | it('should support buffer', function(){ 74 | assert.strictEqual(utils.wetag(Buffer.from('express!')), 75 | 'W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') 76 | }) 77 | 78 | it('should support empty string', function(){ 79 | assert.strictEqual(utils.wetag(''), 80 | 'W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"') 81 | }) 82 | }) 83 | --------------------------------------------------------------------------------