mirror of
https://github.com/expressjs/express.git
synced 2026-02-28 03:29:26 +00:00
Compare commits
133 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 |
295
.github/workflows/ci.yml
vendored
295
.github/workflows/ci.yml
vendored
@@ -1,179 +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
|
||||
- Node.js 15.x
|
||||
- Node.js 16.x
|
||||
- Node.js 17.x
|
||||
- Node.js 18.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 nyc@10.3.2 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 nyc@10.3.2 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 nyc@10.3.2 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 nyc@10.3.2 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 nyc@10.3.2 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 nyc@11.9.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 nyc@11.9.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 nyc@14.1.1 supertest@3.4.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 nyc@14.1.1 supertest@6.1.6
|
||||
- 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"
|
||||
npm-i: mocha@9.2.2
|
||||
|
||||
- name: Node.js 13.x
|
||||
node-version: "13.14"
|
||||
npm-i: mocha@9.2.2
|
||||
|
||||
- name: Node.js 14.x
|
||||
node-version: "14.20"
|
||||
|
||||
- name: Node.js 15.x
|
||||
node-version: "15.14"
|
||||
|
||||
- name: Node.js 16.x
|
||||
node-version: "16.17"
|
||||
|
||||
- name: Node.js 17.x
|
||||
node-version: "17.9"
|
||||
|
||||
- name: Node.js 18.x
|
||||
node-version: "18.10"
|
||||
- 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 < 12
|
||||
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then
|
||||
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
|
||||
grep -E '^eslint(-|$)' | \
|
||||
sort -r | \
|
||||
xargs -n1 npm rm --silent --save-dev
|
||||
fi
|
||||
- name: Install 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 ||:
|
||||
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print "::set-output name=" $2 "::" $3 }'
|
||||
- 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
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
# npm
|
||||
node_modules
|
||||
package-lock.json
|
||||
npm-shrinkwrap.json
|
||||
*.log
|
||||
*.gz
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
90
History.md
90
History.md
@@ -1,3 +1,83 @@
|
||||
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
|
||||
===================
|
||||
|
||||
@@ -2111,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
|
||||
@@ -3322,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
|
||||
|
||||
@@ -3358,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
|
||||
@@ -3397,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)
|
||||
|
||||
108
Readme.md
108
Readme.md
@@ -1,10 +1,29 @@
|
||||
[](http://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [Node.js](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-version-image]][npm-url]
|
||||
[![NPM Install Size][npm-install-size-image]][npm-install-size-url]
|
||||
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
|
||||
|
||||
```js
|
||||
const express = require('express')
|
||||
@@ -104,7 +123,7 @@ $ npm start
|
||||
To view the examples, clone the Express repo and install the dependencies:
|
||||
|
||||
```console
|
||||
$ git clone git://github.com/expressjs/express.git --depth 1
|
||||
$ git clone https://github.com/expressjs/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
```
|
||||
@@ -144,10 +163,82 @@ $ npm test
|
||||
|
||||
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)
|
||||
@@ -164,3 +255,6 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d
|
||||
[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,
|
||||
|
||||
10
Security.md
10
Security.md
@@ -29,6 +29,12 @@ announcement, and may ask for additional information or guidance.
|
||||
Report security bugs in third-party modules to the person or team maintaining
|
||||
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
|
||||
|
||||
When the security team receives a security bug report, they will assign it to a
|
||||
@@ -40,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.
|
||||
|
||||
|
||||
18
appveyor.yml
18
appveyor.yml
@@ -17,9 +17,13 @@ environment:
|
||||
- nodejs_version: "13.14"
|
||||
- nodejs_version: "14.20"
|
||||
- nodejs_version: "15.14"
|
||||
- nodejs_version: "16.17"
|
||||
- nodejs_version: "16.20"
|
||||
- nodejs_version: "17.9"
|
||||
- nodejs_version: "18.10"
|
||||
- nodejs_version: "18.19"
|
||||
- nodejs_version: "19.9"
|
||||
- nodejs_version: "20.11"
|
||||
- nodejs_version: "21.6"
|
||||
- nodejs_version: "22.0"
|
||||
cache:
|
||||
- node_modules
|
||||
install:
|
||||
@@ -30,7 +34,11 @@ install:
|
||||
# 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
|
||||
@@ -65,12 +73,12 @@ install:
|
||||
# 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 < 8
|
||||
# - 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 8) {
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
|
||||
npm install --silent --save-dev nyc@14.1.1
|
||||
}
|
||||
- ps: |
|
||||
|
||||
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,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
|
||||
|
||||
@@ -13,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) {
|
||||
|
||||
@@ -26,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!');
|
||||
});
|
||||
|
||||
@@ -26,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,62 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
@@ -32,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'));
|
||||
|
||||
@@ -61,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,
|
||||
@@ -99,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
|
||||
|
||||
@@ -72,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);
|
||||
});
|
||||
|
||||
|
||||
@@ -822,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);
|
||||
@@ -904,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));
|
||||
};
|
||||
|
||||
@@ -960,7 +970,7 @@ res.redirect = function redirect(url) {
|
||||
|
||||
html: function(){
|
||||
var u = escapeHtml(address);
|
||||
body = '<p>' + statuses.message[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
|
||||
body = '<p>' + statuses.message[status] + '. Redirecting to ' + u + '</p>'
|
||||
},
|
||||
|
||||
default: function(){
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -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';
|
||||
@@ -103,8 +106,10 @@ Route.prototype.dispatch = function dispatch(req, res, done) {
|
||||
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';
|
||||
}
|
||||
|
||||
@@ -117,17 +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
|
||||
* @param {Number} index
|
||||
* @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(/ *= */);
|
||||
@@ -282,6 +280,7 @@ function createETagGenerator (options) {
|
||||
/**
|
||||
* Parse an extended query string with qs.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
|
||||
51
package.json
51
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Fast, unopinionated, minimalist web framework",
|
||||
"version": "4.18.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",
|
||||
@@ -30,32 +34,32 @@
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"content-disposition": "0.5.4",
|
||||
"body-parser": "~1.20.4",
|
||||
"content-disposition": "~0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"cookie": "~0.7.1",
|
||||
"cookie-signature": "~1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"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.4.1",
|
||||
"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.11.0",
|
||||
"qs": "~6.14.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.18.0",
|
||||
"serve-static": "1.15.0",
|
||||
"send": "~0.19.0",
|
||||
"serve-static": "~1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"statuses": "~2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
@@ -65,18 +69,17 @@
|
||||
"connect-redis": "3.4.2",
|
||||
"cookie-parser": "1.4.6",
|
||||
"cookie-session": "2.0.0",
|
||||
"ejs": "3.1.8",
|
||||
"eslint": "8.24.0",
|
||||
"ejs": "3.1.9",
|
||||
"eslint": "8.47.0",
|
||||
"express-session": "1.17.2",
|
||||
"hbs": "4.2.0",
|
||||
"marked": "0.7.0",
|
||||
"method-override": "3.0.0",
|
||||
"mocha": "10.0.0",
|
||||
"mocha": "^6.2.2",
|
||||
"morgan": "1.10.0",
|
||||
"multiparty": "4.2.3",
|
||||
"nyc": "15.1.0",
|
||||
"nyc": "^14.1.1",
|
||||
"pbkdf2-password": "1.2.1",
|
||||
"supertest": "6.3.0",
|
||||
"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": "nyc --reporter=lcovonly --reporter=text npm test",
|
||||
"test-cov": "nyc --reporter=html --reporter=text npm test",
|
||||
"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/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,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!');
|
||||
})
|
||||
|
||||
@@ -198,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');
|
||||
});
|
||||
|
||||
@@ -224,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!');
|
||||
});
|
||||
|
||||
|
||||
@@ -61,6 +61,33 @@ 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
|
||||
|
||||
@@ -201,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');
|
||||
});
|
||||
|
||||
@@ -579,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();
|
||||
@@ -599,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()
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,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)
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
@@ -166,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);
|
||||
});
|
||||
|
||||
@@ -187,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);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,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();
|
||||
@@ -39,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){
|
||||
@@ -50,6 +55,17 @@ 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();
|
||||
assert.throws(app[method].bind(app, '/', 3), /Number/)
|
||||
@@ -90,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);
|
||||
});
|
||||
|
||||
@@ -102,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);
|
||||
});
|
||||
|
||||
@@ -114,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);
|
||||
});
|
||||
|
||||
@@ -126,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);
|
||||
});
|
||||
|
||||
@@ -188,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 = []
|
||||
@@ -896,7 +929,7 @@ describe('app.router', function(){
|
||||
|
||||
request(app)
|
||||
.get('/foo.json')
|
||||
.expect(200, 'foo as json', done)
|
||||
.expect(200, 'foo as json', cb)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -910,7 +943,7 @@ describe('app.router', function(){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/bar', function(req, res){
|
||||
app.get('/bar', function () {
|
||||
assert(0);
|
||||
});
|
||||
|
||||
@@ -919,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)
|
||||
});
|
||||
@@ -939,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')
|
||||
});
|
||||
|
||||
@@ -964,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')
|
||||
})
|
||||
|
||||
@@ -995,7 +1028,7 @@ describe('app.router', function(){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/bar', function(req, res){
|
||||
app.get('/bar', function () {
|
||||
assert(0);
|
||||
});
|
||||
|
||||
@@ -1004,7 +1037,7 @@ describe('app.router', function(){
|
||||
next(new Error('fail'));
|
||||
});
|
||||
|
||||
app.get('/foo', function(req, res, next){
|
||||
app.get('/foo', function () {
|
||||
assert(0);
|
||||
});
|
||||
|
||||
@@ -1109,3 +1142,12 @@ describe('app.router', function(){
|
||||
assert.strictEqual(app.get('/', function () {}), app)
|
||||
})
|
||||
})
|
||||
|
||||
function supportsRegexp(source) {
|
||||
try {
|
||||
new RegExp(source)
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('app', function(){
|
||||
|
||||
request(app)
|
||||
.get('/forum')
|
||||
.expect(200, 'forum', done)
|
||||
.expect(200, 'forum', cb)
|
||||
})
|
||||
|
||||
it('should set the child\'s .parent', function(){
|
||||
|
||||
@@ -262,7 +262,7 @@ describe('express.json()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('true')
|
||||
.expect(400, '[entity.parse.failed] ' + parseError('#rue').replace('#', 't'), done)
|
||||
.expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -290,7 +290,7 @@ describe('express.json()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('true')
|
||||
.expect(400, '[entity.parse.failed] ' + 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) {
|
||||
@@ -298,7 +298,7 @@ describe('express.json()', function () {
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(' true')
|
||||
.expect(400, '[entity.parse.failed] ' + 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) {
|
||||
@@ -316,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)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -486,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) {
|
||||
@@ -508,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) {
|
||||
|
||||
@@ -212,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]'
|
||||
}
|
||||
|
||||
@@ -230,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)
|
||||
})
|
||||
|
||||
@@ -32,5 +32,37 @@ describe('res', function(){
|
||||
.expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT')
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should set expires when passed', function(done) {
|
||||
var expiresAt = new Date()
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.clearCookie('sid', { expires: expiresAt }).end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Set-Cookie', 'sid=; Path=/; Expires=' + expiresAt.toUTCString() )
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should set both maxAge and expires when passed', function(done) {
|
||||
var maxAgeInMs = 10000
|
||||
var expiresAt = new Date()
|
||||
var expectedExpires = new Date(expiresAt.getTime() + maxAgeInMs)
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.clearCookie('sid', { expires: expiresAt, maxAge: maxAgeInMs }).end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
// yes, this is the behavior. When we set a max-age, we also set expires to a date 10 sec ahead of expires
|
||||
// even if we set max-age only, we will also set an expires 10 sec in the future
|
||||
.expect('Set-Cookie', 'sid=; Max-Age=10; Path=/; Expires=' + expectedExpires.toUTCString())
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -82,6 +82,22 @@ describe('res', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('partitioned', function () {
|
||||
it('should set partitioned', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.cookie('name', 'tobi', { partitioned: true });
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Set-Cookie', 'name=tobi; Path=/; Partitioned')
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('maxAge', function(){
|
||||
it('should set relative expires', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -61,7 +61,7 @@ app3.use(function(req, res, next){
|
||||
|
||||
var app4 = express();
|
||||
|
||||
app4.get('/', function(req, res, next){
|
||||
app4.get('/', function (req, res) {
|
||||
res.format({
|
||||
text: function(){ res.send('hey') },
|
||||
html: function(){ res.send('<p>hey</p>') },
|
||||
@@ -155,7 +155,7 @@ describe('res', function(){
|
||||
var app = express();
|
||||
var router = express.Router();
|
||||
|
||||
router.get('/', function(req, res, next){
|
||||
router.get('/', function (req, res) {
|
||||
res.format({
|
||||
text: function(){ res.send('hey') },
|
||||
html: function(){ res.send('<p>hey</p>') },
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
, request = require('supertest')
|
||||
, assert = require('assert')
|
||||
, url = require('url');
|
||||
|
||||
describe('res', function(){
|
||||
describe('.location(url)', function(){
|
||||
it('should set the header', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.location('http://google.com/').end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', 'http://google.com/')
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should preserve trailing slashes when not present', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.location('http://google.com').end();
|
||||
});
|
||||
@@ -31,19 +46,6 @@ describe('res', function(){
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should not touch already-encoded sequences in "url"', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.location('https://google.com?q=%A710').end()
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', 'https://google.com?q=%A710')
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
describe('when url is "back"', function () {
|
||||
it('should set location from "Referer" header', function (done) {
|
||||
var app = express()
|
||||
@@ -101,5 +103,260 @@ describe('res', function(){
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
it('should encode data uri', function (done) {
|
||||
var app = express()
|
||||
app.use(function (req, res) {
|
||||
res.location('data:text/javascript,export default () => { }').end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should consistently handle non-string input: boolean', function (done) {
|
||||
var app = express()
|
||||
app.use(function (req, res) {
|
||||
res.location(true).end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', 'true')
|
||||
.expect(200, done)
|
||||
});
|
||||
|
||||
it('should consistently handle non-string inputs: object', function (done) {
|
||||
var app = express()
|
||||
app.use(function (req, res) {
|
||||
res.location({}).end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', '[object%20Object]')
|
||||
.expect(200, done)
|
||||
});
|
||||
|
||||
it('should consistently handle non-string inputs: array', function (done) {
|
||||
var app = express()
|
||||
app.use(function (req, res) {
|
||||
res.location([]).end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', '')
|
||||
.expect(200, done)
|
||||
});
|
||||
|
||||
it('should consistently handle empty string input', function (done) {
|
||||
var app = express()
|
||||
app.use(function (req, res) {
|
||||
res.location('').end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', '')
|
||||
.expect(200, done)
|
||||
});
|
||||
|
||||
|
||||
if (typeof URL !== 'undefined') {
|
||||
it('should accept an instance of URL', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.location(new URL('http://google.com/')).end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', 'http://google.com/')
|
||||
.expect(200, done);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
describe('location header encoding', function() {
|
||||
function createRedirectServerForDomain (domain) {
|
||||
var app = express();
|
||||
app.use(function (req, res) {
|
||||
var host = url.parse(req.query.q, false, true).host;
|
||||
// This is here to show a basic check one might do which
|
||||
// would pass but then the location header would still be bad
|
||||
if (host !== domain) {
|
||||
res.status(400).end('Bad host: ' + host + ' !== ' + domain);
|
||||
}
|
||||
res.location(req.query.q).end();
|
||||
});
|
||||
return app;
|
||||
}
|
||||
|
||||
function testRequestedRedirect (app, inputUrl, expected, expectedHost, done) {
|
||||
return request(app)
|
||||
// Encode uri because old supertest does not and is required
|
||||
// to test older node versions. New supertest doesn't re-encode
|
||||
// so this works in both.
|
||||
.get('/?q=' + encodeURIComponent(inputUrl))
|
||||
.expect('') // No body.
|
||||
.expect(200)
|
||||
.expect('Location', expected)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
console.log('headers:', res.headers)
|
||||
console.error('error', res.error, err);
|
||||
return done(err, res);
|
||||
}
|
||||
|
||||
// Parse the hosts from the input URL and the Location header
|
||||
var inputHost = url.parse(inputUrl, false, true).host;
|
||||
var locationHost = url.parse(res.headers['location'], false, true).host;
|
||||
|
||||
assert.strictEqual(locationHost, expectedHost);
|
||||
|
||||
// Assert that the hosts are the same
|
||||
if (inputHost !== locationHost) {
|
||||
return done(new Error('Hosts do not match: ' + inputHost + " !== " + locationHost));
|
||||
}
|
||||
|
||||
return done(null, res);
|
||||
});
|
||||
}
|
||||
|
||||
it('should not touch already-encoded sequences in "url"', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'https://google.com?q=%A710',
|
||||
'https://google.com?q=%A710',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should consistently handle relative urls', function (done) {
|
||||
var app = createRedirectServerForDomain(null);
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'/foo/bar',
|
||||
'/foo/bar',
|
||||
null,
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should not encode urls in such a way that they can bypass redirect allow lists', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'http://google.com\\@apple.com',
|
||||
'http://google.com\\@apple.com',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should not be case sensitive', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'HTTP://google.com\\@apple.com',
|
||||
'HTTP://google.com\\@apple.com',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should work with https', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'https://google.com\\@apple.com',
|
||||
'https://google.com\\@apple.com',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should correctly encode schemaless paths', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'//google.com\\@apple.com/',
|
||||
'//google.com\\@apple.com/',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should keep backslashes in the path', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'https://google.com/foo\\bar\\baz',
|
||||
'https://google.com/foo\\bar\\baz',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should escape header splitting for old node versions', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'http://google.com\\@apple.com/%0d%0afoo:%20bar',
|
||||
'http://google.com\\@apple.com/%0d%0afoo:%20bar',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should encode unicode correctly', function (done) {
|
||||
var app = createRedirectServerForDomain(null);
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'/%e2%98%83',
|
||||
'/%e2%98%83',
|
||||
null,
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should encode unicode correctly even with a bad host', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'http://google.com\\@apple.com/%e2%98%83',
|
||||
'http://google.com\\@apple.com/%e2%98%83',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should work correctly despite using deprecated url.parse', function (done) {
|
||||
var app = createRedirectServerForDomain('google.com');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'https://google.com\'.bb.com/1.html',
|
||||
'https://google.com\'.bb.com/1.html',
|
||||
'google.com',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should encode file uri path', function (done) {
|
||||
var app = createRedirectServerForDomain('');
|
||||
testRequestedRedirect(
|
||||
app,
|
||||
'file:///etc\\passwd',
|
||||
'file:///etc\\passwd',
|
||||
'',
|
||||
done
|
||||
);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -106,7 +106,7 @@ describe('res', function(){
|
||||
.set('Accept', 'text/html')
|
||||
.expect('Content-Type', /html/)
|
||||
.expect('Location', 'http://google.com')
|
||||
.expect(302, '<p>Found. Redirecting to <a href="http://google.com">http://google.com</a></p>', done)
|
||||
.expect(302, '<p>Found. Redirecting to http://google.com</p>', done)
|
||||
})
|
||||
|
||||
it('should escape the url', function(done){
|
||||
@@ -122,9 +122,27 @@ describe('res', function(){
|
||||
.set('Accept', 'text/html')
|
||||
.expect('Content-Type', /html/)
|
||||
.expect('Location', '%3Cla\'me%3E')
|
||||
.expect(302, '<p>Found. Redirecting to <a href="%3Cla'me%3E">%3Cla'me%3E</a></p>', done)
|
||||
.expect(302, '<p>Found. Redirecting to %3Cla'me%3E</p>', done)
|
||||
})
|
||||
|
||||
it('should not render evil javascript links in anchor href (prevent XSS)', function(done){
|
||||
var app = express();
|
||||
var xss = 'javascript:eval(document.body.innerHTML=`<p>XSS</p>`);';
|
||||
var encodedXss = 'javascript:eval(document.body.innerHTML=%60%3Cp%3EXSS%3C/p%3E%60);';
|
||||
|
||||
app.use(function(req, res){
|
||||
res.redirect(xss);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'http://example.com')
|
||||
.set('Accept', 'text/html')
|
||||
.expect('Content-Type', /html/)
|
||||
.expect('Location', encodedXss)
|
||||
.expect(302, '<p>Found. Redirecting to ' + encodedXss +'</p>', done);
|
||||
});
|
||||
|
||||
it('should include the redirect type', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -137,7 +155,7 @@ describe('res', function(){
|
||||
.set('Accept', 'text/html')
|
||||
.expect('Content-Type', /html/)
|
||||
.expect('Location', 'http://google.com')
|
||||
.expect(301, '<p>Moved Permanently. Redirecting to <a href="http://google.com">http://google.com</a></p>', done);
|
||||
.expect(301, '<p>Moved Permanently. Redirecting to http://google.com</p>', done);
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ var methods = require('methods');
|
||||
var request = require('supertest');
|
||||
var utils = require('./support/utils');
|
||||
|
||||
var shouldSkipQuery = require('./support/utils').shouldSkipQuery
|
||||
|
||||
describe('res', function(){
|
||||
describe('.send()', function(){
|
||||
it('should set body to ""', function(done){
|
||||
@@ -409,6 +411,9 @@ describe('res', function(){
|
||||
if (method === 'connect') return;
|
||||
|
||||
it('should send ETag in response to ' + method.toUpperCase() + ' request', function (done) {
|
||||
if (method === 'query' && shouldSkipQuery(process.versions.node)) {
|
||||
this.skip()
|
||||
}
|
||||
var app = express();
|
||||
|
||||
app[method]('/', function (req, res) {
|
||||
|
||||
@@ -16,6 +16,7 @@ exports.shouldHaveBody = shouldHaveBody
|
||||
exports.shouldHaveHeader = shouldHaveHeader
|
||||
exports.shouldNotHaveBody = shouldNotHaveBody
|
||||
exports.shouldNotHaveHeader = shouldNotHaveHeader;
|
||||
exports.shouldSkipQuery = shouldSkipQuery
|
||||
|
||||
/**
|
||||
* Assert that a supertest response has a specific body.
|
||||
@@ -70,3 +71,16 @@ function shouldNotHaveHeader(header) {
|
||||
assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header);
|
||||
};
|
||||
}
|
||||
|
||||
function getMajorVersion(versionString) {
|
||||
return versionString.split('.')[0];
|
||||
}
|
||||
|
||||
function shouldSkipQuery(versionString) {
|
||||
// Skipping HTTP QUERY tests below Node 22, QUERY wasn't fully supported by Node until 22
|
||||
// we could update this implementation to run on supported versions of 21 once they exist
|
||||
// upstream tracking https://github.com/nodejs/node/issues/51562
|
||||
// express tracking issue: https://github.com/expressjs/express/issues/5615
|
||||
return Number(getMajorVersion(versionString)) < 22
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user