mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 18:57:43 +00:00
Compare commits
240 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d39e8ad177 | ||
|
|
efe85d9fdc | ||
|
|
f62378e1bc | ||
|
|
12fae14531 | ||
|
|
5ddf311af3 | ||
|
|
49744abd11 | ||
|
|
6e97452f60 | ||
|
|
6a23d34d65 | ||
|
|
8c12cdf93b | ||
|
|
7fea74fcf0 | ||
|
|
dac7a0475a | ||
|
|
997919b488 | ||
|
|
36fb59c6c7 | ||
|
|
3a5edfaff0 | ||
|
|
52d978119a | ||
|
|
fe93005e04 | ||
|
|
20415843f4 | ||
|
|
1faf228935 | ||
|
|
2e0fb646d0 | ||
|
|
59fc27028e | ||
|
|
51fc39ccf8 | ||
|
|
8e229f9275 | ||
|
|
a024c8a7b6 | ||
|
|
7e562c6d8d | ||
|
|
1bcde96bc8 | ||
|
|
7d36477568 | ||
|
|
40d2d8f2c8 | ||
|
|
77ada906db | ||
|
|
21df421ebc | ||
|
|
4c9ddc1c47 | ||
|
|
9ebe5d500d | ||
|
|
ec4a01b6b8 | ||
|
|
54271f69b5 | ||
|
|
125bb742a3 | ||
|
|
2a980ad160 | ||
|
|
a3e7e05e0a | ||
|
|
c5addb9a17 | ||
|
|
e35380a39d | ||
|
|
f5b6e67aed | ||
|
|
2177f67f54 | ||
|
|
f4bd86ed36 | ||
|
|
2ec589c113 | ||
|
|
4cf7eed927 | ||
|
|
6d084715ba | ||
|
|
61421a8c0c | ||
|
|
f42b160bbc | ||
|
|
689073d657 | ||
|
|
2803a2b35a | ||
|
|
a7d6d29ed3 | ||
|
|
897290b685 | ||
|
|
700349ffaf | ||
|
|
4b9cd2fd0e | ||
|
|
b44191eb3d | ||
|
|
8417c60fcf | ||
|
|
bf91946bd4 | ||
|
|
d97d79ed9a | ||
|
|
26e53f0fbc | ||
|
|
6abec204c0 | ||
|
|
815f799310 | ||
|
|
7f9e5843b9 | ||
|
|
93cf646d5c | ||
|
|
2676a1f281 | ||
|
|
6da57c7819 | ||
|
|
d546f93f2f | ||
|
|
4771ba2bc3 | ||
|
|
3ae704f67f | ||
|
|
8b6d34963d | ||
|
|
36b8148110 | ||
|
|
6d98d2e110 | ||
|
|
51a76366e3 | ||
|
|
4e3f95c0ea | ||
|
|
88bd6d8e3a | ||
|
|
51595d402b | ||
|
|
94669f9289 | ||
|
|
b28db2c12c | ||
|
|
0b746953c4 | ||
|
|
4f0f6cc67d | ||
|
|
a003cfab03 | ||
|
|
a1fa90fcea | ||
|
|
11f2b1db22 | ||
|
|
084e36506a | ||
|
|
0867302ddb | ||
|
|
567c9c665d | ||
|
|
69a4cf2819 | ||
|
|
4ee853e837 | ||
|
|
414854b82e | ||
|
|
06c6b88808 | ||
|
|
1b51edac7c | ||
|
|
b625132864 | ||
|
|
e3eca80584 | ||
|
|
23b44b3ddd | ||
|
|
b9fea12245 | ||
|
|
c259c3407f | ||
|
|
fdeb1d3176 | ||
|
|
734b281900 | ||
|
|
0e3ab6ec21 | ||
|
|
59af63ac2e | ||
|
|
e720c5a21b | ||
|
|
3abea7f818 | ||
|
|
2a89eb5c74 | ||
|
|
59aae7686b | ||
|
|
c4fe7de7bc | ||
|
|
a22920707b | ||
|
|
02d1c3916e | ||
|
|
8d8bfaac7b | ||
|
|
13df1de857 | ||
|
|
2a00da2067 | ||
|
|
24e4a2570d | ||
|
|
91b6fb83b4 | ||
|
|
3531987844 | ||
|
|
f540c3b019 | ||
|
|
b8b2eff3c3 | ||
|
|
f4e48bc43e | ||
|
|
8c24fa8f7b | ||
|
|
0debedf4f3 | ||
|
|
74beeac071 | ||
|
|
9bc1742937 | ||
|
|
5ad95419ba | ||
|
|
8a76f39d98 | ||
|
|
60b7c672c1 | ||
|
|
1e42a98db6 | ||
|
|
506fbd63be | ||
|
|
b9f7a97fe1 | ||
|
|
546969d198 | ||
|
|
f05b5d0e9c | ||
|
|
3c1d605da7 | ||
|
|
6b4c4f5426 | ||
|
|
a1efd9d6cf | ||
|
|
c6ee8d6e7f | ||
|
|
442fd46799 | ||
|
|
723b67766f | ||
|
|
29e117e676 | ||
|
|
06b2b1416d | ||
|
|
8368dc178a | ||
|
|
61f4049122 | ||
|
|
bb7907b932 | ||
|
|
f56ce73186 | ||
|
|
24b3dc5516 | ||
|
|
689d175b8b | ||
|
|
340be0f79a | ||
|
|
33e8dc303a | ||
|
|
644f6464b9 | ||
|
|
ecd7572f1e | ||
|
|
97131bcda8 | ||
|
|
8d98e86d7f | ||
|
|
2c47827053 | ||
|
|
97f0a518d8 | ||
|
|
7ec5dd2b3c | ||
|
|
ab2c70b954 | ||
|
|
745a63f825 | ||
|
|
a2dfc56a49 | ||
|
|
d854c43ea1 | ||
|
|
b02a95c693 | ||
|
|
631ada0c64 | ||
|
|
75e0c7a2c9 | ||
|
|
e2482b7e36 | ||
|
|
2df96e349f | ||
|
|
a38fae126a | ||
|
|
547fdd41dc | ||
|
|
0b330ef57c | ||
|
|
158a17031a | ||
|
|
29ea1b2f74 | ||
|
|
11a209e4b7 | ||
|
|
fd8e45c344 | ||
|
|
708ac4cdf5 | ||
|
|
92c5ce59f5 | ||
|
|
8880ddad1c | ||
|
|
b91c7ffb28 | ||
|
|
ecaf67c930 | ||
|
|
99175c3ef6 | ||
|
|
1b2e097be2 | ||
|
|
04da4aaf1a | ||
|
|
2e2d78c4d9 | ||
|
|
980d881e3b | ||
|
|
1df75763e3 | ||
|
|
32c558d414 | ||
|
|
a10770286e | ||
|
|
5855339455 | ||
|
|
1cc8169938 | ||
|
|
9482b82d0b | ||
|
|
10b9b507b7 | ||
|
|
03dc367187 | ||
|
|
f739b162d9 | ||
|
|
c92420648e | ||
|
|
dd69eedd18 | ||
|
|
0def9bb659 | ||
|
|
4847d0efa1 | ||
|
|
c17fe05861 | ||
|
|
87279c08aa | ||
|
|
8bf0720391 | ||
|
|
eb4c930d5f | ||
|
|
947b6b7d57 | ||
|
|
bf4c3ee00f | ||
|
|
2a7417dd84 | ||
|
|
490f1a1738 | ||
|
|
446046f886 | ||
|
|
291993d73c | ||
|
|
e8594c3571 | ||
|
|
07aa91f7cb | ||
|
|
4ed35b4202 | ||
|
|
ea66a9b81b | ||
|
|
d0e166c3c6 | ||
|
|
cf9f662655 | ||
|
|
8da8f79c44 | ||
|
|
18f782bba9 | ||
|
|
bc5ca05509 | ||
|
|
9967ffbdc2 | ||
|
|
7df0c840e0 | ||
|
|
d8ed591117 | ||
|
|
8ee3420f0f | ||
|
|
3d7fce56a3 | ||
|
|
f9063712e0 | ||
|
|
6381bc6317 | ||
|
|
a007863096 | ||
|
|
e98f5848a0 | ||
|
|
a65913776d | ||
|
|
a39e409cf3 | ||
|
|
82de4de5ab | ||
|
|
12310c5294 | ||
|
|
884657d546 | ||
|
|
7511d08328 | ||
|
|
2585f209f9 | ||
|
|
9d0976229d | ||
|
|
43cc56eb9e | ||
|
|
1c7bbcc143 | ||
|
|
9cbbc8ae74 | ||
|
|
6fbc269563 | ||
|
|
2bc734aa3f | ||
|
|
89bb531b31 | ||
|
|
744564fcf8 | ||
|
|
da6cb0ed8a | ||
|
|
00ad5bee96 | ||
|
|
141914e817 | ||
|
|
bd4fdfe5f7 | ||
|
|
215f484fb4 | ||
|
|
20047bb6e4 | ||
|
|
8b9757e8b8 | ||
|
|
a84e73b958 | ||
|
|
69997cbdbe | ||
|
|
c221b8596e |
276
.github/workflows/ci.yml
vendored
276
.github/workflows/ci.yml
vendored
@@ -1,160 +1,192 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
- pull_request
|
||||
- push
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- '4.x'
|
||||
- '5.x'
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Cancel in progress workflows
|
||||
# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install --ignore-scripts --include=dev
|
||||
|
||||
- name: Run lint
|
||||
run: npm run lint
|
||||
|
||||
test:
|
||||
name: Run tests
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
name:
|
||||
- Node.js 0.10
|
||||
- Node.js 0.12
|
||||
- io.js 1.x
|
||||
- io.js 2.x
|
||||
- io.js 3.x
|
||||
- Node.js 4.x
|
||||
- Node.js 5.x
|
||||
- Node.js 6.x
|
||||
- Node.js 7.x
|
||||
- Node.js 8.x
|
||||
- Node.js 9.x
|
||||
- Node.js 10.x
|
||||
- Node.js 11.x
|
||||
- Node.js 12.x
|
||||
- Node.js 13.x
|
||||
- Node.js 14.x
|
||||
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node-version:
|
||||
- "0.10"
|
||||
- "0.12"
|
||||
- "4"
|
||||
- "5"
|
||||
- "6"
|
||||
- "7"
|
||||
- "8"
|
||||
- "9"
|
||||
- "10"
|
||||
- "11"
|
||||
- "12"
|
||||
- "13"
|
||||
- "14"
|
||||
- "15"
|
||||
- "16"
|
||||
- "17"
|
||||
- "18"
|
||||
- "19"
|
||||
- "20"
|
||||
- "21"
|
||||
- "22"
|
||||
- "23"
|
||||
- "24"
|
||||
# Use supported versions of our testing tools under older versions of Node
|
||||
# Install npm in some specific cases where we need to
|
||||
include:
|
||||
- name: Node.js 0.10
|
||||
node-version: "0.10"
|
||||
npm-i: mocha@3.5.3 supertest@2.0.0
|
||||
- node-version: "0.10"
|
||||
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
|
||||
# Npm isn't being installed on windows w/ setup-node for
|
||||
# 0.10 and 0.12, which will end up choking when npm uses es6
|
||||
npm-version: "npm@2.15.1"
|
||||
|
||||
- name: Node.js 0.12
|
||||
node-version: "0.12"
|
||||
npm-i: mocha@3.5.3 supertest@2.0.0
|
||||
- node-version: "0.12"
|
||||
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
|
||||
npm-version: "npm@2.15.11"
|
||||
|
||||
- name: io.js 1.x
|
||||
node-version: "1.8"
|
||||
npm-i: mocha@3.5.3 supertest@2.0.0
|
||||
- node-version: "4"
|
||||
npm-i: "mocha@5.2.0 nyc@11.9.0 supertest@3.4.2"
|
||||
|
||||
- name: io.js 2.x
|
||||
node-version: "2.5"
|
||||
npm-i: mocha@3.5.3 supertest@2.0.0
|
||||
- node-version: "5"
|
||||
npm-i: "mocha@5.2.0 nyc@11.9.0 supertest@3.4.2"
|
||||
# fixes https://github.com/npm/cli/issues/681
|
||||
npm-version: "npm@3.10.10"
|
||||
|
||||
- name: io.js 3.x
|
||||
node-version: "3.3"
|
||||
npm-i: mocha@3.5.3 supertest@2.0.0
|
||||
- node-version: "6"
|
||||
npm-i: "mocha@6.2.2 nyc@14.1.1 supertest@3.4.2"
|
||||
|
||||
- name: Node.js 4.x
|
||||
node-version: "4.9"
|
||||
npm-i: mocha@5.2.0 supertest@3.4.2
|
||||
- node-version: "7"
|
||||
npm-i: "mocha@6.2.2 nyc@14.1.1 supertest@6.1.6"
|
||||
|
||||
- name: Node.js 5.x
|
||||
node-version: "5.12"
|
||||
npm-i: mocha@5.2.0 supertest@3.4.2
|
||||
- node-version: "8"
|
||||
npm-i: "mocha@7.2.0 nyc@14.1.1 supertest@6.1.6"
|
||||
|
||||
- name: Node.js 6.x
|
||||
node-version: "6.17"
|
||||
npm-i: mocha@6.2.2
|
||||
- node-version: "9"
|
||||
npm-i: "mocha@7.2.0 nyc@14.1.1 supertest@6.1.6"
|
||||
|
||||
- name: Node.js 7.x
|
||||
node-version: "7.10"
|
||||
npm-i: mocha@6.2.2
|
||||
- node-version: "10"
|
||||
npm-i: "mocha@8.4.0 supertest@6.1.6"
|
||||
|
||||
- name: Node.js 8.x
|
||||
node-version: "8.17"
|
||||
npm-i: mocha@7.2.0
|
||||
- node-version: "11"
|
||||
npm-i: "mocha@8.4.0 supertest@6.1.6"
|
||||
|
||||
- name: Node.js 9.x
|
||||
node-version: "9.11"
|
||||
npm-i: mocha@7.2.0
|
||||
- node-version: "12"
|
||||
npm-i: "mocha@9.2.2 supertest@6.1.6"
|
||||
|
||||
- name: Node.js 10.x
|
||||
node-version: "10.24"
|
||||
npm-i: mocha@8.4.0
|
||||
- node-version: "13"
|
||||
npm-i: "mocha@9.2.2 supertest@6.1.6"
|
||||
|
||||
- name: Node.js 11.x
|
||||
node-version: "11.15"
|
||||
npm-i: mocha@8.4.0
|
||||
|
||||
- name: Node.js 12.x
|
||||
node-version: "12.22"
|
||||
|
||||
- name: Node.js 13.x
|
||||
node-version: "13.14"
|
||||
|
||||
- name: Node.js 14.x
|
||||
node-version: "14.18"
|
||||
- node-version: "15"
|
||||
npm-i: "supertest@6.1.6"
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Node.js ${{ matrix.node-version }}
|
||||
shell: bash -eo pipefail -l {0}
|
||||
run: |
|
||||
nvm install --default ${{ matrix.node-version }}
|
||||
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Configure npm
|
||||
run: |
|
||||
npm config set loglevel error
|
||||
npm config set shrinkwrap false
|
||||
- name: Npm version fixes
|
||||
if: ${{matrix.npm-version != ''}}
|
||||
run: npm install -g ${{ matrix.npm-version }}
|
||||
|
||||
- name: Install npm module(s) ${{ matrix.npm-i }}
|
||||
run: npm install --save-dev ${{ matrix.npm-i }}
|
||||
if: matrix.npm-i != ''
|
||||
- name: Configure npm loglevel
|
||||
run: |
|
||||
npm config set loglevel error
|
||||
shell: bash
|
||||
|
||||
- name: Remove non-test dependencies
|
||||
run: npm rm --silent --save-dev connect-redis
|
||||
- name: Install Node version specific dev deps
|
||||
if: ${{ matrix.npm-i != '' }}
|
||||
run: npm install --save-dev ${{ matrix.npm-i }}
|
||||
|
||||
- name: Setup Node.js version-specific dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
# eslint for linting
|
||||
# - remove on Node.js < 10
|
||||
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then
|
||||
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
|
||||
grep -E '^eslint(-|$)' | \
|
||||
sort -r | \
|
||||
xargs -n1 npm rm --silent --save-dev
|
||||
fi
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: npm install
|
||||
- name: Remove non-test dependencies
|
||||
run: npm rm --silent --save-dev connect-redis
|
||||
|
||||
- name: List environment
|
||||
id: list_env
|
||||
shell: bash
|
||||
run: |
|
||||
echo "node@$(node -v)"
|
||||
echo "npm@$(npm -v)"
|
||||
npm -s ls ||:
|
||||
- name: Output Node and NPM versions
|
||||
run: |
|
||||
echo "Node.js version: $(node -v)"
|
||||
echo "NPM version: $(npm -v)"
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: npm run test-ci
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: npm run test-ci
|
||||
|
||||
- name: Lint code
|
||||
if: steps.list_env.outputs.eslint != ''
|
||||
run: npm run lint
|
||||
|
||||
- name: Collect code coverage
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
flag-name: run-${{ matrix.test_number }}
|
||||
parallel: true
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
|
||||
path: ./coverage/lcov.info
|
||||
retention-days: 1
|
||||
|
||||
coverage:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
steps:
|
||||
- name: Upload code coverage
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.github_token }}
|
||||
parallel-finished: true
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install lcov
|
||||
shell: bash
|
||||
run: sudo apt-get -y install lcov
|
||||
|
||||
- name: Collect coverage reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./coverage
|
||||
pattern: coverage-node-*
|
||||
|
||||
- name: Merge coverage reports
|
||||
shell: bash
|
||||
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: coverallsapp/github-action@v2
|
||||
with:
|
||||
file: ./lcov.info
|
||||
|
||||
66
.github/workflows/codeql.yml
vendored
Normal file
66
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ["master"]
|
||||
schedule:
|
||||
- cron: "0 0 * * 1"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7
|
||||
with:
|
||||
languages: javascript
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
# - name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7
|
||||
with:
|
||||
category: "/language:javascript"
|
||||
69
.github/workflows/iojs.yml
vendored
Normal file
69
.github/workflows/iojs.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: iojs-ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- '4.x'
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: ["1.8", "2.5", "3.3"]
|
||||
include:
|
||||
- node-version: "1.8"
|
||||
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
|
||||
- node-version: "2.5"
|
||||
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
|
||||
- node-version: "3.3"
|
||||
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install iojs ${{ matrix.node-version }}
|
||||
shell: bash -eo pipefail -l {0}
|
||||
run: |
|
||||
nvm install --default ${{ matrix.node-version }}
|
||||
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Configure npm
|
||||
run: |
|
||||
npm config set loglevel error
|
||||
npm config set shrinkwrap false
|
||||
|
||||
- name: Install npm module(s) ${{ matrix.npm-i }}
|
||||
run: npm install --save-dev ${{ matrix.npm-i }}
|
||||
if: matrix.npm-i != ''
|
||||
|
||||
- name: Remove non-test dependencies
|
||||
run: npm rm --silent --save-dev connect-redis
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: npm install
|
||||
|
||||
- name: List environment
|
||||
id: list_env
|
||||
shell: bash
|
||||
run: |
|
||||
echo "node@$(node -v)"
|
||||
echo "npm@$(npm -v)"
|
||||
npm -s ls ||:
|
||||
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: npm run test
|
||||
|
||||
21
.gitignore
vendored
21
.gitignore
vendored
@@ -1,27 +1,16 @@
|
||||
# OS X
|
||||
.DS_Store*
|
||||
Icon?
|
||||
._*
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Linux
|
||||
.directory
|
||||
*~
|
||||
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
package-lock.json
|
||||
npm-shrinkwrap.json
|
||||
*.log
|
||||
*.gz
|
||||
|
||||
|
||||
# Coveralls
|
||||
.nyc_output
|
||||
coverage
|
||||
|
||||
# Benchmarking
|
||||
benchmarks/graphs
|
||||
|
||||
# ignore additional files using core.excludesFile
|
||||
# https://git-scm.com/docs/gitignore
|
||||
|
||||
@@ -9,7 +9,7 @@ also easily visible to outsiders.
|
||||
|
||||
## Section 1: Scope
|
||||
|
||||
Express is a http web server framework with a simple and expressive API
|
||||
Express is a HTTP web server framework with a simple and expressive API
|
||||
which is highly aligned with Node.js core. We aim to be the best in
|
||||
class for writing performant, spec compliant, and powerful web servers
|
||||
in Node.js. As one of the oldest and most popular web frameworks in
|
||||
@@ -24,7 +24,7 @@ Express is made of many modules spread between three GitHub Orgs:
|
||||
libraries
|
||||
- [pillarjs](http://github.com/pillarjs/): Components which make up
|
||||
Express but can also be used for other web frameworks
|
||||
- [jshttp](http://github.com/jshttp/): Low level http libraries
|
||||
- [jshttp](http://github.com/jshttp/): Low level HTTP libraries
|
||||
|
||||
### 1.2: Out-of-Scope
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ project community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant, version 2.0](cc-20-doc).
|
||||
This Code of Conduct is adapted from the [Contributor Covenant, version 2.0][cc-20-doc].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
152
Contributing.md
152
Contributing.md
@@ -12,6 +12,7 @@ contributors can be involved in decision making.
|
||||
|
||||
* A **Contributor** is any individual creating or commenting on an issue or pull request.
|
||||
* A **Committer** is a subset of contributors who have been given write access to the repository.
|
||||
* A **Project Captain** is the lead maintainer of a repository.
|
||||
* A **TC (Technical Committee)** is a group of committers representing the required technical
|
||||
expertise to resolve rare disputes.
|
||||
* A **Triager** is a subset of contributors who have been given triage access to the repository.
|
||||
@@ -62,29 +63,14 @@ compromise among committers be the default resolution mechanism.
|
||||
Anyone can become a triager! Read more about the process of being a triager in
|
||||
[the triage process document](Triager-Guide.md).
|
||||
|
||||
[Open an issue in `expressjs/express` repo](https://github.com/expressjs/express/issues/new)
|
||||
to request the triage role. State that you have read and agree to the
|
||||
[Code of Conduct](Code-Of-Conduct.md) and details of the role.
|
||||
Currently, any existing [organization member](https://github.com/orgs/expressjs/people) can nominate
|
||||
a new triager. If you are interested in becoming a triager, our best advice is to actively participate
|
||||
in the community by helping triaging issues and pull requests. As well we recommend
|
||||
to engage in other community activities like attending the TC meetings, and participating in the Slack
|
||||
discussions.
|
||||
|
||||
Here is an example issue content you can copy and paste:
|
||||
|
||||
```
|
||||
Title: Request triager role for <your GitHub username>
|
||||
|
||||
I have read and understood the project's Code of Conduct.
|
||||
I also have read and understood the process and best practices around Express triaging.
|
||||
|
||||
I request for a triager role for the following GitHub organizations:
|
||||
|
||||
jshttp
|
||||
pillarjs
|
||||
express
|
||||
```
|
||||
|
||||
Once you have opened your issue, a member of the TC will add you to the `triage` team in
|
||||
the organizations requested. They will then close the issue.
|
||||
|
||||
Happy triaging!
|
||||
You can also reach out to any of the [organization members](https://github.com/orgs/expressjs/people)
|
||||
if you have questions or need guidance.
|
||||
|
||||
## Becoming a Committer
|
||||
|
||||
@@ -102,12 +88,122 @@ If a consensus cannot be reached that has no objections then a majority wins vot
|
||||
is called. It is also expected that the majority of decisions made by the TC are via
|
||||
a consensus seeking process and that voting is only used as a last-resort.
|
||||
|
||||
Resolution may involve returning the issue to committers with suggestions on how to
|
||||
move forward towards a consensus. It is not expected that a meeting of the TC
|
||||
Resolution may involve returning the issue to project captains with suggestions on
|
||||
how to move forward towards a consensus. It is not expected that a meeting of the TC
|
||||
will resolve all issues on its agenda during that meeting and may prefer to continue
|
||||
the discussion happening among the committers.
|
||||
the discussion happening among the project captains.
|
||||
|
||||
Members can be added to the TC at any time. Any committer can nominate another committer
|
||||
Members can be added to the TC at any time. Any TC member can nominate another committer
|
||||
to the TC and the TC uses its standard consensus seeking process to evaluate whether or
|
||||
not to add this new member. Members who do not participate consistently at the level of
|
||||
a majority of the other members are expected to resign.
|
||||
not to add this new member. The TC will consist of a minimum of 3 active members and a
|
||||
maximum of 10. If the TC should drop below 5 members the active TC members should nominate
|
||||
someone new. If a TC member is stepping down, they are encouraged (but not required) to
|
||||
nominate someone to take their place.
|
||||
|
||||
TC members will be added as admin's on the Github orgs, npm orgs, and other resources as
|
||||
necessary to be effective in the role.
|
||||
|
||||
To remain "active" a TC member should have participation within the last 12 months and miss
|
||||
no more than six consecutive TC meetings. Our goal is to increase participation, not punish
|
||||
people for any lack of participation, this guideline should be only be used as such
|
||||
(replace an inactive member with a new active one, for example). Members who do not meet this
|
||||
are expected to step down. If A TC member does not step down, an issue can be opened in the
|
||||
discussions repo to move them to inactive status. TC members who step down or are removed due
|
||||
to inactivity will be moved into inactive status.
|
||||
|
||||
Inactive status members can become active members by self nomination if the TC is not already
|
||||
larger than the maximum of 10. They will also be given preference if, while at max size, an
|
||||
active member steps down.
|
||||
|
||||
## Project Captains
|
||||
|
||||
The Express TC can designate captains for individual projects/repos in the
|
||||
organizations. These captains are responsible for being the primary
|
||||
day-to-day maintainers of the repo on a technical and community front.
|
||||
Repo captains are empowered with repo ownership and package publication rights.
|
||||
When there are conflicts, especially on topics that effect the Express project
|
||||
at large, captains are responsible to raise it up to the TC and drive
|
||||
those conflicts to resolution. Captains are also responsible for making sure
|
||||
community members follow the community guidelines, maintaining the repo
|
||||
and the published package, as well as in providing user support.
|
||||
|
||||
Like TC members, Repo captains are a subset of committers.
|
||||
|
||||
To become a captain for a project the candidate is expected to participate in that
|
||||
project for at least 6 months as a committer prior to the request. They should have
|
||||
helped with code contributions as well as triaging issues. They are also required to
|
||||
have 2FA enabled on both their GitHub and npm accounts. Any TC member or existing
|
||||
captain on the repo can nominate another committer to the captain role, submit a PR to
|
||||
this doc, under `Current Project Captains` section (maintaining the sort order) with
|
||||
the project, their GitHub handle and npm username (if different). The PR will require
|
||||
at least 2 approvals from TC members and 2 weeks hold time to allow for comment and/or
|
||||
dissent. When the PR is merged, a TC member will add them to the proper GitHub/npm groups.
|
||||
|
||||
### Active Projects and Captains
|
||||
|
||||
- `expressjs/badgeboard`: @wesleytodd
|
||||
- `expressjs/basic-auth-connect`: N/A
|
||||
- `expressjs/body-parser`: @wesleytodd, @jonchurch
|
||||
- `expressjs/compression`: N/A
|
||||
- `expressjs/connect-multiparty`: N/A
|
||||
- `expressjs/cookie-parser`: @wesleytodd, @UlisesGascon
|
||||
- `expressjs/cookie-session`: N/A
|
||||
- `expressjs/cors`: @jonchurch
|
||||
- `expressjs/discussions`: @wesleytodd
|
||||
- `expressjs/errorhandler`: N/A
|
||||
- `expressjs/express-paginate`: N/A
|
||||
- `expressjs/express`: @wesleytodd
|
||||
- `expressjs/expressjs.com`: @crandmck, @jonchurch
|
||||
- `expressjs/flash`: N/A
|
||||
- `expressjs/generator`: @wesleytodd
|
||||
- `expressjs/method-override`: N/A
|
||||
- `expressjs/morgan`: @jonchurch
|
||||
- `expressjs/multer`: @LinusU
|
||||
- `expressjs/response-time`: @blakeembrey
|
||||
- `expressjs/serve-favicon`: N/A
|
||||
- `expressjs/serve-index`: N/A
|
||||
- `expressjs/serve-static`: N/A
|
||||
- `expressjs/session`: N/A
|
||||
- `expressjs/statusboard`: @wesleytodd
|
||||
- `expressjs/timeout`: N/A
|
||||
- `expressjs/vhost`: N/A
|
||||
- `jshttp/accepts`: @blakeembrey
|
||||
- `jshttp/basic-auth`: @blakeembrey
|
||||
- `jshttp/compressible`: @blakeembrey
|
||||
- `jshttp/content-disposition`: @blakeembrey
|
||||
- `jshttp/content-type`: @blakeembrey
|
||||
- `jshttp/cookie`: @wesleytodd
|
||||
- `jshttp/etag`: @blakeembrey
|
||||
- `jshttp/forwarded`: @blakeembrey
|
||||
- `jshttp/fresh`: @blakeembrey
|
||||
- `jshttp/http-assert`: @wesleytodd, @jonchurch
|
||||
- `jshttp/http-errors`: @wesleytodd, @jonchurch
|
||||
- `jshttp/media-typer`: @blakeembrey
|
||||
- `jshttp/methods`: @blakeembrey
|
||||
- `jshttp/mime-db`: @blakeembrey, @UlisesGascon
|
||||
- `jshttp/mime-types`: @blakeembrey, @UlisesGascon
|
||||
- `jshttp/negotiator`: @blakeembrey
|
||||
- `jshttp/on-finished`: @wesleytodd
|
||||
- `jshttp/on-headers`: @blakeembrey
|
||||
- `jshttp/proxy-addr`: @wesleytodd
|
||||
- `jshttp/range-parser`: @blakeembrey
|
||||
- `jshttp/statuses`: @blakeembrey
|
||||
- `jshttp/type-is`: @blakeembrey
|
||||
- `jshttp/vary`: @blakeembrey
|
||||
- `pillarjs/cookies`: @blakeembrey
|
||||
- `pillarjs/csrf`: N/A
|
||||
- `pillarjs/encodeurl`: @blakeembrey
|
||||
- `pillarjs/finalhandler`: @wesleytodd
|
||||
- `pillarjs/hbs`: N/A
|
||||
- `pillarjs/multiparty`: @blakeembrey
|
||||
- `pillarjs/parseurl`: @blakeembrey
|
||||
- `pillarjs/path-to-regexp`: @blakeembrey
|
||||
- `pillarjs/request`: @wesleytodd
|
||||
- `pillarjs/resolve-path`: @blakeembrey
|
||||
- `pillarjs/router`: @blakeembrey
|
||||
- `pillarjs/send`: @blakeembrey
|
||||
- `pillarjs/understanding-csrf`: N/A
|
||||
|
||||
### Current Initiative Captains
|
||||
|
||||
- Triage team [ref](https://github.com/expressjs/discussions/issues/227): @UlisesGascon
|
||||
|
||||
168
History.md
168
History.md
@@ -1,3 +1,161 @@
|
||||
4.22.1 / 2025-12-01
|
||||
==========
|
||||
|
||||
* Revert security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))
|
||||
* The prior release (4.22.0) included an erroneous breaking change related to the extended query parser. There is no actual security vulnerability associated with this behavior (CVE-2024-51999 has been rejected). The change has been fully reverted in this release.
|
||||
|
||||
4.22.0 / 2025-12-01
|
||||
==========
|
||||
* Security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))
|
||||
* deps: use tilde notation for dependencies
|
||||
* deps: qs@6.14.0
|
||||
|
||||
4.21.2 / 2024-11-06
|
||||
==========
|
||||
|
||||
* deps: path-to-regexp@0.1.12
|
||||
- Fix backtracking protection
|
||||
* deps: path-to-regexp@0.1.11
|
||||
- Throws an error on invalid path values
|
||||
|
||||
4.21.1 / 2024-10-08
|
||||
==========
|
||||
|
||||
* Backported a fix for [CVE-2024-47764](https://nvd.nist.gov/vuln/detail/CVE-2024-47764)
|
||||
|
||||
|
||||
4.21.0 / 2024-09-11
|
||||
==========
|
||||
|
||||
* Deprecate `res.location("back")` and `res.redirect("back")` magic string
|
||||
* deps: serve-static@1.16.2
|
||||
* includes send@0.19.0
|
||||
* deps: finalhandler@1.3.1
|
||||
* deps: qs@6.13.0
|
||||
|
||||
4.20.0 / 2024-09-10
|
||||
==========
|
||||
* deps: serve-static@0.16.0
|
||||
* Remove link renderization in html while redirecting
|
||||
* deps: send@0.19.0
|
||||
* Remove link renderization in html while redirecting
|
||||
* deps: body-parser@0.6.0
|
||||
* add `depth` option to customize the depth level in the parser
|
||||
* IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`)
|
||||
* Remove link renderization in html while using `res.redirect`
|
||||
* deps: path-to-regexp@0.1.10
|
||||
- Adds support for named matching groups in the routes using a regex
|
||||
- Adds backtracking protection to parameters without regexes defined
|
||||
* deps: encodeurl@~2.0.0
|
||||
- Removes encoding of `\`, `|`, and `^` to align better with URL spec
|
||||
* Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie`
|
||||
- Will be ignored in v5, clearCookie will set a cookie with an expires in the past to instruct clients to delete the cookie
|
||||
|
||||
4.19.2 / 2024-03-25
|
||||
==========
|
||||
|
||||
* Improved fix for open redirect allow list bypass
|
||||
|
||||
4.19.1 / 2024-03-20
|
||||
==========
|
||||
|
||||
* Allow passing non-strings to res.location with new encoding handling checks
|
||||
|
||||
4.19.0 / 2024-03-20
|
||||
==========
|
||||
|
||||
* Prevent open redirect allow list bypass due to encodeurl
|
||||
* deps: cookie@0.6.0
|
||||
|
||||
4.18.3 / 2024-02-29
|
||||
==========
|
||||
|
||||
* Fix routing requests without method
|
||||
* deps: body-parser@1.20.2
|
||||
- Fix strict json error message on Node.js 19+
|
||||
- deps: content-type@~1.0.5
|
||||
- deps: raw-body@2.5.2
|
||||
* deps: cookie@0.6.0
|
||||
- Add `partitioned` option
|
||||
|
||||
4.18.2 / 2022-10-08
|
||||
===================
|
||||
|
||||
* Fix regression routing a large stack in a single route
|
||||
* deps: body-parser@1.20.1
|
||||
- deps: qs@6.11.0
|
||||
- perf: remove unnecessary object clone
|
||||
* deps: qs@6.11.0
|
||||
|
||||
4.18.1 / 2022-04-29
|
||||
===================
|
||||
|
||||
* Fix hanging on large stack of sync routes
|
||||
|
||||
4.18.0 / 2022-04-25
|
||||
===================
|
||||
|
||||
* Add "root" option to `res.download`
|
||||
* Allow `options` without `filename` in `res.download`
|
||||
* Deprecate string and non-integer arguments to `res.status`
|
||||
* Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie`
|
||||
* Fix handling very large stacks of sync middleware
|
||||
* Ignore `Object.prototype` values in settings through `app.set`/`app.get`
|
||||
* Invoke `default` with same arguments as types in `res.format`
|
||||
* Support proper 205 responses using `res.send`
|
||||
* Use `http-errors` for `res.format` error
|
||||
* deps: body-parser@1.20.0
|
||||
- Fix error message for json parse whitespace in `strict`
|
||||
- Fix internal error when inflated body exceeds limit
|
||||
- Prevent loss of async hooks context
|
||||
- Prevent hanging when request already read
|
||||
- deps: depd@2.0.0
|
||||
- deps: http-errors@2.0.0
|
||||
- deps: on-finished@2.4.1
|
||||
- deps: qs@6.10.3
|
||||
- deps: raw-body@2.5.1
|
||||
* deps: cookie@0.5.0
|
||||
- Add `priority` option
|
||||
- Fix `expires` option to reject invalid dates
|
||||
* deps: depd@2.0.0
|
||||
- Replace internal `eval` usage with `Function` constructor
|
||||
- Use instance methods on `process` to check for listeners
|
||||
* deps: finalhandler@1.2.0
|
||||
- Remove set content headers that break response
|
||||
- deps: on-finished@2.4.1
|
||||
- deps: statuses@2.0.1
|
||||
* deps: on-finished@2.4.1
|
||||
- Prevent loss of async hooks context
|
||||
* deps: qs@6.10.3
|
||||
* deps: send@0.18.0
|
||||
- Fix emitted 416 error missing headers property
|
||||
- Limit the headers removed for 304 response
|
||||
- deps: depd@2.0.0
|
||||
- deps: destroy@1.2.0
|
||||
- deps: http-errors@2.0.0
|
||||
- deps: on-finished@2.4.1
|
||||
- deps: statuses@2.0.1
|
||||
* deps: serve-static@1.15.0
|
||||
- deps: send@0.18.0
|
||||
* deps: statuses@2.0.1
|
||||
- Remove code 306
|
||||
- Rename `425 Unordered Collection` to standard `425 Too Early`
|
||||
|
||||
4.17.3 / 2022-02-16
|
||||
===================
|
||||
|
||||
* deps: accepts@~1.3.8
|
||||
- deps: mime-types@~2.1.34
|
||||
- deps: negotiator@0.6.3
|
||||
* deps: body-parser@1.19.2
|
||||
- deps: bytes@3.1.2
|
||||
- deps: qs@6.9.7
|
||||
- deps: raw-body@2.4.3
|
||||
* deps: cookie@0.4.2
|
||||
* deps: qs@6.9.7
|
||||
* Fix handling of `__proto__` keys
|
||||
* pref: remove unnecessary regexp for trust proxy
|
||||
|
||||
4.17.2 / 2021-12-16
|
||||
===================
|
||||
|
||||
@@ -2033,7 +2191,7 @@
|
||||
* deps: connect@2.21.0
|
||||
- deprecate `connect(middleware)` -- use `app.use(middleware)` instead
|
||||
- deprecate `connect.createServer()` -- use `connect()` instead
|
||||
- fix `res.setHeader()` patch to work with with get -> append -> set pattern
|
||||
- fix `res.setHeader()` patch to work with get -> append -> set pattern
|
||||
- deps: compression@~1.0.8
|
||||
- deps: errorhandler@~1.1.1
|
||||
- deps: express-session@~1.5.0
|
||||
@@ -3244,8 +3402,8 @@ Shaw]
|
||||
* Added node v0.1.97 compatibility
|
||||
* Added support for deleting cookies via Request#cookie('key', null)
|
||||
* Updated haml submodule
|
||||
* Fixed not-found page, now using using charset utf-8
|
||||
* Fixed show-exceptions page, now using using charset utf-8
|
||||
* Fixed not-found page, now using charset utf-8
|
||||
* Fixed show-exceptions page, now using charset utf-8
|
||||
* Fixed view support due to fs.readFile Buffers
|
||||
* Changed; mime.type() no longer accepts ".type" due to node extname() changes
|
||||
|
||||
@@ -3280,7 +3438,7 @@ Shaw]
|
||||
==================
|
||||
|
||||
* Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s
|
||||
encoding is set to 'utf8' or 'utf-8'.
|
||||
encoding is set to 'utf8' or 'utf-8').
|
||||
* Added "encoding" option to Request#render(). Closes #299
|
||||
* Added "dump exceptions" setting, which is enabled by default.
|
||||
* Added simple ejs template engine support
|
||||
@@ -3319,7 +3477,7 @@ Shaw]
|
||||
* Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js
|
||||
* Added callback function support to Request#halt() as 3rd/4th arg
|
||||
* Added preprocessing of route param wildcards using param(). Closes #251
|
||||
* Added view partial support (with collections etc)
|
||||
* Added view partial support (with collections etc.)
|
||||
* Fixed bug preventing falsey params (such as ?page=0). Closes #286
|
||||
* Fixed setting of multiple cookies. Closes #199
|
||||
* Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)
|
||||
|
||||
180
Readme.md
180
Readme.md
@@ -1,12 +1,29 @@
|
||||
[](http://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
**Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org).**
|
||||
|
||||
**This project has a [Code of Conduct][].**
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Installation](#Installation)
|
||||
* [Features](#Features)
|
||||
* [Docs & Community](#docs--community)
|
||||
* [Quick Start](#Quick-Start)
|
||||
* [Running Tests](#Running-Tests)
|
||||
* [Philosophy](#Philosophy)
|
||||
* [Examples](#Examples)
|
||||
* [Contributing to Express](#Contributing)
|
||||
* [TC (Technical Committee)](#tc-technical-committee)
|
||||
* [Triagers](#triagers)
|
||||
* [License](#license)
|
||||
|
||||
|
||||
[![NPM Version][npm-version-image]][npm-url]
|
||||
[![NPM Install Size][npm-install-size-image]][npm-install-size-url]
|
||||
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
|
||||
[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
|
||||
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
[![Linux Build][ci-image]][ci-url]
|
||||
[![Windows Build][appveyor-image]][appveyor-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
```js
|
||||
const express = require('express')
|
||||
@@ -33,7 +50,7 @@ the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
|
||||
Installation is done using the
|
||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ npm install express
|
||||
```
|
||||
|
||||
@@ -53,7 +70,7 @@ for more information.
|
||||
## Docs & Community
|
||||
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
|
||||
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
|
||||
* [#express](https://web.libera.chat/#express) on [Libera Chat](https://libera.chat) IRC
|
||||
* [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||
* Visit the [Wiki](https://github.com/expressjs/express/wiki)
|
||||
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
||||
@@ -61,35 +78,31 @@ for more information.
|
||||
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/expressjs/express/wiki/New-features-in-4.x).
|
||||
|
||||
### Security Issues
|
||||
|
||||
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:
|
||||
|
||||
Install the executable. The executable's major version will match Express's:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ npm install -g express-generator@4
|
||||
```
|
||||
|
||||
Create the app:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
```
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Start the server:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ npm start
|
||||
```
|
||||
|
||||
@@ -109,50 +122,139 @@ $ npm start
|
||||
|
||||
To view the examples, clone the Express repo and install the dependencies:
|
||||
|
||||
```bash
|
||||
$ git clone git://github.com/expressjs/express.git --depth 1
|
||||
```console
|
||||
$ git clone https://github.com/expressjs/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Then run whichever example you want:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ node examples/content-negotiation
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
To run the test suite, first install the dependencies, then run `npm test`:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
[Contributing Guide](Contributing.md)
|
||||
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
|
||||
[![Windows Build][appveyor-image]][appveyor-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
The Express.js project welcomes all constructive contributions. Contributions take many forms,
|
||||
from code for bug fixes and enhancements, to additions and fixes to documentation, additional
|
||||
tests, triaging incoming pull requests and issues, and more!
|
||||
|
||||
See the [Contributing Guide](Contributing.md) for more technical details on contributing.
|
||||
|
||||
### Security Issues
|
||||
|
||||
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the test suite, first install the dependencies, then run `npm test`:
|
||||
|
||||
```console
|
||||
$ npm install
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## People
|
||||
|
||||
The original author of Express is [TJ Holowaychuk](https://github.com/tj)
|
||||
|
||||
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson)
|
||||
|
||||
[List of all contributors](https://github.com/expressjs/express/graphs/contributors)
|
||||
|
||||
### TC (Technical Committee)
|
||||
|
||||
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
|
||||
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
|
||||
* [wesleytodd](https://github.com/wesleytodd) - **Wes Todd**
|
||||
* [LinusU](https://github.com/LinusU) - **Linus Unnebäck**
|
||||
* [blakeembrey](https://github.com/blakeembrey) - **Blake Embrey**
|
||||
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
|
||||
* [crandmck](https://github.com/crandmck) - **Rand McKinney**
|
||||
* [ctcpip](https://github.com/ctcpip) - **Chris de Almeida**
|
||||
|
||||
<details>
|
||||
<summary>TC emeriti members</summary>
|
||||
|
||||
#### TC emeriti members
|
||||
|
||||
* [dougwilson](https://github.com/dougwilson) - **Douglas Wilson**
|
||||
* [hacksparrow](https://github.com/hacksparrow) - **Hage Yaapa**
|
||||
* [jonathanong](https://github.com/jonathanong) - **jongleberry**
|
||||
* [niftylettuce](https://github.com/niftylettuce) - **niftylettuce**
|
||||
* [troygoode](https://github.com/troygoode) - **Troy Goode**
|
||||
</details>
|
||||
|
||||
|
||||
### Triagers
|
||||
|
||||
* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair**
|
||||
* [carpasse](https://github.com/carpasse) - **Carlos Serrano**
|
||||
* [CBID2](https://github.com/CBID2) - **Christine Belzie**
|
||||
* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**
|
||||
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
|
||||
* [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin**
|
||||
* [0ss](https://github.com/0ss) - **Salah**
|
||||
* [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him)
|
||||
* [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi**
|
||||
* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)
|
||||
* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**
|
||||
* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him)
|
||||
* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
|
||||
|
||||
<details>
|
||||
<summary>Triagers emeriti members</summary>
|
||||
|
||||
#### Emeritus Triagers
|
||||
|
||||
* [AuggieH](https://github.com/AuggieH) - **Auggie Hudak**
|
||||
* [G-Rath](https://github.com/G-Rath) - **Gareth Jones**
|
||||
* [MohammadXroid](https://github.com/MohammadXroid) - **Mohammad Ayashi**
|
||||
* [NawafSwe](https://github.com/NawafSwe) - **Nawaf Alsharqi**
|
||||
* [NotMoni](https://github.com/NotMoni) - **Moni**
|
||||
* [VigneshMurugan](https://github.com/VigneshMurugan) - **Vignesh Murugan**
|
||||
* [davidmashe](https://github.com/davidmashe) - **David Ashe**
|
||||
* [digitaIfabric](https://github.com/digitaIfabric) - **David**
|
||||
* [e-l-i-s-e](https://github.com/e-l-i-s-e) - **Elise Bonner**
|
||||
* [fed135](https://github.com/fed135) - **Frederic Charette**
|
||||
* [firmanJS](https://github.com/firmanJS) - **Firman Abdul Hakim**
|
||||
* [getspooky](https://github.com/getspooky) - **Yasser Ameur**
|
||||
* [ghinks](https://github.com/ghinks) - **Glenn**
|
||||
* [ghousemohamed](https://github.com/ghousemohamed) - **Ghouse Mohamed**
|
||||
* [gireeshpunathil](https://github.com/gireeshpunathil) - **Gireesh Punathil**
|
||||
* [jake32321](https://github.com/jake32321) - **Jake Reed**
|
||||
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
|
||||
* [lekanikotun](https://github.com/lekanikotun) - **Troy Goode**
|
||||
* [marsonya](https://github.com/marsonya) - **Lekan Ikotun**
|
||||
* [mastermatt](https://github.com/mastermatt) - **Matt R. Wilson**
|
||||
* [maxakuru](https://github.com/maxakuru) - **Max Edell**
|
||||
* [mlrawlings](https://github.com/mlrawlings) - **Michael Rawlings**
|
||||
* [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov**
|
||||
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
|
||||
* [tarunyadav1](https://github.com/tarunyadav1) - **Tarun yadav**
|
||||
* [tunniclm](https://github.com/tunniclm) - **Mike Tunnicliffe**
|
||||
</details>
|
||||
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[ci-image]: https://img.shields.io/github/workflow/status/expressjs/express/ci/master.svg?label=linux
|
||||
[ci-url]: https://github.com/expressjs/express/actions?query=workflow%3Aci
|
||||
[npm-image]: https://img.shields.io/npm/v/express.svg
|
||||
[npm-url]: https://npmjs.org/package/express
|
||||
[downloads-image]: https://img.shields.io/npm/dm/express.svg
|
||||
[downloads-url]: https://npmcharts.com/compare/express?minimal=true
|
||||
[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/express/master.svg?label=windows
|
||||
[appveyor-image]: https://badgen.net/appveyor/ci/dougwilson/express/master?label=windows
|
||||
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/express
|
||||
[coveralls-image]: https://img.shields.io/coveralls/expressjs/express/master.svg
|
||||
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/express/master
|
||||
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
|
||||
[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/express/master?label=linux
|
||||
[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml
|
||||
[npm-downloads-image]: https://badgen.net/npm/dm/express
|
||||
[npm-downloads-url]: https://npmcharts.com/compare/express?minimal=true
|
||||
[npm-install-size-image]: https://badgen.net/packagephobia/install/express
|
||||
[npm-install-size-url]: https://packagephobia.com/result?p=express
|
||||
[npm-url]: https://npmjs.org/package/express
|
||||
[npm-version-image]: https://badgen.net/npm/v/express
|
||||
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge
|
||||
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/express
|
||||
[Code of Conduct]: https://github.com/expressjs/express/blob/master/Code-Of-Conduct.md
|
||||
|
||||
@@ -77,6 +77,13 @@ non-patch flow.
|
||||
- This branch contains the commits accepted so far that implement the proposal
|
||||
in the tracking pull request.
|
||||
|
||||
### Pre-release Versions
|
||||
|
||||
Alpha and Beta releases are made from a proposal branch. The version number should be
|
||||
incremented to the next minor version with a `-beta` or `-alpha` suffix.
|
||||
For example, if the next beta release is `5.0.1`, the beta release would be `5.0.1-beta.0`.
|
||||
The pre-releases are unstable and not suitable for production use.
|
||||
|
||||
### Patch flow
|
||||
|
||||
In the patch flow, simple changes are committed to the release branch which
|
||||
@@ -184,3 +191,9 @@ $ npm publish
|
||||
|
||||
**NOTE:** The version number to publish will be picked up automatically from
|
||||
package.json.
|
||||
|
||||
### Step 7. Update documentation website
|
||||
|
||||
The documentation website https://expressjs.com/ documents the current release version in various places. For a new release:
|
||||
1. Change the value of `current_version` in https://github.com/expressjs/expressjs.com/blob/gh-pages/_data/express.yml to match the latest version number.
|
||||
2. Add a new section to the change log. For example, for a 4.x release, https://github.com/expressjs/expressjs.com/blob/gh-pages/en/changelog/4x.md,
|
||||
|
||||
13
Security.md
13
Security.md
@@ -27,8 +27,13 @@ endeavor to keep you informed of the progress towards a fix and full
|
||||
announcement, and may ask for additional information or guidance.
|
||||
|
||||
Report security bugs in third-party modules to the person or team maintaining
|
||||
the module. You can also report a vulnerability through the
|
||||
[Node Security Project](https://nodesecurity.io/report).
|
||||
the module.
|
||||
|
||||
## Pre-release Versions
|
||||
|
||||
Alpha and Beta releases are unstable and **not suitable for production use**.
|
||||
Vulnerabilities found in pre-releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section.
|
||||
Due to the unstable nature of the branch it is not guaranteed that any fixes will be released in the next pre-release.
|
||||
|
||||
## Disclosure Policy
|
||||
|
||||
@@ -41,6 +46,10 @@ involving the following steps:
|
||||
* Prepare fixes for all releases still under maintenance. These fixes will be
|
||||
released as fast as possible to npm.
|
||||
|
||||
## The Express Threat Model
|
||||
|
||||
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)
|
||||
|
||||
## Comments on this Policy
|
||||
|
||||
If you have suggestions on how this process could be improved please submit a
|
||||
|
||||
@@ -9,11 +9,18 @@ classification:
|
||||
|
||||
* `needs triage`: This can be kept if the triager is unsure which next steps to take
|
||||
* `awaiting more info`: If more info has been requested from the author, apply this label.
|
||||
* `question`: User questions that do not appear to be bugs or enhancements.
|
||||
* `discuss`: Topics for discussion. Might end in an `enhancement` or `question` label.
|
||||
* `bug`: Issues that present a reasonable conviction there is a reproducible bug.
|
||||
* `enhancement`: Issues that are found to be a reasonable candidate feature additions.
|
||||
|
||||
If the issue is a question or discussion, it should be moved to GitHub Discussions.
|
||||
|
||||
### Moving Discussions and Questions to GitHub Discussions
|
||||
|
||||
For issues labeled with `question` or `discuss`, it is recommended to move them to GitHub Discussions instead:
|
||||
|
||||
* **Questions**: User questions that do not appear to be bugs or enhancements should be moved to GitHub Discussions.
|
||||
* **Discussions**: Topics for discussion should be moved to GitHub Discussions. If the discussion leads to a new feature or bug identification, it can be moved back to Issues.
|
||||
|
||||
In all cases, issues may be closed by maintainers if they don't receive a timely response when
|
||||
further information is sought, or when additional questions are asked.
|
||||
|
||||
|
||||
40
appveyor.yml
40
appveyor.yml
@@ -15,18 +15,30 @@ environment:
|
||||
- nodejs_version: "11.15"
|
||||
- nodejs_version: "12.22"
|
||||
- nodejs_version: "13.14"
|
||||
- nodejs_version: "14.18"
|
||||
- nodejs_version: "14.20"
|
||||
- nodejs_version: "15.14"
|
||||
- nodejs_version: "16.20"
|
||||
- nodejs_version: "17.9"
|
||||
- nodejs_version: "18.19"
|
||||
- nodejs_version: "19.9"
|
||||
- nodejs_version: "20.11"
|
||||
- nodejs_version: "21.6"
|
||||
- nodejs_version: "22.0"
|
||||
cache:
|
||||
- node_modules
|
||||
install:
|
||||
# Install Node.js
|
||||
- ps: >-
|
||||
try { Install-Product node $env:nodejs_version -ErrorAction Stop }
|
||||
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) }
|
||||
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64 }
|
||||
# Configure npm
|
||||
- ps: |
|
||||
npm config set loglevel error
|
||||
npm config set shrinkwrap false
|
||||
if ((npm config get package-lock) -eq "true") {
|
||||
npm config set package-lock false
|
||||
} else {
|
||||
npm config set shrinkwrap false
|
||||
}
|
||||
# Remove all non-test dependencies
|
||||
- ps: |
|
||||
# Remove example dependencies
|
||||
@@ -43,6 +55,7 @@ install:
|
||||
# - use 6.x for Node.js < 8
|
||||
# - use 7.x for Node.js < 10
|
||||
# - use 8.x for Node.js < 12
|
||||
# - use 9.x for Node.js < 14
|
||||
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
|
||||
npm install --silent --save-dev mocha@3.5.3
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
|
||||
@@ -53,15 +66,32 @@ install:
|
||||
npm install --silent --save-dev mocha@7.2.0
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 12) {
|
||||
npm install --silent --save-dev mocha@8.4.0
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 14) {
|
||||
npm install --silent --save-dev mocha@9.2.2
|
||||
}
|
||||
- ps: |
|
||||
# nyc for test coverage
|
||||
# - use 10.3.2 for Node.js < 4
|
||||
# - use 11.9.0 for Node.js < 6
|
||||
# - use 14.1.1 for Node.js < 10
|
||||
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
|
||||
npm install --silent --save-dev nyc@10.3.2
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
|
||||
npm install --silent --save-dev nyc@11.9.0
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
|
||||
npm install --silent --save-dev nyc@14.1.1
|
||||
}
|
||||
- ps: |
|
||||
# supertest for http calls
|
||||
# - use 2.0.0 for Node.js < 4
|
||||
# - use 3.4.2 for Node.js < 6
|
||||
# - use 3.4.2 for Node.js < 7
|
||||
# - use 6.1.6 for Node.js < 8
|
||||
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
|
||||
npm install --silent --save-dev supertest@2.0.0
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 7) {
|
||||
npm install --silent --save-dev supertest@3.4.2
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
|
||||
npm install --silent --save-dev supertest@6.1.6
|
||||
}
|
||||
# Update Node.js modules
|
||||
- ps: |
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
|
||||
all:
|
||||
@./run 1 middleware
|
||||
@./run 5 middleware
|
||||
@./run 10 middleware
|
||||
@./run 15 middleware
|
||||
@./run 20 middleware
|
||||
@./run 30 middleware
|
||||
@./run 50 middleware
|
||||
@./run 100 middleware
|
||||
@./run 1 middleware 50
|
||||
@./run 5 middleware 50
|
||||
@./run 10 middleware 50
|
||||
@./run 15 middleware 50
|
||||
@./run 20 middleware 50
|
||||
@./run 30 middleware 50
|
||||
@./run 50 middleware 50
|
||||
@./run 100 middleware 50
|
||||
@./run 10 middleware 100
|
||||
@./run 10 middleware 250
|
||||
@./run 10 middleware 500
|
||||
@./run 10 middleware 1000
|
||||
@echo
|
||||
|
||||
.PHONY: all
|
||||
|
||||
34
benchmarks/README.md
Normal file
34
benchmarks/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Express Benchmarks
|
||||
|
||||
## Installation
|
||||
|
||||
You will need to install [wrk](https://github.com/wg/wrk/blob/master/INSTALL) in order to run the benchmarks.
|
||||
|
||||
## Running
|
||||
|
||||
To run the benchmarks, first install the dependencies `npm i`, then run `make`
|
||||
|
||||
The output will look something like this:
|
||||
|
||||
```
|
||||
50 connections
|
||||
1 middleware
|
||||
7.15ms
|
||||
6784.01
|
||||
|
||||
[...redacted...]
|
||||
|
||||
1000 connections
|
||||
10 middleware
|
||||
139.21ms
|
||||
6155.19
|
||||
|
||||
```
|
||||
|
||||
### Tip: Include Node.js version in output
|
||||
|
||||
You can use `make && node -v` to include the node.js version in the output.
|
||||
|
||||
### Tip: Save the results to a file
|
||||
|
||||
You can use `make > results.log` to save the results to a file `results.log`.
|
||||
@@ -13,7 +13,7 @@ while (n--) {
|
||||
});
|
||||
}
|
||||
|
||||
app.use(function(req, res, next){
|
||||
app.use(function(req, res){
|
||||
res.send('Hello World')
|
||||
});
|
||||
|
||||
|
||||
@@ -4,13 +4,15 @@ echo
|
||||
MW=$1 node $2 &
|
||||
pid=$!
|
||||
|
||||
echo " $3 connections"
|
||||
|
||||
sleep 2
|
||||
|
||||
wrk 'http://localhost:3333/?foo[bar]=baz' \
|
||||
-d 3 \
|
||||
-c 50 \
|
||||
-c $3 \
|
||||
-t 8 \
|
||||
| grep 'Requests/sec' \
|
||||
| awk '{ print " " $2 }'
|
||||
| grep 'Requests/sec\|Latency' \
|
||||
| awk '{ print " " $2 }'
|
||||
|
||||
kill $pid
|
||||
|
||||
@@ -13,7 +13,6 @@ This page contains list of examples using Express.
|
||||
- [hello-world](./hello-world) - Simple request handler
|
||||
- [markdown](./markdown) - Markdown as template engine
|
||||
- [multi-router](./multi-router) - Working with multiple Express routers
|
||||
- [multipart](./multipart) - Accepting multipart-encoded forms
|
||||
- [mvc](./mvc) - MVC-style controllers
|
||||
- [online](./online) - Tracking online user activity with `online` and `redis` packages
|
||||
- [params](./params) - Working with route parameters
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -59,14 +61,14 @@ function authenticate(name, pass, fn) {
|
||||
if (!module.parent) console.log('authenticating %s:%s', name, pass);
|
||||
var user = users[name];
|
||||
// query the db for the given username
|
||||
if (!user) return fn(new Error('cannot find user'));
|
||||
if (!user) return fn(null, null)
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the hash against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {
|
||||
if (err) return fn(err);
|
||||
if (hash === user.hash) return fn(null, user)
|
||||
fn(new Error('invalid password'));
|
||||
fn(null, null)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,8 +101,9 @@ app.get('/login', function(req, res){
|
||||
res.render('login');
|
||||
});
|
||||
|
||||
app.post('/login', function(req, res){
|
||||
app.post('/login', function (req, res, next) {
|
||||
authenticate(req.body.username, req.body.password, function(err, user){
|
||||
if (err) return next(err)
|
||||
if (user) {
|
||||
// Regenerate session when signing in
|
||||
// to prevent fixation
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
|
||||
<form method="post" action="/login">
|
||||
<p>
|
||||
<label>Username:</label>
|
||||
<input type="text" name="username">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" name="username" id="username">
|
||||
</p>
|
||||
<p>
|
||||
<label>Password:</label>
|
||||
<input type="text" name="password">
|
||||
<label for="password">Password:</label>
|
||||
<input type="text" name="password" id="password">
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Login">
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var users = require('./db');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var users = require('./db');
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -11,13 +13,10 @@ var app = module.exports = express();
|
||||
app.use(cookieSession({ secret: 'manny is cool' }));
|
||||
|
||||
// do something with the session
|
||||
app.use(count);
|
||||
|
||||
// custom middleware
|
||||
function count(req, res) {
|
||||
app.get('/', function (req, res) {
|
||||
req.session.count = (req.session.count || 0) + 1
|
||||
res.send('viewed ' + req.session.count + ' times\n')
|
||||
}
|
||||
})
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('path');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// path to where the files are stored on disk
|
||||
var FILES_DIR = path.join(__dirname, 'files')
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<ul>' +
|
||||
'<li>Download <a href="/files/notes/groceries.txt">notes/groceries.txt</a>.</li>' +
|
||||
@@ -18,9 +24,7 @@ app.get('/', function(req, res){
|
||||
// /files/* is accessed via req.params[0]
|
||||
// but here we name it :file
|
||||
app.get('/files/:file(*)', function(req, res, next){
|
||||
var filePath = path.join(__dirname, 'files', req.params.file);
|
||||
|
||||
res.download(filePath, function (err) {
|
||||
res.download(req.params.file, { root: FILES_DIR }, function (err) {
|
||||
if (!err) return; // file sent
|
||||
if (err.status !== 404) return next(err); // non-404 error
|
||||
// file for download not found
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -24,7 +26,7 @@ function error(err, req, res, next) {
|
||||
res.send('Internal Server Error');
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
app.get('/', function () {
|
||||
// Caught and passed down to the errorHandler middleware
|
||||
throw new Error('something broke!');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = express();
|
||||
var app = module.exports = express()
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -24,7 +26,7 @@ app.engine('md', function(path, options, fn){
|
||||
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// make it the default so we dont need .md
|
||||
// make it the default, so we don't need .md
|
||||
app.set('view engine', 'md');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv1 = express.Router();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv2 = express.Router();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../..');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var multiparty = require('multiparty');
|
||||
var format = require('util').format;
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<form method="post" enctype="multipart/form-data">'
|
||||
+ '<p>Title: <input type="text" name="title" /></p>'
|
||||
+ '<p>Image: <input type="file" name="image" /></p>'
|
||||
+ '<p><input type="submit" value="Upload" /></p>'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res, next){
|
||||
// create a form to begin parsing
|
||||
var form = new multiparty.Form();
|
||||
var image;
|
||||
var title;
|
||||
|
||||
form.on('error', next);
|
||||
form.on('close', function(){
|
||||
res.send(format('\nuploaded %s (%d Kb) as %s'
|
||||
, image.filename
|
||||
, image.size / 1024 | 0
|
||||
, title));
|
||||
});
|
||||
|
||||
// listen on field event for title
|
||||
form.on('field', function(name, val){
|
||||
if (name !== 'title') return;
|
||||
title = val;
|
||||
});
|
||||
|
||||
// listen on part event for image file
|
||||
form.on('part', function(part){
|
||||
if (!part.filename) return;
|
||||
if (part.name !== 'image') return part.resume();
|
||||
image = {};
|
||||
image.filename = part.filename;
|
||||
image.size = 0;
|
||||
part.on('data', function(buf){
|
||||
image.size += buf.length;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// parse the form
|
||||
form.parse(req);
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(4000);
|
||||
console.log('Express started on port 4000');
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
// faux database
|
||||
|
||||
var pets = exports.pets = [];
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
// install redis first:
|
||||
// https://redis.io/
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var createError = require('http-errors')
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -15,14 +18,6 @@ var users = [
|
||||
, { name: 'bandit' }
|
||||
];
|
||||
|
||||
// Create HTTP error
|
||||
|
||||
function createError(status, message) {
|
||||
var err = new Error(message);
|
||||
err.status = status;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Convert :to and :from to integers
|
||||
|
||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
@@ -37,7 +32,8 @@ app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
// Load user by id
|
||||
|
||||
app.param('user', function(req, res, next, id){
|
||||
if (req.user = users[id]) {
|
||||
req.user = users[id];
|
||||
if (req.user) {
|
||||
next();
|
||||
} else {
|
||||
next(createError(404, 'failed to find user'));
|
||||
@@ -56,7 +52,7 @@ app.get('/', function(req, res){
|
||||
* GET :user.
|
||||
*/
|
||||
|
||||
app.get('/user/:user', function(req, res, next){
|
||||
app.get('/user/:user', function (req, res) {
|
||||
res.send('user ' + req.user.name);
|
||||
});
|
||||
|
||||
@@ -64,7 +60,7 @@ app.get('/user/:user', function(req, res, next){
|
||||
* GET users :from - :to.
|
||||
*/
|
||||
|
||||
app.get('/users/:from-:to', function(req, res, next){
|
||||
app.get('/users/:from-:to', function (req, res) {
|
||||
var from = req.params.from;
|
||||
var to = req.params.to;
|
||||
var names = users.map(function(user){ return user.name; });
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
// Fake posts database
|
||||
|
||||
var posts = [
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.render('index', { title: 'Route Separation Example' });
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
// Fake user database
|
||||
|
||||
var users = [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<h1>Editing <%= user.name %></h1>
|
||||
|
||||
<div id="user">
|
||||
<form action="?_method=put", method="post">
|
||||
<form action="?_method=put" method="post">
|
||||
<p>
|
||||
Name:
|
||||
<input type="text" value="<%= user.name %>" name="user[name]" />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
// install redis first:
|
||||
// https://redis.io/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var search = document.querySelector('[type=search]');
|
||||
var code = document.querySelector('pre');
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Search example</title>
|
||||
<style type="text/css">
|
||||
<style>
|
||||
body {
|
||||
font: 14px "Helvetica Neue", Helvetica;
|
||||
padding: 50px;
|
||||
@@ -15,7 +15,7 @@
|
||||
<h2>Search</h2>
|
||||
<p>Try searching for "ferret" or "cat".</p>
|
||||
<input type="search" name="search" value="" />
|
||||
<pre />
|
||||
<pre></pre>
|
||||
<script src="/client.js" charset="utf-8"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
// install redis first:
|
||||
// https://redis.io/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -59,7 +61,7 @@ function users(req, res, next) {
|
||||
})
|
||||
}
|
||||
|
||||
app.get('/middleware', count, users, function(req, res, next){
|
||||
app.get('/middleware', count, users, function (req, res) {
|
||||
res.render('index', {
|
||||
title: 'Users',
|
||||
count: req.count,
|
||||
@@ -97,7 +99,7 @@ function users2(req, res, next) {
|
||||
})
|
||||
}
|
||||
|
||||
app.get('/middleware-locals', count2, users2, function(req, res, next){
|
||||
app.get('/middleware-locals', count2, users2, function (req, res) {
|
||||
// you can see now how we have much less
|
||||
// to pass to res.render(). If we have
|
||||
// several routes related to users this
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = User;
|
||||
|
||||
// faux model
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -32,7 +34,7 @@ app.use('/api', function(req, res, next){
|
||||
if (!key) return next(error(400, 'api key required'));
|
||||
|
||||
// key is invalid
|
||||
if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key'));
|
||||
if (apiKeys.indexOf(key) === -1) return next(error(401, 'invalid api key'))
|
||||
|
||||
// all good, store req.key for route access
|
||||
req.key = key;
|
||||
@@ -70,12 +72,12 @@ var userRepos = {
|
||||
// and simply expose the data
|
||||
|
||||
// example: http://localhost:3000/api/users/?api-key=foo
|
||||
app.get('/api/users', function(req, res, next){
|
||||
app.get('/api/users', function (req, res) {
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
// example: http://localhost:3000/api/repos/?api-key=foo
|
||||
app.get('/api/repos', function(req, res, next){
|
||||
app.get('/api/repos', function (req, res) {
|
||||
res.send(repos);
|
||||
});
|
||||
|
||||
|
||||
@@ -29,6 +29,13 @@ var flatten = require('array-flatten');
|
||||
var merge = require('utils-merge');
|
||||
var resolve = require('path').resolve;
|
||||
var setPrototypeOf = require('setprototypeof')
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
@@ -352,7 +359,17 @@ app.param = function param(name, fn) {
|
||||
app.set = function set(setting, val) {
|
||||
if (arguments.length === 1) {
|
||||
// app.get(setting)
|
||||
return this.settings[setting];
|
||||
var settings = this.settings
|
||||
|
||||
while (settings && settings !== Object.prototype) {
|
||||
if (hasOwnProperty.call(settings, setting)) {
|
||||
return settings[setting]
|
||||
}
|
||||
|
||||
settings = Object.getPrototypeOf(settings)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
debug('set "%s" to %o', setting, val);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var contentDisposition = require('content-disposition');
|
||||
var createError = require('http-errors')
|
||||
var deprecate = require('depd')('express');
|
||||
var encodeUrl = require('encodeurl');
|
||||
var escapeHtml = require('escape-html');
|
||||
@@ -64,6 +65,9 @@ var charsetRegExp = /;\s*charset\s*=/;
|
||||
*/
|
||||
|
||||
res.status = function status(code) {
|
||||
if ((typeof code === 'string' || Math.floor(code) !== code) && code > 99 && code < 1000) {
|
||||
deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead')
|
||||
}
|
||||
this.statusCode = code;
|
||||
return this;
|
||||
};
|
||||
@@ -135,7 +139,7 @@ res.send = function send(body) {
|
||||
|
||||
deprecate('res.send(status): Use res.sendStatus(status) instead');
|
||||
this.statusCode = chunk;
|
||||
chunk = statuses[chunk]
|
||||
chunk = statuses.message[chunk]
|
||||
}
|
||||
|
||||
switch (typeof chunk) {
|
||||
@@ -213,6 +217,13 @@ res.send = function send(body) {
|
||||
chunk = '';
|
||||
}
|
||||
|
||||
// alter headers for 205
|
||||
if (this.statusCode === 205) {
|
||||
this.set('Content-Length', '0')
|
||||
this.removeHeader('Transfer-Encoding')
|
||||
chunk = ''
|
||||
}
|
||||
|
||||
if (req.method === 'HEAD') {
|
||||
// skip body for HEAD
|
||||
this.end();
|
||||
@@ -356,7 +367,7 @@ res.jsonp = function jsonp(obj) {
|
||||
*/
|
||||
|
||||
res.sendStatus = function sendStatus(statusCode) {
|
||||
var body = statuses[statusCode] || String(statusCode)
|
||||
var body = statuses.message[statusCode] || String(statusCode)
|
||||
|
||||
this.statusCode = statusCode;
|
||||
this.type('txt');
|
||||
@@ -524,7 +535,7 @@ res.sendfile = deprecate.function(res.sendfile,
|
||||
* Optionally providing an alternate attachment `filename`,
|
||||
* and optional callback `callback(err)`. The callback is invoked
|
||||
* when the data transfer is complete, or when an error has
|
||||
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
|
||||
* occurred. Be sure to check `res.headersSent` if you plan to respond.
|
||||
*
|
||||
* Optionally providing an `options` object to use with `res.sendFile()`.
|
||||
* This function will set the `Content-Disposition` header, overriding
|
||||
@@ -551,6 +562,13 @@ res.download = function download (path, filename, options, callback) {
|
||||
opts = null
|
||||
}
|
||||
|
||||
// support optional filename, where options may be in it's place
|
||||
if (typeof filename === 'object' &&
|
||||
(typeof options === 'function' || options === undefined)) {
|
||||
name = null
|
||||
opts = filename
|
||||
}
|
||||
|
||||
// set Content-Disposition when file is sent
|
||||
var headers = {
|
||||
'Content-Disposition': contentDisposition(name || path)
|
||||
@@ -572,7 +590,9 @@ res.download = function download (path, filename, options, callback) {
|
||||
opts.headers = headers
|
||||
|
||||
// Resolve the full path for sendFile
|
||||
var fullPath = resolve(path);
|
||||
var fullPath = !opts.root
|
||||
? resolve(path)
|
||||
: path
|
||||
|
||||
// send file
|
||||
return this.sendFile(fullPath, opts, done)
|
||||
@@ -665,9 +685,8 @@ res.format = function(obj){
|
||||
var req = this.req;
|
||||
var next = req.next;
|
||||
|
||||
var fn = obj.default;
|
||||
if (fn) delete obj.default;
|
||||
var keys = Object.keys(obj);
|
||||
var keys = Object.keys(obj)
|
||||
.filter(function (v) { return v !== 'default' })
|
||||
|
||||
var key = keys.length > 0
|
||||
? req.accepts(keys)
|
||||
@@ -678,13 +697,12 @@ res.format = function(obj){
|
||||
if (key) {
|
||||
this.set('Content-Type', normalizeType(key).value);
|
||||
obj[key](req, this, next);
|
||||
} else if (fn) {
|
||||
fn();
|
||||
} else if (obj.default) {
|
||||
obj.default(req, this, next)
|
||||
} else {
|
||||
var err = new Error('Not Acceptable');
|
||||
err.status = err.statusCode = 406;
|
||||
err.types = normalizeTypes(keys).map(function(o){ return o.value });
|
||||
next(err);
|
||||
next(createError(406, {
|
||||
types: normalizeTypes(keys).map(function (o) { return o.value })
|
||||
}))
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -804,6 +822,14 @@ res.get = function(field){
|
||||
*/
|
||||
|
||||
res.clearCookie = function clearCookie(name, options) {
|
||||
if (options) {
|
||||
if (options.maxAge) {
|
||||
deprecate('res.clearCookie: Passing "options.maxAge" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.');
|
||||
}
|
||||
if (options.expires) {
|
||||
deprecate('res.clearCookie: Passing "options.expires" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.');
|
||||
}
|
||||
}
|
||||
var opts = merge({ expires: new Date(1), path: '/' }, options);
|
||||
|
||||
return this.cookie(name, '', opts);
|
||||
@@ -850,9 +876,13 @@ res.cookie = function (name, value, options) {
|
||||
val = 's:' + sign(val, secret);
|
||||
}
|
||||
|
||||
if ('maxAge' in opts) {
|
||||
opts.expires = new Date(Date.now() + opts.maxAge);
|
||||
opts.maxAge /= 1000;
|
||||
if (opts.maxAge != null) {
|
||||
var maxAge = opts.maxAge - 0
|
||||
|
||||
if (!isNaN(maxAge)) {
|
||||
opts.expires = new Date(Date.now() + maxAge)
|
||||
opts.maxAge = Math.floor(maxAge / 1000)
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.path == null) {
|
||||
@@ -882,14 +912,16 @@ res.cookie = function (name, value, options) {
|
||||
*/
|
||||
|
||||
res.location = function location(url) {
|
||||
var loc = url;
|
||||
var loc;
|
||||
|
||||
// "back" is an alias for the referrer
|
||||
if (url === 'back') {
|
||||
deprecate('res.location("back"): use res.location(req.get("Referrer") || "/") and refer to https://dub.sh/security-redirect for best practices');
|
||||
loc = this.req.get('Referrer') || '/';
|
||||
} else {
|
||||
loc = String(url);
|
||||
}
|
||||
|
||||
// set location
|
||||
return this.set('Location', encodeUrl(loc));
|
||||
};
|
||||
|
||||
@@ -933,12 +965,12 @@ res.redirect = function redirect(url) {
|
||||
// Support text/{plain,html} by default
|
||||
this.format({
|
||||
text: function(){
|
||||
body = statuses[status] + '. Redirecting to ' + address
|
||||
body = statuses.message[status] + '. Redirecting to ' + address
|
||||
},
|
||||
|
||||
html: function(){
|
||||
var u = escapeHtml(address);
|
||||
body = '<p>' + statuses[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
|
||||
body = '<p>' + statuses.message[status] + '. Redirecting to ' + u + '</p>'
|
||||
},
|
||||
|
||||
default: function(){
|
||||
@@ -1113,7 +1145,7 @@ function sendfile(res, file, options, callback) {
|
||||
* ability to escape characters that can trigger HTML sniffing.
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {function} replaces
|
||||
* @param {function} replacer
|
||||
* @param {number} spaces
|
||||
* @param {boolean} escape
|
||||
* @returns {string}
|
||||
|
||||
@@ -36,7 +36,7 @@ var toString = Object.prototype.toString;
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @return {Router} which is an callable function
|
||||
* @return {Router} which is a callable function
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -108,8 +108,8 @@ proto.param = function param(name, fn) {
|
||||
var ret;
|
||||
|
||||
if (name[0] === ':') {
|
||||
deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead');
|
||||
name = name.substr(1);
|
||||
deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.slice(1)) + ', fn) instead')
|
||||
name = name.slice(1)
|
||||
}
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
@@ -142,6 +142,7 @@ proto.handle = function handle(req, res, out) {
|
||||
var protohost = getProtohost(req.url) || ''
|
||||
var removed = '';
|
||||
var slashAdded = false;
|
||||
var sync = 0
|
||||
var paramcalled = {};
|
||||
|
||||
// store options for OPTIONS request
|
||||
@@ -180,14 +181,14 @@ proto.handle = function handle(req, res, out) {
|
||||
|
||||
// remove added slash
|
||||
if (slashAdded) {
|
||||
req.url = req.url.substr(1);
|
||||
req.url = req.url.slice(1)
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
// restore altered req.url
|
||||
if (removed.length !== 0) {
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
req.url = protohost + removed + req.url.slice(protohost.length)
|
||||
removed = '';
|
||||
}
|
||||
|
||||
@@ -203,6 +204,11 @@ proto.handle = function handle(req, res, out) {
|
||||
return;
|
||||
}
|
||||
|
||||
// max sync stack
|
||||
if (++sync > 100) {
|
||||
return setImmediate(next, err)
|
||||
}
|
||||
|
||||
// get pathname of request
|
||||
var path = getPathname(req);
|
||||
|
||||
@@ -251,7 +257,6 @@ proto.handle = function handle(req, res, out) {
|
||||
// don't even bother matching route
|
||||
if (!has_method && method !== 'HEAD') {
|
||||
match = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,21 +279,21 @@ proto.handle = function handle(req, res, out) {
|
||||
// this should be done for the layer
|
||||
self.process_params(layer, paramcalled, req, res, function (err) {
|
||||
if (err) {
|
||||
return next(layerError || err);
|
||||
next(layerError || err)
|
||||
} else if (route) {
|
||||
layer.handle_request(req, res, next)
|
||||
} else {
|
||||
trim_prefix(layer, layerError, layerPath, path)
|
||||
}
|
||||
|
||||
if (route) {
|
||||
return layer.handle_request(req, res, next);
|
||||
}
|
||||
|
||||
trim_prefix(layer, layerError, layerPath, path);
|
||||
sync = 0
|
||||
});
|
||||
}
|
||||
|
||||
function trim_prefix(layer, layerError, layerPath, path) {
|
||||
if (layerPath.length !== 0) {
|
||||
// Validate path is a prefix match
|
||||
if (layerPath !== path.substr(0, layerPath.length)) {
|
||||
if (layerPath !== path.slice(0, layerPath.length)) {
|
||||
next(layerError)
|
||||
return
|
||||
}
|
||||
@@ -301,7 +306,7 @@ proto.handle = function handle(req, res, out) {
|
||||
// middleware (.use stuff) needs to have the path stripped
|
||||
debug('trim prefix (%s) from url %s', layerPath, req.url);
|
||||
removed = layerPath;
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
req.url = protohost + req.url.slice(protohost.length + removed.length)
|
||||
|
||||
// Ensure leading slash
|
||||
if (!protohost && req.url[0] !== '/') {
|
||||
@@ -547,10 +552,10 @@ function getProtohost(url) {
|
||||
var pathLength = searchIndex !== -1
|
||||
? searchIndex
|
||||
: url.length
|
||||
var fqdnIndex = url.substr(0, pathLength).indexOf('://')
|
||||
var fqdnIndex = url.slice(0, pathLength).indexOf('://')
|
||||
|
||||
return fqdnIndex !== -1
|
||||
? url.substr(0, url.indexOf('/', 3 + fqdnIndex))
|
||||
? url.substring(0, url.indexOf('/', 3 + fqdnIndex))
|
||||
: undefined
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,10 @@ Route.prototype._handles_method = function _handles_method(method) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var name = method.toLowerCase();
|
||||
// normalize name
|
||||
var name = typeof method === 'string'
|
||||
? method.toLowerCase()
|
||||
: method
|
||||
|
||||
if (name === 'head' && !this.methods['head']) {
|
||||
name = 'get';
|
||||
@@ -98,11 +101,15 @@ Route.prototype._options = function _options() {
|
||||
Route.prototype.dispatch = function dispatch(req, res, done) {
|
||||
var idx = 0;
|
||||
var stack = this.stack;
|
||||
var sync = 0
|
||||
|
||||
if (stack.length === 0) {
|
||||
return done();
|
||||
}
|
||||
var method = typeof req.method === 'string'
|
||||
? req.method.toLowerCase()
|
||||
: req.method
|
||||
|
||||
var method = req.method.toLowerCase();
|
||||
if (method === 'head' && !this.methods['head']) {
|
||||
method = 'get';
|
||||
}
|
||||
@@ -122,20 +129,27 @@ Route.prototype.dispatch = function dispatch(req, res, done) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
var layer = stack[idx++];
|
||||
// max sync stack
|
||||
if (++sync > 100) {
|
||||
return setImmediate(next, err)
|
||||
}
|
||||
|
||||
var layer = stack[idx++]
|
||||
|
||||
// end of layers
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
return done(err)
|
||||
}
|
||||
|
||||
if (layer.method && layer.method !== method) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
next(err)
|
||||
} else if (err) {
|
||||
layer.handle_error(err, req, res, next);
|
||||
} else {
|
||||
layer.handle_request(req, res, next);
|
||||
}
|
||||
|
||||
sync = 0
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -117,16 +117,15 @@ exports.contentDisposition = deprecate.function(contentDisposition,
|
||||
/**
|
||||
* Parse accept params `str` returning an
|
||||
* object with `.value`, `.quality` and `.params`.
|
||||
* also includes `.originalIndex` for stable sorting
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function acceptParams(str, index) {
|
||||
function acceptParams (str) {
|
||||
var parts = str.split(/ *; */);
|
||||
var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
|
||||
var ret = { value: parts[0], quality: 1, params: {} }
|
||||
|
||||
for (var i = 1; i < parts.length; ++i) {
|
||||
var pms = parts[i].split(/ *= */);
|
||||
@@ -228,7 +227,8 @@ exports.compileTrust = function(val) {
|
||||
|
||||
if (typeof val === 'string') {
|
||||
// Support comma-separated values
|
||||
val = val.split(/ *, */);
|
||||
val = val.split(',')
|
||||
.map(function (v) { return v.trim() })
|
||||
}
|
||||
|
||||
return proxyaddr.compile(val || []);
|
||||
@@ -280,6 +280,7 @@ function createETagGenerator (options) {
|
||||
/**
|
||||
* Parse an extended query string with qs.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -74,7 +74,7 @@ function View(name, options) {
|
||||
|
||||
if (!opts.engines[this.ext]) {
|
||||
// load engine
|
||||
var mod = this.ext.substr(1)
|
||||
var mod = this.ext.slice(1)
|
||||
debug('require "%s"', mod)
|
||||
|
||||
// default engine export
|
||||
|
||||
55
package.json
55
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Fast, unopinionated, minimalist web framework",
|
||||
"version": "4.17.2",
|
||||
"version": "4.22.1",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
|
||||
@@ -15,6 +15,10 @@
|
||||
"license": "MIT",
|
||||
"repository": "expressjs/express",
|
||||
"homepage": "http://expressjs.com/",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
},
|
||||
"keywords": [
|
||||
"express",
|
||||
"framework",
|
||||
@@ -28,33 +32,34 @@
|
||||
"api"
|
||||
],
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.7",
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.19.1",
|
||||
"content-disposition": "0.5.4",
|
||||
"body-parser": "~1.20.4",
|
||||
"content-disposition": "~0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.4.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"cookie": "~0.7.1",
|
||||
"cookie-signature": "~1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"encodeurl": "~1.0.2",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "~1.1.2",
|
||||
"fresh": "0.5.2",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"finalhandler": "~1.3.1",
|
||||
"fresh": "~0.5.2",
|
||||
"http-errors": "~2.0.0",
|
||||
"merge-descriptors": "1.0.3",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "~2.3.0",
|
||||
"on-finished": "~2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"path-to-regexp": "~0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.9.6",
|
||||
"qs": "~6.14.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.17.2",
|
||||
"serve-static": "1.14.2",
|
||||
"send": "~0.19.0",
|
||||
"serve-static": "~1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "~1.5.0",
|
||||
"statuses": "~2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
@@ -64,19 +69,17 @@
|
||||
"connect-redis": "3.4.2",
|
||||
"cookie-parser": "1.4.6",
|
||||
"cookie-session": "2.0.0",
|
||||
"ejs": "3.1.6",
|
||||
"eslint": "7.32.0",
|
||||
"ejs": "3.1.9",
|
||||
"eslint": "8.47.0",
|
||||
"express-session": "1.17.2",
|
||||
"hbs": "4.2.0",
|
||||
"istanbul": "0.4.5",
|
||||
"marked": "0.7.0",
|
||||
"method-override": "3.0.0",
|
||||
"mocha": "9.1.3",
|
||||
"mocha": "^6.2.2",
|
||||
"morgan": "1.10.0",
|
||||
"multiparty": "4.2.2",
|
||||
"nyc": "^14.1.1",
|
||||
"pbkdf2-password": "1.2.1",
|
||||
"should": "13.2.3",
|
||||
"supertest": "6.1.6",
|
||||
"supertest": "^6.1.6",
|
||||
"vhost": "~3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -92,8 +95,8 @@
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
|
||||
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
|
||||
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
|
||||
"test-ci": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=lcovonly --reporter=text npm test",
|
||||
"test-cov": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=html --reporter=text npm test",
|
||||
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
var after = require('after');
|
||||
var should = require('should');
|
||||
var assert = require('assert')
|
||||
var express = require('../')
|
||||
, Route = express.Route
|
||||
, methods = require('methods')
|
||||
@@ -12,6 +13,37 @@ describe('Route', function(){
|
||||
route.dispatch(req, {}, done)
|
||||
})
|
||||
|
||||
it('should not stack overflow with a large sync stack', function (done) {
|
||||
this.timeout(5000) // long-running test
|
||||
|
||||
var req = { method: 'GET', url: '/' }
|
||||
var route = new Route('/foo')
|
||||
|
||||
route.get(function (req, res, next) {
|
||||
req.counter = 0
|
||||
next()
|
||||
})
|
||||
|
||||
for (var i = 0; i < 6000; i++) {
|
||||
route.all(function (req, res, next) {
|
||||
req.counter++
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
route.get(function (req, res, next) {
|
||||
req.called = true
|
||||
next()
|
||||
})
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err)
|
||||
assert.ok(req.called)
|
||||
assert.strictEqual(req.counter, 6000)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('.all', function(){
|
||||
it('should add handler', function(done){
|
||||
var req = { method: 'GET', url: '/' };
|
||||
@@ -24,7 +56,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.ok()
|
||||
assert.ok(req.called)
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -34,7 +66,7 @@ describe('Route', function(){
|
||||
var route = new Route('/foo');
|
||||
var cb = after(methods.length, function (err) {
|
||||
if (err) return done(err);
|
||||
count.should.equal(methods.length);
|
||||
assert.strictEqual(count, methods.length)
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -65,7 +97,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
req.count.should.equal(2);
|
||||
assert.strictEqual(req.count, 2)
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -83,7 +115,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.ok()
|
||||
assert.ok(req.called)
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -92,7 +124,7 @@ describe('Route', function(){
|
||||
var req = { method: 'POST', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
route.get(function(req, res, next) {
|
||||
route.get(function () {
|
||||
throw new Error('not me!');
|
||||
})
|
||||
|
||||
@@ -103,7 +135,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.true()
|
||||
assert.ok(req.called)
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -129,7 +161,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
req.order.should.equal('abc');
|
||||
assert.strictEqual(req.order, 'abc')
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -155,9 +187,9 @@ describe('Route', function(){
|
||||
});
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
should(err).be.ok()
|
||||
should(err.message).equal('foobar');
|
||||
req.order.should.equal('a');
|
||||
assert.ok(err)
|
||||
assert.strictEqual(err.message, 'foobar')
|
||||
assert.strictEqual(req.order, 'a')
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -166,7 +198,7 @@ describe('Route', function(){
|
||||
var req = { order: '', method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
route.all(function(req, res, next){
|
||||
route.all(function () {
|
||||
throw new Error('foobar');
|
||||
});
|
||||
|
||||
@@ -181,9 +213,9 @@ describe('Route', function(){
|
||||
});
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
should(err).be.ok()
|
||||
should(err.message).equal('foobar');
|
||||
req.order.should.equal('a');
|
||||
assert.ok(err)
|
||||
assert.strictEqual(err.message, 'foobar')
|
||||
assert.strictEqual(req.order, 'a')
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -192,7 +224,7 @@ describe('Route', function(){
|
||||
var req = { method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
route.get(function(req, res, next){
|
||||
route.get(function () {
|
||||
throw new Error('boom!');
|
||||
});
|
||||
|
||||
@@ -207,7 +239,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.message).equal('oops');
|
||||
assert.strictEqual(req.message, 'oops')
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -221,8 +253,8 @@ describe('Route', function(){
|
||||
});
|
||||
|
||||
route.dispatch(req, {}, function(err){
|
||||
should(err).be.ok()
|
||||
err.message.should.equal('boom!');
|
||||
assert.ok(err)
|
||||
assert.strictEqual(err.message, 'boom!')
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -233,7 +265,7 @@ describe('Route', function(){
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
// this should not execute
|
||||
true.should.be.false()
|
||||
throw new Error('should not be called')
|
||||
});
|
||||
|
||||
route.dispatch(req, {}, done);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var after = require('after');
|
||||
var express = require('../')
|
||||
@@ -60,7 +61,36 @@ describe('Router', function(){
|
||||
router.handle({ method: 'GET' }, {}, done)
|
||||
})
|
||||
|
||||
it('handle missing method', function (done) {
|
||||
var all = false
|
||||
var router = new Router()
|
||||
var route = router.route('/foo')
|
||||
var use = false
|
||||
|
||||
route.post(function (req, res, next) { next(new Error('should not run')) })
|
||||
route.all(function (req, res, next) {
|
||||
all = true
|
||||
next()
|
||||
})
|
||||
route.get(function (req, res, next) { next(new Error('should not run')) })
|
||||
|
||||
router.get('/foo', function (req, res, next) { next(new Error('should not run')) })
|
||||
router.use(function (req, res, next) {
|
||||
use = true
|
||||
next()
|
||||
})
|
||||
|
||||
router.handle({ url: '/foo' }, {}, function (err) {
|
||||
if (err) return done(err)
|
||||
assert.ok(all)
|
||||
assert.ok(use)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not stack overflow with many registered routes', function(done){
|
||||
this.timeout(5000) // long-running test
|
||||
|
||||
var handler = function(req, res){ res.end(new Error('wrong handler')) };
|
||||
var router = new Router();
|
||||
|
||||
@@ -75,6 +105,56 @@ describe('Router', function(){
|
||||
router.handle({ url: '/', method: 'GET' }, { end: done });
|
||||
});
|
||||
|
||||
it('should not stack overflow with a large sync route stack', function (done) {
|
||||
this.timeout(5000) // long-running test
|
||||
|
||||
var router = new Router()
|
||||
|
||||
router.get('/foo', function (req, res, next) {
|
||||
req.counter = 0
|
||||
next()
|
||||
})
|
||||
|
||||
for (var i = 0; i < 6000; i++) {
|
||||
router.get('/foo', function (req, res, next) {
|
||||
req.counter++
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
router.get('/foo', function (req, res) {
|
||||
assert.strictEqual(req.counter, 6000)
|
||||
res.end()
|
||||
})
|
||||
|
||||
router.handle({ url: '/foo', method: 'GET' }, { end: done })
|
||||
})
|
||||
|
||||
it('should not stack overflow with a large sync middleware stack', function (done) {
|
||||
this.timeout(5000) // long-running test
|
||||
|
||||
var router = new Router()
|
||||
|
||||
router.use(function (req, res, next) {
|
||||
req.counter = 0
|
||||
next()
|
||||
})
|
||||
|
||||
for (var i = 0; i < 6000; i++) {
|
||||
router.use(function (req, res, next) {
|
||||
req.counter++
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
router.use(function (req, res) {
|
||||
assert.strictEqual(req.counter, 6000)
|
||||
res.end()
|
||||
})
|
||||
|
||||
router.handle({ url: '/', method: 'GET' }, { end: done })
|
||||
})
|
||||
|
||||
describe('.handle', function(){
|
||||
it('should dispatch', function(done){
|
||||
var router = new Router();
|
||||
@@ -148,7 +228,7 @@ describe('Router', function(){
|
||||
it('should handle throwing inside routes with params', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
router.get('/foo/:id', function(req, res, next){
|
||||
router.get('/foo/:id', function () {
|
||||
throw new Error('foo');
|
||||
});
|
||||
|
||||
@@ -526,8 +606,8 @@ describe('Router', function(){
|
||||
var req2 = { url: '/foo/10/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
var cb = after(2, done)
|
||||
|
||||
done = after(2, done);
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
@@ -546,14 +626,14 @@ describe('Router', function(){
|
||||
assert.ifError(err);
|
||||
assert.equal(req1.ms, 50);
|
||||
assert.equal(req1.originalUrl, '/foo/50/bar');
|
||||
done();
|
||||
cb()
|
||||
});
|
||||
|
||||
router.handle(req2, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(req2.ms, 10);
|
||||
assert.equal(req2.originalUrl, '/foo/10/bar');
|
||||
done();
|
||||
cb()
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('auth', function(){
|
||||
.expect(200, /<form/, done)
|
||||
})
|
||||
|
||||
it('should display login error', function(done){
|
||||
it('should display login error for bad user', function (done) {
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
@@ -36,6 +36,21 @@ describe('auth', function(){
|
||||
.expect(200, /Authentication failed/, done)
|
||||
})
|
||||
})
|
||||
|
||||
it('should display login error for bad password', function (done) {
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=tj&password=nogood')
|
||||
.expect('Location', '/login')
|
||||
.expect(302, function (err, res) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/login')
|
||||
.set('Cookie', getCookie(res))
|
||||
.expect(200, /Authentication failed/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /logout',function(){
|
||||
|
||||
@@ -36,4 +36,12 @@ describe('downloads', function(){
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /files/../index.js', function () {
|
||||
it('should respond with 403', function (done) {
|
||||
request(app)
|
||||
.get('/files/../index.js')
|
||||
.expect(403, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
21
test/acceptance/hello-world.js
Normal file
21
test/acceptance/hello-world.js
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
var app = require('../../examples/hello-world')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('hello-world', function () {
|
||||
describe('GET /', function () {
|
||||
it('should respond with hello world', function (done) {
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'Hello World', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /missing', function () {
|
||||
it('should respond with 404', function (done) {
|
||||
request(app)
|
||||
.get('/missing')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,22 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
var after = require('after')
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('app.all()', function(){
|
||||
it('should add a router per method', function(done){
|
||||
var app = express();
|
||||
var cb = after(2, done)
|
||||
|
||||
app.all('/tobi', function(req, res){
|
||||
res.end(req.method);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/tobi')
|
||||
.expect('PUT', function(){
|
||||
request(app)
|
||||
.put('/tobi')
|
||||
.expect(200, 'PUT', cb)
|
||||
|
||||
request(app)
|
||||
.get('/tobi')
|
||||
.expect('GET', done);
|
||||
});
|
||||
.expect(200, 'GET', cb)
|
||||
})
|
||||
|
||||
it('should run the callback for a method just once', function(done){
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var express = require('../')
|
||||
, fs = require('fs');
|
||||
var path = require('path')
|
||||
@@ -22,16 +24,16 @@ describe('app', function(){
|
||||
|
||||
app.render('user.html', function(err, str){
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should throw when the callback is missing', function(){
|
||||
var app = express();
|
||||
(function(){
|
||||
assert.throws(function () {
|
||||
app.engine('.html', null);
|
||||
}).should.throw('callback function required');
|
||||
}, /callback function required/)
|
||||
})
|
||||
|
||||
it('should work without leading "."', function(done){
|
||||
@@ -43,7 +45,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user.html', function(err, str){
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -58,7 +60,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user', function(err, str){
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -73,7 +75,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user', function(err, str){
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../');
|
||||
var request = require('supertest');
|
||||
@@ -46,23 +47,20 @@ describe('HEAD', function(){
|
||||
describe('app.head()', function(){
|
||||
it('should override', function(done){
|
||||
var app = express()
|
||||
, called;
|
||||
|
||||
app.head('/tobi', function(req, res){
|
||||
called = true;
|
||||
res.end('');
|
||||
res.header('x-method', 'head')
|
||||
res.end()
|
||||
});
|
||||
|
||||
app.get('/tobi', function(req, res){
|
||||
assert(0, 'should not call GET');
|
||||
res.header('x-method', 'get')
|
||||
res.send('tobi');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.head('/tobi')
|
||||
.expect(200, function(){
|
||||
assert(called);
|
||||
done();
|
||||
});
|
||||
.head('/tobi')
|
||||
.expect('x-method', 'head')
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
58
test/app.js
58
test/app.js
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var express = require('..')
|
||||
@@ -32,8 +33,8 @@ describe('app.parent', function(){
|
||||
blog.use('/admin', blogAdmin);
|
||||
|
||||
assert(!app.parent, 'app.parent');
|
||||
blog.parent.should.equal(app);
|
||||
blogAdmin.parent.should.equal(blog);
|
||||
assert.strictEqual(blog.parent, app)
|
||||
assert.strictEqual(blogAdmin.parent, blog)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -48,10 +49,10 @@ describe('app.mountpath', function(){
|
||||
app.use(fallback);
|
||||
blog.use('/admin', admin);
|
||||
|
||||
admin.mountpath.should.equal('/admin');
|
||||
app.mountpath.should.equal('/');
|
||||
blog.mountpath.should.equal('/blog');
|
||||
fallback.mountpath.should.equal('/');
|
||||
assert.strictEqual(admin.mountpath, '/admin')
|
||||
assert.strictEqual(app.mountpath, '/')
|
||||
assert.strictEqual(blog.mountpath, '/blog')
|
||||
assert.strictEqual(fallback.mountpath, '/')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -76,35 +77,56 @@ describe('app.path()', function(){
|
||||
app.use('/blog', blog);
|
||||
blog.use('/admin', blogAdmin);
|
||||
|
||||
app.path().should.equal('');
|
||||
blog.path().should.equal('/blog');
|
||||
blogAdmin.path().should.equal('/blog/admin');
|
||||
assert.strictEqual(app.path(), '')
|
||||
assert.strictEqual(blog.path(), '/blog')
|
||||
assert.strictEqual(blogAdmin.path(), '/blog/admin')
|
||||
})
|
||||
})
|
||||
|
||||
describe('in development', function(){
|
||||
before(function () {
|
||||
this.env = process.env.NODE_ENV
|
||||
process.env.NODE_ENV = 'development'
|
||||
})
|
||||
|
||||
after(function () {
|
||||
process.env.NODE_ENV = this.env
|
||||
})
|
||||
|
||||
it('should disable "view cache"', function(){
|
||||
process.env.NODE_ENV = 'development';
|
||||
var app = express();
|
||||
app.enabled('view cache').should.be.false()
|
||||
process.env.NODE_ENV = 'test';
|
||||
assert.ok(!app.enabled('view cache'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('in production', function(){
|
||||
before(function () {
|
||||
this.env = process.env.NODE_ENV
|
||||
process.env.NODE_ENV = 'production'
|
||||
})
|
||||
|
||||
after(function () {
|
||||
process.env.NODE_ENV = this.env
|
||||
})
|
||||
|
||||
it('should enable "view cache"', function(){
|
||||
process.env.NODE_ENV = 'production';
|
||||
var app = express();
|
||||
app.enabled('view cache').should.be.true()
|
||||
process.env.NODE_ENV = 'test';
|
||||
assert.ok(app.enabled('view cache'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('without NODE_ENV', function(){
|
||||
before(function () {
|
||||
this.env = process.env.NODE_ENV
|
||||
process.env.NODE_ENV = ''
|
||||
})
|
||||
|
||||
after(function () {
|
||||
process.env.NODE_ENV = this.env
|
||||
})
|
||||
|
||||
it('should default to development', function(){
|
||||
process.env.NODE_ENV = '';
|
||||
var app = express();
|
||||
app.get('env').should.equal('development');
|
||||
process.env.NODE_ENV = 'test';
|
||||
assert.strictEqual(app.get('env'), 'development')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../')
|
||||
|
||||
@@ -5,9 +6,8 @@ describe('app.listen()', function(){
|
||||
it('should wrap with an HTTP server', function(done){
|
||||
var app = express();
|
||||
|
||||
var server = app.listen(9999, function(){
|
||||
server.close();
|
||||
done();
|
||||
var server = app.listen(0, function () {
|
||||
server.close(done)
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var express = require('../')
|
||||
|
||||
describe('app', function(){
|
||||
describe('.locals(obj)', function(){
|
||||
it('should merge locals', function(){
|
||||
var app = express();
|
||||
Object.keys(app.locals).should.eql(['settings']);
|
||||
app.locals.user = 'tobi';
|
||||
app.locals.age = 2;
|
||||
Object.keys(app.locals).should.eql(['settings', 'user', 'age']);
|
||||
app.locals.user.should.equal('tobi');
|
||||
app.locals.age.should.equal(2);
|
||||
describe('.locals', function () {
|
||||
it('should default object', function () {
|
||||
var app = express()
|
||||
assert.ok(app.locals)
|
||||
assert.strictEqual(typeof app.locals, 'object')
|
||||
})
|
||||
})
|
||||
|
||||
describe('.locals.settings', function(){
|
||||
it('should expose app settings', function(){
|
||||
var app = express();
|
||||
app.set('title', 'House of Manny');
|
||||
var obj = app.locals.settings;
|
||||
obj.should.have.property('env', 'test');
|
||||
obj.should.have.property('title', 'House of Manny');
|
||||
describe('.settings', function () {
|
||||
it('should contain app settings ', function () {
|
||||
var app = express()
|
||||
app.set('title', 'Express')
|
||||
assert.ok(app.locals.settings)
|
||||
assert.strictEqual(typeof app.locals.settings, 'object')
|
||||
assert.strictEqual(app.locals.settings, app.settings)
|
||||
assert.strictEqual(app.locals.settings.title, 'Express')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
@@ -40,7 +42,7 @@ describe('app', function(){
|
||||
|
||||
it('should fail if not given fn', function(){
|
||||
var app = express();
|
||||
app.param.bind(app, ':name', 'bob').should.throw();
|
||||
assert.throws(app.param.bind(app, ':name', 'bob'))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -57,24 +59,22 @@ describe('app', function(){
|
||||
|
||||
app.get('/post/:id', function(req, res){
|
||||
var id = req.params.id;
|
||||
id.should.be.a.Number()
|
||||
res.send('' + id);
|
||||
res.send((typeof id) + ':' + id)
|
||||
});
|
||||
|
||||
app.get('/user/:uid', function(req, res){
|
||||
var id = req.params.id;
|
||||
id.should.be.a.Number()
|
||||
res.send('' + id);
|
||||
res.send((typeof id) + ':' + id)
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.expect(200, '123', function (err) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/post/123')
|
||||
.expect('123', done);
|
||||
})
|
||||
.get('/user/123')
|
||||
.expect(200, 'number:123', function (err) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/post/123')
|
||||
.expect('number:123', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -91,13 +91,12 @@ describe('app', function(){
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
var id = req.params.id;
|
||||
id.should.be.a.Number()
|
||||
res.send('' + id);
|
||||
res.send((typeof id) + ':' + id)
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.expect('123', done);
|
||||
.get('/user/123')
|
||||
.expect(200, 'number:123', done)
|
||||
})
|
||||
|
||||
it('should only call once per request', function(done) {
|
||||
@@ -167,7 +166,7 @@ describe('app', function(){
|
||||
app.get('/:user', function(req, res, next) {
|
||||
next('route');
|
||||
});
|
||||
app.get('/:user', function(req, res, next) {
|
||||
app.get('/:user', function (req, res) {
|
||||
res.send(req.params.user);
|
||||
});
|
||||
|
||||
@@ -188,11 +187,11 @@ describe('app', function(){
|
||||
next(new Error('invalid invocation'))
|
||||
});
|
||||
|
||||
app.post('/:user', function(req, res, next) {
|
||||
app.post('/:user', function (req, res) {
|
||||
res.send(req.params.user);
|
||||
});
|
||||
|
||||
app.get('/:thing', function(req, res, next) {
|
||||
app.get('/:thing', function (req, res) {
|
||||
res.send(req.thing);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var express = require('..');
|
||||
@@ -13,7 +14,7 @@ describe('app', function(){
|
||||
|
||||
app.render(path.join(__dirname, 'fixtures', 'user.tmpl'), function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -26,7 +27,7 @@ describe('app', function(){
|
||||
|
||||
app.render(path.join(__dirname, 'fixtures', 'user'), function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -39,7 +40,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -52,7 +53,7 @@ describe('app', function(){
|
||||
|
||||
app.render('blog/post', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<h1>blog post</h1>');
|
||||
assert.strictEqual(str, '<h1>blog post</h1>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -72,8 +73,8 @@ describe('app', function(){
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', function(err, str){
|
||||
err.should.be.ok()
|
||||
err.message.should.equal('err!');
|
||||
assert.ok(err)
|
||||
assert.strictEqual(err.message, 'err!')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -113,7 +114,7 @@ describe('app', function(){
|
||||
|
||||
app.render('email.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>This is an email</p>');
|
||||
assert.strictEqual(str, '<p>This is an email</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -128,7 +129,7 @@ describe('app', function(){
|
||||
|
||||
app.render('email', function(err, str){
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>This is an email</p>');
|
||||
assert.strictEqual(str, '<p>This is an email</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -143,7 +144,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -161,7 +162,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<span>tobi</span>');
|
||||
assert.strictEqual(str, '<span>tobi</span>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -178,7 +179,7 @@ describe('app', function(){
|
||||
|
||||
app.render('name.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -219,7 +220,7 @@ describe('app', function(){
|
||||
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
str.should.equal('abstract engine');
|
||||
assert.strictEqual(str, 'abstract engine')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -245,12 +246,12 @@ describe('app', function(){
|
||||
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
assert.strictEqual(count, 1)
|
||||
assert.strictEqual(str, 'abstract engine')
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(2);
|
||||
str.should.equal('abstract engine');
|
||||
assert.strictEqual(count, 2)
|
||||
assert.strictEqual(str, 'abstract engine')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -275,12 +276,12 @@ describe('app', function(){
|
||||
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
assert.strictEqual(count, 1)
|
||||
assert.strictEqual(str, 'abstract engine')
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
assert.strictEqual(count, 1)
|
||||
assert.strictEqual(str, 'abstract engine')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -298,7 +299,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user.tmpl', { user: user }, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -311,7 +312,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user.tmpl', {}, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
assert.strictEqual(str, '<p>tobi</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -325,7 +326,7 @@ describe('app', function(){
|
||||
|
||||
app.render('user.tmpl', { user: jane }, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>jane</p>');
|
||||
assert.strictEqual(str, '<p>jane</p>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -350,12 +351,12 @@ describe('app', function(){
|
||||
|
||||
app.render('something', {cache: true}, function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
assert.strictEqual(count, 1)
|
||||
assert.strictEqual(str, 'abstract engine')
|
||||
app.render('something', {cache: true}, function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
assert.strictEqual(count, 1)
|
||||
assert.strictEqual(str, 'abstract engine')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
var after = require('after')
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
@@ -19,5 +21,123 @@ describe('app', function(){
|
||||
.get('/foo?name=tobi')
|
||||
.expect('name=tobi', done);
|
||||
})
|
||||
|
||||
it('should only extend for the referenced app', function (done) {
|
||||
var app1 = express()
|
||||
var app2 = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app1.request.foobar = function () {
|
||||
return 'tobi'
|
||||
}
|
||||
|
||||
app1.get('/', function (req, res) {
|
||||
res.send(req.foobar())
|
||||
})
|
||||
|
||||
app2.get('/', function (req, res) {
|
||||
res.send(req.foobar())
|
||||
})
|
||||
|
||||
request(app1)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', cb)
|
||||
|
||||
request(app2)
|
||||
.get('/')
|
||||
.expect(500, /(?:not a function|has no method)/, cb)
|
||||
})
|
||||
|
||||
it('should inherit to sub apps', function (done) {
|
||||
var app1 = express()
|
||||
var app2 = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app1.request.foobar = function () {
|
||||
return 'tobi'
|
||||
}
|
||||
|
||||
app1.use('/sub', app2)
|
||||
|
||||
app1.get('/', function (req, res) {
|
||||
res.send(req.foobar())
|
||||
})
|
||||
|
||||
app2.get('/', function (req, res) {
|
||||
res.send(req.foobar())
|
||||
})
|
||||
|
||||
request(app1)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', cb)
|
||||
|
||||
request(app1)
|
||||
.get('/sub')
|
||||
.expect(200, 'tobi', cb)
|
||||
})
|
||||
|
||||
it('should allow sub app to override', function (done) {
|
||||
var app1 = express()
|
||||
var app2 = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app1.request.foobar = function () {
|
||||
return 'tobi'
|
||||
}
|
||||
|
||||
app2.request.foobar = function () {
|
||||
return 'loki'
|
||||
}
|
||||
|
||||
app1.use('/sub', app2)
|
||||
|
||||
app1.get('/', function (req, res) {
|
||||
res.send(req.foobar())
|
||||
})
|
||||
|
||||
app2.get('/', function (req, res) {
|
||||
res.send(req.foobar())
|
||||
})
|
||||
|
||||
request(app1)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', cb)
|
||||
|
||||
request(app1)
|
||||
.get('/sub')
|
||||
.expect(200, 'loki', cb)
|
||||
})
|
||||
|
||||
it('should not pollute parent app', function (done) {
|
||||
var app1 = express()
|
||||
var app2 = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app1.request.foobar = function () {
|
||||
return 'tobi'
|
||||
}
|
||||
|
||||
app2.request.foobar = function () {
|
||||
return 'loki'
|
||||
}
|
||||
|
||||
app1.use('/sub', app2)
|
||||
|
||||
app1.get('/sub/foo', function (req, res) {
|
||||
res.send(req.foobar())
|
||||
})
|
||||
|
||||
app2.get('/', function (req, res) {
|
||||
res.send(req.foobar())
|
||||
})
|
||||
|
||||
request(app1)
|
||||
.get('/sub')
|
||||
.expect(200, 'loki', cb)
|
||||
|
||||
request(app1)
|
||||
.get('/sub/foo')
|
||||
.expect(200, 'tobi', cb)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
var after = require('after')
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
@@ -20,25 +22,122 @@ describe('app', function(){
|
||||
.expect('HEY', done);
|
||||
})
|
||||
|
||||
it('should not be influenced by other app protos', function(done){
|
||||
var app = express()
|
||||
, app2 = express();
|
||||
it('should only extend for the referenced app', function (done) {
|
||||
var app1 = express()
|
||||
var app2 = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app.response.shout = function(str){
|
||||
this.send(str.toUpperCase());
|
||||
};
|
||||
app1.response.shout = function (str) {
|
||||
this.send(str.toUpperCase())
|
||||
}
|
||||
|
||||
app2.response.shout = function(str){
|
||||
this.send(str);
|
||||
};
|
||||
app1.get('/', function (req, res) {
|
||||
res.shout('foo')
|
||||
})
|
||||
|
||||
app.use(function(req, res){
|
||||
res.shout('hey');
|
||||
});
|
||||
app2.get('/', function (req, res) {
|
||||
res.shout('foo')
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('HEY', done);
|
||||
request(app1)
|
||||
.get('/')
|
||||
.expect(200, 'FOO', cb)
|
||||
|
||||
request(app2)
|
||||
.get('/')
|
||||
.expect(500, /(?:not a function|has no method)/, cb)
|
||||
})
|
||||
|
||||
it('should inherit to sub apps', function (done) {
|
||||
var app1 = express()
|
||||
var app2 = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app1.response.shout = function (str) {
|
||||
this.send(str.toUpperCase())
|
||||
}
|
||||
|
||||
app1.use('/sub', app2)
|
||||
|
||||
app1.get('/', function (req, res) {
|
||||
res.shout('foo')
|
||||
})
|
||||
|
||||
app2.get('/', function (req, res) {
|
||||
res.shout('foo')
|
||||
})
|
||||
|
||||
request(app1)
|
||||
.get('/')
|
||||
.expect(200, 'FOO', cb)
|
||||
|
||||
request(app1)
|
||||
.get('/sub')
|
||||
.expect(200, 'FOO', cb)
|
||||
})
|
||||
|
||||
it('should allow sub app to override', function (done) {
|
||||
var app1 = express()
|
||||
var app2 = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app1.response.shout = function (str) {
|
||||
this.send(str.toUpperCase())
|
||||
}
|
||||
|
||||
app2.response.shout = function (str) {
|
||||
this.send(str + '!')
|
||||
}
|
||||
|
||||
app1.use('/sub', app2)
|
||||
|
||||
app1.get('/', function (req, res) {
|
||||
res.shout('foo')
|
||||
})
|
||||
|
||||
app2.get('/', function (req, res) {
|
||||
res.shout('foo')
|
||||
})
|
||||
|
||||
request(app1)
|
||||
.get('/')
|
||||
.expect(200, 'FOO', cb)
|
||||
|
||||
request(app1)
|
||||
.get('/sub')
|
||||
.expect(200, 'foo!', cb)
|
||||
})
|
||||
|
||||
it('should not pollute parent app', function (done) {
|
||||
var app1 = express()
|
||||
var app2 = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app1.response.shout = function (str) {
|
||||
this.send(str.toUpperCase())
|
||||
}
|
||||
|
||||
app2.response.shout = function (str) {
|
||||
this.send(str + '!')
|
||||
}
|
||||
|
||||
app1.use('/sub', app2)
|
||||
|
||||
app1.get('/sub/foo', function (req, res) {
|
||||
res.shout('foo')
|
||||
})
|
||||
|
||||
app2.get('/', function (req, res) {
|
||||
res.shout('foo')
|
||||
})
|
||||
|
||||
request(app1)
|
||||
.get('/sub')
|
||||
.expect(200, 'foo!', cb)
|
||||
|
||||
request(app1)
|
||||
.get('/sub/foo')
|
||||
.expect(200, 'FOO', cb)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../');
|
||||
var request = require('supertest');
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var after = require('after');
|
||||
var express = require('../')
|
||||
@@ -5,6 +6,8 @@ var express = require('../')
|
||||
, assert = require('assert')
|
||||
, methods = require('methods');
|
||||
|
||||
var shouldSkipQuery = require('./support/utils').shouldSkipQuery
|
||||
|
||||
describe('app.router', function(){
|
||||
it('should restore req.params after leaving router', function(done){
|
||||
var app = express();
|
||||
@@ -38,6 +41,9 @@ describe('app.router', function(){
|
||||
if (method === 'connect') return;
|
||||
|
||||
it('should include ' + method.toUpperCase(), function(done){
|
||||
if (method === 'query' && shouldSkipQuery(process.versions.node)) {
|
||||
this.skip()
|
||||
}
|
||||
var app = express();
|
||||
|
||||
app[method]('/foo', function(req, res){
|
||||
@@ -49,9 +55,20 @@ describe('app.router', function(){
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should not support ' + method.toUpperCase() + ' without a path', function () {
|
||||
if (method === 'get' || (method === 'query' && shouldSkipQuery(process.versions.node))) {
|
||||
this.skip();
|
||||
}
|
||||
var app = express();
|
||||
|
||||
assert.throws(function () {
|
||||
app[method](function (req, res) { });
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject numbers for app.' + method, function(){
|
||||
var app = express();
|
||||
app[method].bind(app, '/', 3).should.throw(/Number/);
|
||||
assert.throws(app[method].bind(app, '/', 3), /Number/)
|
||||
})
|
||||
});
|
||||
|
||||
@@ -89,7 +106,7 @@ describe('app.router', function(){
|
||||
it('should decode correct params', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/:name', function(req, res, next){
|
||||
app.get('/:name', function (req, res) {
|
||||
res.send(req.params.name);
|
||||
});
|
||||
|
||||
@@ -101,7 +118,7 @@ describe('app.router', function(){
|
||||
it('should not accept params in malformed paths', function(done) {
|
||||
var app = express();
|
||||
|
||||
app.get('/:name', function(req, res, next){
|
||||
app.get('/:name', function (req, res) {
|
||||
res.send(req.params.name);
|
||||
});
|
||||
|
||||
@@ -113,7 +130,7 @@ describe('app.router', function(){
|
||||
it('should not decode spaces', function(done) {
|
||||
var app = express();
|
||||
|
||||
app.get('/:name', function(req, res, next){
|
||||
app.get('/:name', function (req, res) {
|
||||
res.send(req.params.name);
|
||||
});
|
||||
|
||||
@@ -125,7 +142,7 @@ describe('app.router', function(){
|
||||
it('should work with unicode', function(done) {
|
||||
var app = express();
|
||||
|
||||
app.get('/:name', function(req, res, next){
|
||||
app.get('/:name', function (req, res) {
|
||||
res.send(req.params.name);
|
||||
});
|
||||
|
||||
@@ -187,6 +204,23 @@ describe('app.router', function(){
|
||||
.expect('editing user 10', done);
|
||||
})
|
||||
|
||||
if (supportsRegexp('(?<foo>.*)')) {
|
||||
it('should populate req.params with named captures', function(done){
|
||||
var app = express();
|
||||
var re = new RegExp('^/user/(?<userId>[0-9]+)/(view|edit)?$');
|
||||
|
||||
app.get(re, function(req, res){
|
||||
var id = req.params.userId
|
||||
, op = req.params[0];
|
||||
res.end(op + 'ing user ' + id);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/10/edit')
|
||||
.expect('editing user 10', done);
|
||||
})
|
||||
}
|
||||
|
||||
it('should ensure regexp matches path prefix', function (done) {
|
||||
var app = express()
|
||||
var p = []
|
||||
@@ -636,18 +670,19 @@ describe('app.router', function(){
|
||||
|
||||
it('should work cross-segment', function(done){
|
||||
var app = express();
|
||||
var cb = after(2, done)
|
||||
|
||||
app.get('/api*', function(req, res){
|
||||
res.send(req.params[0]);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/api')
|
||||
.expect('', function(){
|
||||
request(app)
|
||||
.get('/api')
|
||||
.expect(200, '', cb)
|
||||
|
||||
request(app)
|
||||
.get('/api/hey')
|
||||
.expect('/hey', done);
|
||||
});
|
||||
.expect(200, '/hey', cb)
|
||||
})
|
||||
|
||||
it('should allow naming', function(done){
|
||||
@@ -863,36 +898,38 @@ describe('app.router', function(){
|
||||
describe('.:name', function(){
|
||||
it('should denote a format', function(done){
|
||||
var app = express();
|
||||
var cb = after(2, done)
|
||||
|
||||
app.get('/:name.:format', function(req, res){
|
||||
res.end(req.params.name + ' as ' + req.params.format);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo.json')
|
||||
.expect('foo as json', function(){
|
||||
request(app)
|
||||
.get('/foo.json')
|
||||
.expect(200, 'foo as json', cb)
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect(404, done);
|
||||
});
|
||||
.expect(404, cb)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.:name?', function(){
|
||||
it('should denote an optional format', function(done){
|
||||
var app = express();
|
||||
var cb = after(2, done)
|
||||
|
||||
app.get('/:name.:format?', function(req, res){
|
||||
res.end(req.params.name + ' as ' + (req.params.format || 'html'));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('foo as html', function(){
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect(200, 'foo as html', cb)
|
||||
|
||||
request(app)
|
||||
.get('/foo.json')
|
||||
.expect('foo as json', done);
|
||||
});
|
||||
.expect(200, 'foo as json', cb)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -906,7 +943,7 @@ describe('app.router', function(){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/bar', function(req, res){
|
||||
app.get('/bar', function () {
|
||||
assert(0);
|
||||
});
|
||||
|
||||
@@ -915,7 +952,7 @@ describe('app.router', function(){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/foo', function(req, res, next){
|
||||
app.get('/foo', function (req, res) {
|
||||
calls.push('/foo 2');
|
||||
res.json(calls)
|
||||
});
|
||||
@@ -935,7 +972,7 @@ describe('app.router', function(){
|
||||
next('route')
|
||||
}
|
||||
|
||||
app.get('/foo', fn, function(req, res, next){
|
||||
app.get('/foo', fn, function (req, res) {
|
||||
res.end('failure')
|
||||
});
|
||||
|
||||
@@ -960,11 +997,11 @@ describe('app.router', function(){
|
||||
next('router')
|
||||
}
|
||||
|
||||
router.get('/foo', fn, function (req, res, next) {
|
||||
router.get('/foo', fn, function (req, res) {
|
||||
res.end('failure')
|
||||
})
|
||||
|
||||
router.get('/foo', function (req, res, next) {
|
||||
router.get('/foo', function (req, res) {
|
||||
res.end('failure')
|
||||
})
|
||||
|
||||
@@ -991,7 +1028,7 @@ describe('app.router', function(){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/bar', function(req, res){
|
||||
app.get('/bar', function () {
|
||||
assert(0);
|
||||
});
|
||||
|
||||
@@ -1000,7 +1037,7 @@ describe('app.router', function(){
|
||||
next(new Error('fail'));
|
||||
});
|
||||
|
||||
app.get('/foo', function(req, res, next){
|
||||
app.get('/foo', function () {
|
||||
assert(0);
|
||||
});
|
||||
|
||||
@@ -1102,6 +1139,15 @@ describe('app.router', function(){
|
||||
|
||||
it('should be chainable', function(){
|
||||
var app = express();
|
||||
app.get('/', function(){}).should.equal(app);
|
||||
assert.strictEqual(app.get('/', function () {}), app)
|
||||
})
|
||||
})
|
||||
|
||||
function supportsRegexp(source) {
|
||||
try {
|
||||
new RegExp(source)
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
@@ -34,20 +37,20 @@ describe('app', function(){
|
||||
next();
|
||||
}, function(err, req, res, next){
|
||||
b = true;
|
||||
err.message.should.equal('fabricated error');
|
||||
assert.strictEqual(err.message, 'fabricated error')
|
||||
next(err);
|
||||
}, function(err, req, res, next){
|
||||
c = true;
|
||||
err.message.should.equal('fabricated error');
|
||||
assert.strictEqual(err.message, 'fabricated error')
|
||||
next();
|
||||
}, function(err, req, res, next){
|
||||
d = true;
|
||||
next();
|
||||
}, function(req, res){
|
||||
a.should.be.false()
|
||||
b.should.be.true()
|
||||
c.should.be.true()
|
||||
d.should.be.false()
|
||||
assert.ok(!a)
|
||||
assert.ok(b)
|
||||
assert.ok(c)
|
||||
assert.ok(!d)
|
||||
res.send(204);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var after = require('after');
|
||||
var assert = require('assert')
|
||||
@@ -10,7 +11,7 @@ describe('app', function(){
|
||||
, app = express();
|
||||
|
||||
blog.on('mount', function(arg){
|
||||
arg.should.equal(app);
|
||||
assert.strictEqual(arg, app)
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -37,6 +38,7 @@ describe('app', function(){
|
||||
var blog = express()
|
||||
, forum = express()
|
||||
, app = express();
|
||||
var cb = after(2, done)
|
||||
|
||||
blog.get('/', function(req, res){
|
||||
res.end('blog');
|
||||
@@ -50,12 +52,12 @@ describe('app', function(){
|
||||
app.use('/forum', forum);
|
||||
|
||||
request(app)
|
||||
.get('/blog')
|
||||
.expect('blog', function(){
|
||||
request(app)
|
||||
.get('/blog')
|
||||
.expect(200, 'blog', cb)
|
||||
|
||||
request(app)
|
||||
.get('/forum')
|
||||
.expect('forum', done);
|
||||
});
|
||||
.expect(200, 'forum', cb)
|
||||
})
|
||||
|
||||
it('should set the child\'s .parent', function(){
|
||||
@@ -63,7 +65,7 @@ describe('app', function(){
|
||||
, app = express();
|
||||
|
||||
app.use('/blog', blog);
|
||||
blog.parent.should.equal(app);
|
||||
assert.strictEqual(blog.parent, app)
|
||||
})
|
||||
|
||||
it('should support dynamic routes', function(done){
|
||||
@@ -102,11 +104,11 @@ describe('app', function(){
|
||||
});
|
||||
|
||||
blog.once('mount', function (parent) {
|
||||
parent.should.equal(app);
|
||||
assert.strictEqual(parent, app)
|
||||
cb();
|
||||
});
|
||||
other.once('mount', function (parent) {
|
||||
parent.should.equal(app);
|
||||
assert.strictEqual(parent, app)
|
||||
cb();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert');
|
||||
var express = require('..');
|
||||
@@ -10,6 +11,12 @@ describe('config', function () {
|
||||
assert.equal(app.get('foo'), 'bar');
|
||||
})
|
||||
|
||||
it('should set prototype values', function () {
|
||||
var app = express()
|
||||
app.set('hasOwnProperty', 42)
|
||||
assert.strictEqual(app.get('hasOwnProperty'), 42)
|
||||
})
|
||||
|
||||
it('should return the app', function () {
|
||||
var app = express();
|
||||
assert.equal(app.set('foo', 'bar'), app);
|
||||
@@ -20,6 +27,17 @@ describe('config', function () {
|
||||
assert.equal(app.set('foo', undefined), app);
|
||||
})
|
||||
|
||||
it('should return set value', function () {
|
||||
var app = express()
|
||||
app.set('foo', 'bar')
|
||||
assert.strictEqual(app.set('foo'), 'bar')
|
||||
})
|
||||
|
||||
it('should return undefined for prototype values', function () {
|
||||
var app = express()
|
||||
assert.strictEqual(app.set('hasOwnProperty'), undefined)
|
||||
})
|
||||
|
||||
describe('"etag"', function(){
|
||||
it('should throw on bad value', function(){
|
||||
var app = express();
|
||||
@@ -50,6 +68,11 @@ describe('config', function () {
|
||||
assert.strictEqual(app.get('foo'), undefined);
|
||||
})
|
||||
|
||||
it('should return undefined for prototype values', function () {
|
||||
var app = express()
|
||||
assert.strictEqual(app.get('hasOwnProperty'), undefined)
|
||||
})
|
||||
|
||||
it('should otherwise return the value', function(){
|
||||
var app = express();
|
||||
app.set('foo', 'bar');
|
||||
@@ -124,6 +147,12 @@ describe('config', function () {
|
||||
assert.equal(app.enable('tobi'), app);
|
||||
assert.strictEqual(app.get('tobi'), true);
|
||||
})
|
||||
|
||||
it('should set prototype values', function () {
|
||||
var app = express()
|
||||
app.enable('hasOwnProperty')
|
||||
assert.strictEqual(app.get('hasOwnProperty'), true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.disable()', function(){
|
||||
@@ -132,6 +161,12 @@ describe('config', function () {
|
||||
assert.equal(app.disable('tobi'), app);
|
||||
assert.strictEqual(app.get('tobi'), false);
|
||||
})
|
||||
|
||||
it('should set prototype values', function () {
|
||||
var app = express()
|
||||
app.disable('hasOwnProperty')
|
||||
assert.strictEqual(app.get('hasOwnProperty'), false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.enabled()', function(){
|
||||
@@ -145,6 +180,11 @@ describe('config', function () {
|
||||
app.set('foo', 'bar');
|
||||
assert.strictEqual(app.enabled('foo'), true);
|
||||
})
|
||||
|
||||
it('should default to false for prototype values', function () {
|
||||
var app = express()
|
||||
assert.strictEqual(app.enabled('hasOwnProperty'), false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.disabled()', function(){
|
||||
@@ -158,5 +198,10 @@ describe('config', function () {
|
||||
app.set('foo', 'bar');
|
||||
assert.strictEqual(app.disabled('foo'), false);
|
||||
})
|
||||
|
||||
it('should default to true for prototype values', function () {
|
||||
var app = express()
|
||||
assert.strictEqual(app.disabled('hasOwnProperty'), true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var express = require('../');
|
||||
var request = require('supertest');
|
||||
var should = require('should');
|
||||
|
||||
describe('exports', function(){
|
||||
it('should expose Router', function(){
|
||||
express.Router.should.be.a.Function()
|
||||
assert.strictEqual(typeof express.Router, 'function')
|
||||
})
|
||||
|
||||
it('should expose json middleware', function () {
|
||||
@@ -35,20 +35,23 @@ describe('exports', function(){
|
||||
})
|
||||
|
||||
it('should expose the application prototype', function(){
|
||||
express.application.set.should.be.a.Function()
|
||||
assert.strictEqual(typeof express.application, 'object')
|
||||
assert.strictEqual(typeof express.application.set, 'function')
|
||||
})
|
||||
|
||||
it('should expose the request prototype', function(){
|
||||
express.request.accepts.should.be.a.Function()
|
||||
assert.strictEqual(typeof express.request, 'object')
|
||||
assert.strictEqual(typeof express.request.accepts, 'function')
|
||||
})
|
||||
|
||||
it('should expose the response prototype', function(){
|
||||
express.response.send.should.be.a.Function()
|
||||
assert.strictEqual(typeof express.response, 'object')
|
||||
assert.strictEqual(typeof express.response.send, 'function')
|
||||
})
|
||||
|
||||
it('should permit modifying the .application prototype', function(){
|
||||
express.application.foo = function(){ return 'bar'; };
|
||||
express().foo().should.equal('bar');
|
||||
assert.strictEqual(express().foo(), 'bar')
|
||||
})
|
||||
|
||||
it('should permit modifying the .request prototype', function(done){
|
||||
@@ -78,10 +81,7 @@ describe('exports', function(){
|
||||
})
|
||||
|
||||
it('should throw on old middlewares', function(){
|
||||
var error;
|
||||
try { express.bodyParser; } catch (e) { error = e; }
|
||||
should(error).have.property('message');
|
||||
error.message.should.containEql('middleware');
|
||||
error.message.should.containEql('bodyParser');
|
||||
assert.throws(function () { express.bodyParser() }, /Error:.*middleware.*bodyParser/)
|
||||
assert.throws(function () { express.limit() }, /Error:.*middleware.*limit/)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var asyncHooks = tryRequire('async_hooks')
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
|
||||
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||
? describe
|
||||
: describe.skip
|
||||
|
||||
describe('express.json()', function () {
|
||||
it('should parse JSON', function (done) {
|
||||
request(createApp())
|
||||
@@ -37,6 +43,14 @@ describe('express.json()', function () {
|
||||
.expect(200, '{}', done)
|
||||
})
|
||||
|
||||
it('should 400 when only whitespace', function (done) {
|
||||
request(createApp())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(' \n')
|
||||
.expect(400, '[entity.parse.failed] ' + parseError(' '), done)
|
||||
})
|
||||
|
||||
it('should 400 when invalid content-length', function (done) {
|
||||
var app = express()
|
||||
|
||||
@@ -58,6 +72,32 @@ describe('express.json()', function () {
|
||||
.expect(400, /content length/, done)
|
||||
})
|
||||
|
||||
it('should 500 if stream not readable', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
req.on('end', next)
|
||||
req.resume()
|
||||
})
|
||||
|
||||
app.use(express.json())
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.status(err.status || 500)
|
||||
res.send('[' + err.type + '] ' + err.message)
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
res.json(req.body)
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{"user":"tobi"}')
|
||||
.expect(500, '[stream.not.readable] stream is not readable', done)
|
||||
})
|
||||
|
||||
it('should handle duplicated middleware', function (done) {
|
||||
var app = express()
|
||||
|
||||
@@ -85,7 +125,7 @@ describe('express.json()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{:')
|
||||
.expect(400, parseError('{:'), done)
|
||||
.expect(400, '[entity.parse.failed] ' + parseError('{:'), done)
|
||||
})
|
||||
|
||||
it('should 400 for incomplete', function (done) {
|
||||
@@ -93,16 +133,7 @@ describe('express.json()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{"user"')
|
||||
.expect(400, parseError('{"user"'), done)
|
||||
})
|
||||
|
||||
it('should error with type = "entity.parse.failed"', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send(' {"user"')
|
||||
.expect(400, 'entity.parse.failed', done)
|
||||
.expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done)
|
||||
})
|
||||
|
||||
it('should include original body on error object', function (done) {
|
||||
@@ -123,24 +154,13 @@ describe('express.json()', function () {
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('Content-Length', '1034')
|
||||
.send(JSON.stringify({ str: buf.toString() }))
|
||||
.expect(413, done)
|
||||
})
|
||||
|
||||
it('should error with type = "entity.too.large"', function (done) {
|
||||
var buf = Buffer.alloc(1024, '.')
|
||||
request(createApp({ limit: '1kb' }))
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('Content-Length', '1034')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send(JSON.stringify({ str: buf.toString() }))
|
||||
.expect(413, 'entity.too.large', done)
|
||||
.expect(413, '[entity.too.large] request entity too large', done)
|
||||
})
|
||||
|
||||
it('should 413 when over limit with chunked encoding', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var buf = Buffer.alloc(1024, '.')
|
||||
var server = createApp({ limit: '1kb' })
|
||||
var test = request(server).post('/')
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.set('Transfer-Encoding', 'chunked')
|
||||
test.write('{"str":')
|
||||
@@ -148,6 +168,15 @@ describe('express.json()', function () {
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should 413 when inflated body over limit', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex'))
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should accept number of bytes', function (done) {
|
||||
var buf = Buffer.alloc(1024, '.')
|
||||
request(createApp({ limit: 1024 }))
|
||||
@@ -160,11 +189,11 @@ describe('express.json()', function () {
|
||||
it('should not change when options altered', function (done) {
|
||||
var buf = Buffer.alloc(1024, '.')
|
||||
var options = { limit: '1kb' }
|
||||
var server = createApp(options)
|
||||
var app = createApp(options)
|
||||
|
||||
options.limit = '100kb'
|
||||
|
||||
request(server)
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(JSON.stringify({ str: buf.toString() }))
|
||||
@@ -173,14 +202,23 @@ describe('express.json()', function () {
|
||||
|
||||
it('should not hang response', function (done) {
|
||||
var buf = Buffer.alloc(10240, '.')
|
||||
var server = createApp({ limit: '8kb' })
|
||||
var test = request(server).post('/')
|
||||
var app = createApp({ limit: '8kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.write(buf)
|
||||
test.write(buf)
|
||||
test.write(buf)
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should not error when inflating', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex'))
|
||||
test.expect(413, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with inflate option', function () {
|
||||
@@ -194,7 +232,7 @@ describe('express.json()', function () {
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
|
||||
test.expect(415, 'content encoding unsupported', done)
|
||||
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -224,7 +262,7 @@ describe('express.json()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('true')
|
||||
.expect(400, parseError('#rue').replace('#', 't'), done)
|
||||
.expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -252,7 +290,7 @@ describe('express.json()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('true')
|
||||
.expect(400, parseError('#rue').replace('#', 't'), done)
|
||||
.expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
|
||||
})
|
||||
|
||||
it('should not parse primitives with leading whitespaces', function (done) {
|
||||
@@ -260,7 +298,7 @@ describe('express.json()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(' true')
|
||||
.expect(400, parseError(' #rue').replace('#', 't'), done)
|
||||
.expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done)
|
||||
})
|
||||
|
||||
it('should allow leading whitespaces in JSON', function (done) {
|
||||
@@ -271,15 +309,6 @@ describe('express.json()', function () {
|
||||
.expect(200, '{"user":"tobi"}', done)
|
||||
})
|
||||
|
||||
it('should error with type = "entity.parse.failed"', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send('true')
|
||||
.expect(400, 'entity.parse.failed', done)
|
||||
})
|
||||
|
||||
it('should include correct message in stack trace', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
@@ -287,7 +316,7 @@ describe('express.json()', function () {
|
||||
.set('X-Error-Property', 'stack')
|
||||
.send('true')
|
||||
.expect(400)
|
||||
.expect(shouldContainInBody(parseError('#rue').replace('#', 't')))
|
||||
.expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't')))
|
||||
.end(done)
|
||||
})
|
||||
})
|
||||
@@ -396,65 +425,59 @@ describe('express.json()', function () {
|
||||
})
|
||||
|
||||
it('should error from verify', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('["tobi"]')
|
||||
.expect(403, 'no arrays', done)
|
||||
})
|
||||
|
||||
it('should error with type = "entity.verify.failed"', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
} })
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send('["tobi"]')
|
||||
.expect(403, 'entity.verify.failed', done)
|
||||
.expect(403, '[entity.verify.failed] no arrays', done)
|
||||
})
|
||||
|
||||
it('should allow custom codes', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x5b) return
|
||||
var err = new Error('no arrays')
|
||||
err.status = 400
|
||||
throw err
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x5b) return
|
||||
var err = new Error('no arrays')
|
||||
err.status = 400
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('["tobi"]')
|
||||
.expect(400, 'no arrays', done)
|
||||
.expect(400, '[entity.verify.failed] no arrays', done)
|
||||
})
|
||||
|
||||
it('should allow custom type', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x5b) return
|
||||
var err = new Error('no arrays')
|
||||
err.type = 'foo.bar'
|
||||
throw err
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x5b) return
|
||||
var err = new Error('no arrays')
|
||||
err.type = 'foo.bar'
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send('["tobi"]')
|
||||
.expect(403, 'foo.bar', done)
|
||||
.expect(403, '[foo.bar] no arrays', done)
|
||||
})
|
||||
|
||||
it('should include original body on error object', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
@@ -465,9 +488,11 @@ describe('express.json()', function () {
|
||||
})
|
||||
|
||||
it('should allow pass-through', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
@@ -477,9 +502,11 @@ describe('express.json()', function () {
|
||||
})
|
||||
|
||||
it('should work with different charsets', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
}
|
||||
})
|
||||
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/json; charset=utf-16')
|
||||
@@ -488,14 +515,120 @@ describe('express.json()', function () {
|
||||
})
|
||||
|
||||
it('should 415 on unknown charset prior to verify', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
throw new Error('unexpected verify call')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
throw new Error('unexpected verify call')
|
||||
}
|
||||
})
|
||||
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/json; charset=x-bogus')
|
||||
test.write(Buffer.from('00000000', 'hex'))
|
||||
test.expect(415, 'unsupported charset "X-BOGUS"', done)
|
||||
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
|
||||
})
|
||||
})
|
||||
|
||||
describeAsyncHooks('async local storage', function () {
|
||||
before(function () {
|
||||
var app = express()
|
||||
var store = { foo: 'bar' }
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||
req.asyncLocalStorage.run(store, next)
|
||||
})
|
||||
|
||||
app.use(express.json())
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
var local = req.asyncLocalStorage.getStore()
|
||||
|
||||
if (local) {
|
||||
res.setHeader('x-store-foo', String(local.foo))
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
var local = req.asyncLocalStorage.getStore()
|
||||
|
||||
if (local) {
|
||||
res.setHeader('x-store-foo', String(local.foo))
|
||||
}
|
||||
|
||||
res.status(err.status || 500)
|
||||
res.send('[' + err.type + '] ' + err.message)
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
res.json(req.body)
|
||||
})
|
||||
|
||||
this.app = app
|
||||
})
|
||||
|
||||
it('should presist store', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{"user":"tobi"}')
|
||||
.expect(200)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.expect('{"user":"tobi"}')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when unmatched content-type', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/fizzbuzz')
|
||||
.send('buzz')
|
||||
.expect(200)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.expect('{}')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when inflated', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
|
||||
test.expect(200)
|
||||
test.expect('x-store-foo', 'bar')
|
||||
test.expect('{"name":"论"}')
|
||||
test.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when inflate error', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
|
||||
test.expect(400)
|
||||
test.expect('x-store-foo', 'bar')
|
||||
test.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when parse error', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{"user":')
|
||||
.expect(400)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when limit exceeded', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}')
|
||||
.expect(413)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.end(done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -537,15 +670,7 @@ describe('express.json()', function () {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Type', 'application/json; charset=koi8-r')
|
||||
test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
|
||||
test.expect(415, 'unsupported charset "KOI8-R"', done)
|
||||
})
|
||||
|
||||
it('should error with type = "charset.unsupported"', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Type', 'application/json; charset=koi8-r')
|
||||
test.set('X-Error-Property', 'type')
|
||||
test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
|
||||
test.expect(415, 'charset.unsupported', done)
|
||||
test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -598,16 +723,7 @@ describe('express.json()', function () {
|
||||
test.set('Content-Encoding', 'nulls')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.write(Buffer.from('000000000000', 'hex'))
|
||||
test.expect(415, 'unsupported content encoding "nulls"', done)
|
||||
})
|
||||
|
||||
it('should error with type = "encoding.unsupported"', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'nulls')
|
||||
test.set('Content-Type', 'application/json')
|
||||
test.set('X-Error-Property', 'type')
|
||||
test.write(Buffer.from('000000000000', 'hex'))
|
||||
test.expect(415, 'encoding.unsupported', done)
|
||||
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
|
||||
})
|
||||
|
||||
it('should 400 on malformed encoding', function (done) {
|
||||
@@ -638,7 +754,9 @@ function createApp (options) {
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.status(err.status || 500)
|
||||
res.send(String(err[req.headers['x-error-property'] || 'message']))
|
||||
res.send(String(req.headers['x-error-property']
|
||||
? err[req.headers['x-error-property']]
|
||||
: ('[' + err.type + '] ' + err.message)))
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
@@ -662,3 +780,11 @@ function shouldContainInBody (str) {
|
||||
'expected \'' + res.text + '\' to contain \'' + str + '\'')
|
||||
}
|
||||
}
|
||||
|
||||
function tryRequire (name) {
|
||||
try {
|
||||
return require(name)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var asyncHooks = tryRequire('async_hooks')
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
|
||||
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||
? describe
|
||||
: describe.skip
|
||||
|
||||
describe('express.raw()', function () {
|
||||
before(function () {
|
||||
this.app = createApp()
|
||||
@@ -59,6 +65,36 @@ describe('express.raw()', function () {
|
||||
.expect(200, { buf: '' }, done)
|
||||
})
|
||||
|
||||
it('should 500 if stream not readable', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
req.on('end', next)
|
||||
req.resume()
|
||||
})
|
||||
|
||||
app.use(express.raw())
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.status(err.status || 500)
|
||||
res.send('[' + err.type + '] ' + err.message)
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
if (Buffer.isBuffer(req.body)) {
|
||||
res.json({ buf: req.body.toString('hex') })
|
||||
} else {
|
||||
res.json(req.body)
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/octet-stream')
|
||||
.send('the user is tobi')
|
||||
.expect(500, '[stream.not.readable] stream is not readable', done)
|
||||
})
|
||||
|
||||
it('should handle duplicated middleware', function (done) {
|
||||
var app = express()
|
||||
|
||||
@@ -101,6 +137,15 @@ describe('express.raw()', function () {
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should 413 when inflated body over limit', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should accept number of bytes', function (done) {
|
||||
var buf = Buffer.alloc(1028, '.')
|
||||
var app = createApp({ limit: 1024 })
|
||||
@@ -133,6 +178,15 @@ describe('express.raw()', function () {
|
||||
test.write(buf)
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should not error when inflating', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex'))
|
||||
test.expect(413, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with inflate option', function () {
|
||||
@@ -146,7 +200,7 @@ describe('express.raw()', function () {
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
||||
test.expect(415, 'content encoding unsupported', done)
|
||||
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -262,34 +316,40 @@ describe('express.raw()', function () {
|
||||
})
|
||||
|
||||
it('should error from verify', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x00) throw new Error('no leading null')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x00) throw new Error('no leading null')
|
||||
}
|
||||
})
|
||||
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
test.write(Buffer.from('000102', 'hex'))
|
||||
test.expect(403, 'no leading null', done)
|
||||
test.expect(403, '[entity.verify.failed] no leading null', done)
|
||||
})
|
||||
|
||||
it('should allow custom codes', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x00) return
|
||||
var err = new Error('no leading null')
|
||||
err.status = 400
|
||||
throw err
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x00) return
|
||||
var err = new Error('no leading null')
|
||||
err.status = 400
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
test.write(Buffer.from('000102', 'hex'))
|
||||
test.expect(400, 'no leading null', done)
|
||||
test.expect(400, '[entity.verify.failed] no leading null', done)
|
||||
})
|
||||
|
||||
it('should allow pass-through', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x00) throw new Error('no leading null')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x00) throw new Error('no leading null')
|
||||
}
|
||||
})
|
||||
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
@@ -298,6 +358,104 @@ describe('express.raw()', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describeAsyncHooks('async local storage', function () {
|
||||
before(function () {
|
||||
var app = express()
|
||||
var store = { foo: 'bar' }
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||
req.asyncLocalStorage.run(store, next)
|
||||
})
|
||||
|
||||
app.use(express.raw())
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
var local = req.asyncLocalStorage.getStore()
|
||||
|
||||
if (local) {
|
||||
res.setHeader('x-store-foo', String(local.foo))
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
var local = req.asyncLocalStorage.getStore()
|
||||
|
||||
if (local) {
|
||||
res.setHeader('x-store-foo', String(local.foo))
|
||||
}
|
||||
|
||||
res.status(err.status || 500)
|
||||
res.send('[' + err.type + '] ' + err.message)
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
if (Buffer.isBuffer(req.body)) {
|
||||
res.json({ buf: req.body.toString('hex') })
|
||||
} else {
|
||||
res.json(req.body)
|
||||
}
|
||||
})
|
||||
|
||||
this.app = app
|
||||
})
|
||||
|
||||
it('should presist store', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/octet-stream')
|
||||
.send('the user is tobi')
|
||||
.expect(200)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.expect({ buf: '746865207573657220697320746f6269' })
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when unmatched content-type', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/fizzbuzz')
|
||||
.send('buzz')
|
||||
.expect(200)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.expect('{}')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when inflated', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
||||
test.expect(200)
|
||||
test.expect('x-store-foo', 'bar')
|
||||
test.expect({ buf: '6e616d653de8aeba' })
|
||||
test.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when inflate error', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))
|
||||
test.expect(400)
|
||||
test.expect('x-store-foo', 'bar')
|
||||
test.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when limit exceeded', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/octet-stream')
|
||||
.send('the user is ' + Buffer.alloc(1024 * 100, '.').toString())
|
||||
.expect(413)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.end(done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('charset', function () {
|
||||
before(function () {
|
||||
this.app = createApp()
|
||||
@@ -355,12 +513,12 @@ describe('express.raw()', function () {
|
||||
test.expect(200, { buf: '6e616d653de8aeba' }, done)
|
||||
})
|
||||
|
||||
it('should fail on unknown encoding', function (done) {
|
||||
it('should 415 on unknown encoding', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'nulls')
|
||||
test.set('Content-Type', 'application/octet-stream')
|
||||
test.write(Buffer.from('000000000000', 'hex'))
|
||||
test.expect(415, 'unsupported content encoding "nulls"', done)
|
||||
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -372,7 +530,9 @@ function createApp (options) {
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.status(err.status || 500)
|
||||
res.send(String(err[req.headers['x-error-property'] || 'message']))
|
||||
res.send(String(req.headers['x-error-property']
|
||||
? err[req.headers['x-error-property']]
|
||||
: ('[' + err.type + '] ' + err.message)))
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
@@ -385,3 +545,11 @@ function createApp (options) {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
function tryRequire (name) {
|
||||
try {
|
||||
return require(name)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
@@ -485,7 +486,7 @@ describe('express.static()', function () {
|
||||
request(this.app)
|
||||
.get('/users')
|
||||
.expect('Location', '/users/')
|
||||
.expect(301, /<a href="\/users\/">/, done)
|
||||
.expect(301, /\/users\//, done)
|
||||
})
|
||||
|
||||
it('should redirect directories with query string', function (done) {
|
||||
@@ -507,7 +508,7 @@ describe('express.static()', function () {
|
||||
.get('/snow')
|
||||
.expect('Location', '/snow%20%E2%98%83/')
|
||||
.expect('Content-Type', /html/)
|
||||
.expect(301, />Redirecting to <a href="\/snow%20%E2%98%83\/">\/snow%20%E2%98%83\/<\/a></, done)
|
||||
.expect(301, />Redirecting to \/snow%20%E2%98%83\/</, done)
|
||||
})
|
||||
|
||||
it('should respond with default Content-Security-Policy', function (done) {
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var asyncHooks = tryRequire('async_hooks')
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
|
||||
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||
? describe
|
||||
: describe.skip
|
||||
|
||||
describe('express.text()', function () {
|
||||
before(function () {
|
||||
this.app = createApp()
|
||||
@@ -55,6 +61,32 @@ describe('express.text()', function () {
|
||||
.expect(200, '""', done)
|
||||
})
|
||||
|
||||
it('should 500 if stream not readable', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
req.on('end', next)
|
||||
req.resume()
|
||||
})
|
||||
|
||||
app.use(express.text())
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.status(err.status || 500)
|
||||
res.send('[' + err.type + '] ' + err.message)
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
res.json(req.body)
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'text/plain')
|
||||
.send('user is tobi')
|
||||
.expect(500, '[stream.not.readable] stream is not readable', done)
|
||||
})
|
||||
|
||||
it('should handle duplicated middleware', function (done) {
|
||||
var app = express()
|
||||
|
||||
@@ -74,16 +106,16 @@ describe('express.text()', function () {
|
||||
|
||||
describe('with defaultCharset option', function () {
|
||||
it('should change default charset', function (done) {
|
||||
var app = createApp({ defaultCharset: 'koi8-r' })
|
||||
var test = request(app).post('/')
|
||||
var server = createApp({ defaultCharset: 'koi8-r' })
|
||||
var test = request(server).post('/')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.write(Buffer.from('6e616d6520697320cec5d4', 'hex'))
|
||||
test.expect(200, '"name is нет"', done)
|
||||
})
|
||||
|
||||
it('should honor content-type charset', function (done) {
|
||||
var app = createApp({ defaultCharset: 'koi8-r' })
|
||||
var test = request(app).post('/')
|
||||
var server = createApp({ defaultCharset: 'koi8-r' })
|
||||
var test = request(server).post('/')
|
||||
test.set('Content-Type', 'text/plain; charset=utf-8')
|
||||
test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))
|
||||
test.expect(200, '"name is 论"', done)
|
||||
@@ -102,8 +134,8 @@ describe('express.text()', function () {
|
||||
})
|
||||
|
||||
it('should 413 when over limit with chunked encoding', function (done) {
|
||||
var buf = Buffer.alloc(1028, '.')
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var buf = Buffer.alloc(1028, '.')
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.set('Transfer-Encoding', 'chunked')
|
||||
@@ -111,6 +143,15 @@ describe('express.text()', function () {
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should 413 when inflated body over limit', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should accept number of bytes', function (done) {
|
||||
var buf = Buffer.alloc(1028, '.')
|
||||
request(createApp({ limit: 1024 }))
|
||||
@@ -135,8 +176,8 @@ describe('express.text()', function () {
|
||||
})
|
||||
|
||||
it('should not hang response', function (done) {
|
||||
var buf = Buffer.alloc(10240, '.')
|
||||
var app = createApp({ limit: '8kb' })
|
||||
var buf = Buffer.alloc(10240, '.')
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.write(buf)
|
||||
@@ -144,6 +185,17 @@ describe('express.text()', function () {
|
||||
test.write(buf)
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should not error when inflating', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a1470404', 'hex'))
|
||||
setTimeout(function () {
|
||||
test.expect(413, done)
|
||||
}, 100)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with inflate option', function () {
|
||||
@@ -157,7 +209,7 @@ describe('express.text()', function () {
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))
|
||||
test.expect(415, 'content encoding unsupported', done)
|
||||
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -277,36 +329,42 @@ describe('express.text()', function () {
|
||||
})
|
||||
|
||||
it('should error from verify', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'text/plain')
|
||||
.send(' user is tobi')
|
||||
.expect(403, 'no leading space', done)
|
||||
.expect(403, '[entity.verify.failed] no leading space', done)
|
||||
})
|
||||
|
||||
it('should allow custom codes', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x20) return
|
||||
var err = new Error('no leading space')
|
||||
err.status = 400
|
||||
throw err
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x20) return
|
||||
var err = new Error('no leading space')
|
||||
err.status = 400
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'text/plain')
|
||||
.send(' user is tobi')
|
||||
.expect(400, 'no leading space', done)
|
||||
.expect(400, '[entity.verify.failed] no leading space', done)
|
||||
})
|
||||
|
||||
it('should allow pass-through', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
@@ -316,14 +374,110 @@ describe('express.text()', function () {
|
||||
})
|
||||
|
||||
it('should 415 on unknown charset prior to verify', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
throw new Error('unexpected verify call')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
throw new Error('unexpected verify call')
|
||||
}
|
||||
})
|
||||
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'text/plain; charset=x-bogus')
|
||||
test.write(Buffer.from('00000000', 'hex'))
|
||||
test.expect(415, 'unsupported charset "X-BOGUS"', done)
|
||||
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
|
||||
})
|
||||
})
|
||||
|
||||
describeAsyncHooks('async local storage', function () {
|
||||
before(function () {
|
||||
var app = express()
|
||||
var store = { foo: 'bar' }
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||
req.asyncLocalStorage.run(store, next)
|
||||
})
|
||||
|
||||
app.use(express.text())
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
var local = req.asyncLocalStorage.getStore()
|
||||
|
||||
if (local) {
|
||||
res.setHeader('x-store-foo', String(local.foo))
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
var local = req.asyncLocalStorage.getStore()
|
||||
|
||||
if (local) {
|
||||
res.setHeader('x-store-foo', String(local.foo))
|
||||
}
|
||||
|
||||
res.status(err.status || 500)
|
||||
res.send('[' + err.type + '] ' + err.message)
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
res.json(req.body)
|
||||
})
|
||||
|
||||
this.app = app
|
||||
})
|
||||
|
||||
it('should presist store', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'text/plain')
|
||||
.send('user is tobi')
|
||||
.expect(200)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.expect('"user is tobi"')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when unmatched content-type', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/fizzbuzz')
|
||||
.send('buzz')
|
||||
.expect(200)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.expect('{}')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when inflated', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))
|
||||
test.expect(200)
|
||||
test.expect('x-store-foo', 'bar')
|
||||
test.expect('"name is 论"')
|
||||
test.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when inflate error', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000', 'hex'))
|
||||
test.expect(400)
|
||||
test.expect('x-store-foo', 'bar')
|
||||
test.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when limit exceeded', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'text/plain')
|
||||
.send('user is ' + Buffer.alloc(1024 * 100, '.').toString())
|
||||
.expect(413)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.end(done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -365,7 +519,7 @@ describe('express.text()', function () {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Type', 'text/plain; charset=x-bogus')
|
||||
test.write(Buffer.from('00000000', 'hex'))
|
||||
test.expect(415, 'unsupported charset "X-BOGUS"', done)
|
||||
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -413,12 +567,12 @@ describe('express.text()', function () {
|
||||
test.expect(200, '"name is 论"', done)
|
||||
})
|
||||
|
||||
it('should fail on unknown encoding', function (done) {
|
||||
it('should 415 on unknown encoding', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'nulls')
|
||||
test.set('Content-Type', 'text/plain')
|
||||
test.write(Buffer.from('000000000000', 'hex'))
|
||||
test.expect(415, 'unsupported content encoding "nulls"', done)
|
||||
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -430,7 +584,9 @@ function createApp (options) {
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.status(err.status || 500)
|
||||
res.send(err.message)
|
||||
res.send(String(req.headers['x-error-property']
|
||||
? err[req.headers['x-error-property']]
|
||||
: ('[' + err.type + '] ' + err.message)))
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
@@ -439,3 +595,11 @@ function createApp (options) {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
function tryRequire (name) {
|
||||
try {
|
||||
return require(name)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
var asyncHooks = tryRequire('async_hooks')
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
|
||||
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||
? describe
|
||||
: describe.skip
|
||||
|
||||
describe('express.urlencoded()', function () {
|
||||
before(function () {
|
||||
this.app = createApp()
|
||||
@@ -56,6 +62,32 @@ describe('express.urlencoded()', function () {
|
||||
.expect(200, '{}', done)
|
||||
})
|
||||
|
||||
it('should 500 if stream not readable', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
req.on('end', next)
|
||||
req.resume()
|
||||
})
|
||||
|
||||
app.use(express.urlencoded())
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.status(err.status || 500)
|
||||
res.send('[' + err.type + '] ' + err.message)
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
res.json(req.body)
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send('user=tobi')
|
||||
.expect(500, '[stream.not.readable] stream is not readable', done)
|
||||
})
|
||||
|
||||
it('should handle duplicated middleware', function (done) {
|
||||
var app = express()
|
||||
|
||||
@@ -180,7 +212,7 @@ describe('express.urlencoded()', function () {
|
||||
it('should parse deep object', function (done) {
|
||||
var str = 'foo'
|
||||
|
||||
for (var i = 0; i < 500; i++) {
|
||||
for (var i = 0; i < 32; i++) {
|
||||
str += '[p]'
|
||||
}
|
||||
|
||||
@@ -198,7 +230,7 @@ describe('express.urlencoded()', function () {
|
||||
var depth = 0
|
||||
var ref = obj.foo
|
||||
while ((ref = ref.p)) { depth++ }
|
||||
assert.strictEqual(depth, 500)
|
||||
assert.strictEqual(depth, 32)
|
||||
})
|
||||
.expect(200, done)
|
||||
})
|
||||
@@ -216,7 +248,7 @@ describe('express.urlencoded()', function () {
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
||||
test.expect(415, 'content encoding unsupported', done)
|
||||
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -247,8 +279,8 @@ describe('express.urlencoded()', function () {
|
||||
})
|
||||
|
||||
it('should 413 when over limit with chunked encoding', function (done) {
|
||||
var buf = Buffer.alloc(1024, '.')
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var buf = Buffer.alloc(1024, '.')
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
test.set('Transfer-Encoding', 'chunked')
|
||||
@@ -257,6 +289,15 @@ describe('express.urlencoded()', function () {
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should 413 when inflated body over limit', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex'))
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should accept number of bytes', function (done) {
|
||||
var buf = Buffer.alloc(1024, '.')
|
||||
request(createApp({ limit: 1024 }))
|
||||
@@ -281,8 +322,8 @@ describe('express.urlencoded()', function () {
|
||||
})
|
||||
|
||||
it('should not hang response', function (done) {
|
||||
var buf = Buffer.alloc(10240, '.')
|
||||
var app = createApp({ limit: '8kb' })
|
||||
var buf = Buffer.alloc(10240, '.')
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
test.write(buf)
|
||||
@@ -290,6 +331,15 @@ describe('express.urlencoded()', function () {
|
||||
test.write(buf)
|
||||
test.expect(413, done)
|
||||
})
|
||||
|
||||
it('should not error when inflating', function (done) {
|
||||
var app = createApp({ limit: '1kb' })
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex'))
|
||||
test.expect(413, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with parameterLimit option', function () {
|
||||
@@ -309,16 +359,7 @@ describe('express.urlencoded()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send(createManyParams(11))
|
||||
.expect(413, /too many parameters/, done)
|
||||
})
|
||||
|
||||
it('should error with type = "parameters.too.many"', function (done) {
|
||||
request(createApp({ extended: false, parameterLimit: 10 }))
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send(createManyParams(11))
|
||||
.expect(413, 'parameters.too.many', done)
|
||||
.expect(413, '[parameters.too.many] too many parameters', done)
|
||||
})
|
||||
|
||||
it('should work when at the limit', function (done) {
|
||||
@@ -373,16 +414,7 @@ describe('express.urlencoded()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send(createManyParams(11))
|
||||
.expect(413, /too many parameters/, done)
|
||||
})
|
||||
|
||||
it('should error with type = "parameters.too.many"', function (done) {
|
||||
request(createApp({ extended: true, parameterLimit: 10 }))
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send(createManyParams(11))
|
||||
.expect(413, 'parameters.too.many', done)
|
||||
.expect(413, '[parameters.too.many] too many parameters', done)
|
||||
})
|
||||
|
||||
it('should work when at the limit', function (done) {
|
||||
@@ -525,65 +557,59 @@ describe('express.urlencoded()', function () {
|
||||
})
|
||||
|
||||
it('should error from verify', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send(' user=tobi')
|
||||
.expect(403, 'no leading space', done)
|
||||
})
|
||||
|
||||
it('should error with type = "entity.verify.failed"', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||
} })
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send(' user=tobi')
|
||||
.expect(403, 'entity.verify.failed', done)
|
||||
.expect(403, '[entity.verify.failed] no leading space', done)
|
||||
})
|
||||
|
||||
it('should allow custom codes', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x20) return
|
||||
var err = new Error('no leading space')
|
||||
err.status = 400
|
||||
throw err
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x20) return
|
||||
var err = new Error('no leading space')
|
||||
err.status = 400
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send(' user=tobi')
|
||||
.expect(400, 'no leading space', done)
|
||||
.expect(400, '[entity.verify.failed] no leading space', done)
|
||||
})
|
||||
|
||||
it('should allow custom type', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x20) return
|
||||
var err = new Error('no leading space')
|
||||
err.type = 'foo.bar'
|
||||
throw err
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] !== 0x20) return
|
||||
var err = new Error('no leading space')
|
||||
err.type = 'foo.bar'
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.set('X-Error-Property', 'type')
|
||||
.send(' user=tobi')
|
||||
.expect(403, 'foo.bar', done)
|
||||
.expect(403, '[foo.bar] no leading space', done)
|
||||
})
|
||||
|
||||
it('should allow pass-through', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||
}
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
@@ -593,14 +619,110 @@ describe('express.urlencoded()', function () {
|
||||
})
|
||||
|
||||
it('should 415 on unknown charset prior to verify', function (done) {
|
||||
var app = createApp({ verify: function (req, res, buf) {
|
||||
throw new Error('unexpected verify call')
|
||||
} })
|
||||
var app = createApp({
|
||||
verify: function (req, res, buf) {
|
||||
throw new Error('unexpected verify call')
|
||||
}
|
||||
})
|
||||
|
||||
var test = request(app).post('/')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus')
|
||||
test.write(Buffer.from('00000000', 'hex'))
|
||||
test.expect(415, 'unsupported charset "X-BOGUS"', done)
|
||||
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
|
||||
})
|
||||
})
|
||||
|
||||
describeAsyncHooks('async local storage', function () {
|
||||
before(function () {
|
||||
var app = express()
|
||||
var store = { foo: 'bar' }
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||
req.asyncLocalStorage.run(store, next)
|
||||
})
|
||||
|
||||
app.use(express.urlencoded())
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
var local = req.asyncLocalStorage.getStore()
|
||||
|
||||
if (local) {
|
||||
res.setHeader('x-store-foo', String(local.foo))
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
var local = req.asyncLocalStorage.getStore()
|
||||
|
||||
if (local) {
|
||||
res.setHeader('x-store-foo', String(local.foo))
|
||||
}
|
||||
|
||||
res.status(err.status || 500)
|
||||
res.send('[' + err.type + '] ' + err.message)
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
res.json(req.body)
|
||||
})
|
||||
|
||||
this.app = app
|
||||
})
|
||||
|
||||
it('should presist store', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send('user=tobi')
|
||||
.expect(200)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.expect('{"user":"tobi"}')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when unmatched content-type', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/fizzbuzz')
|
||||
.send('buzz')
|
||||
.expect(200)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.expect('{}')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when inflated', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
||||
test.expect(200)
|
||||
test.expect('x-store-foo', 'bar')
|
||||
test.expect('{"name":"论"}')
|
||||
test.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when inflate error', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'gzip')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))
|
||||
test.expect(400)
|
||||
test.expect('x-store-foo', 'bar')
|
||||
test.end(done)
|
||||
})
|
||||
|
||||
it('should presist store when limit exceeded', function (done) {
|
||||
request(this.app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send('user=' + Buffer.alloc(1024 * 100, '.').toString())
|
||||
.expect(413)
|
||||
.expect('x-store-foo', 'bar')
|
||||
.end(done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -635,7 +757,7 @@ describe('express.urlencoded()', function () {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r')
|
||||
test.write(Buffer.from('6e616d653dcec5d4', 'hex'))
|
||||
test.expect(415, 'unsupported charset "KOI8-R"', done)
|
||||
test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -683,12 +805,12 @@ describe('express.urlencoded()', function () {
|
||||
test.expect(200, '{"name":"论"}', done)
|
||||
})
|
||||
|
||||
it('should fail on unknown encoding', function (done) {
|
||||
it('should 415 on unknown encoding', function (done) {
|
||||
var test = request(this.app).post('/')
|
||||
test.set('Content-Encoding', 'nulls')
|
||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
test.write(Buffer.from('000000000000', 'hex'))
|
||||
test.expect(415, 'unsupported content encoding "nulls"', done)
|
||||
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -717,7 +839,9 @@ function createApp (options) {
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.status(err.status || 500)
|
||||
res.send(String(err[req.headers['x-error-property'] || 'message']))
|
||||
res.send(String(req.headers['x-error-property']
|
||||
? err[req.headers['x-error-property']]
|
||||
: ('[' + err.type + '] ' + err.message)))
|
||||
})
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
@@ -732,3 +856,11 @@ function expectKeyCount (count) {
|
||||
assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count)
|
||||
}
|
||||
}
|
||||
|
||||
function tryRequire (name) {
|
||||
try {
|
||||
return require(name)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user