mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
405 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ea537d907d | ||
|
|
eee93a2760 | ||
|
|
b35773cf19 | ||
|
|
c8a42006b8 | ||
|
|
21cf522dcd | ||
|
|
a24f27aba7 | ||
|
|
a33266a206 | ||
|
|
6fe271e8aa | ||
|
|
cbe25d66b3 | ||
|
|
3bb6d96ba9 | ||
|
|
6660649f1b | ||
|
|
a75e4707b9 | ||
|
|
db05a741f0 | ||
|
|
c2e23ece2e | ||
|
|
96850e872a | ||
|
|
b8d59d5c98 | ||
|
|
59d695c447 | ||
|
|
e242796eb3 | ||
|
|
aaa9690bcf | ||
|
|
f275e87dff | ||
|
|
9dd0e7afdb | ||
|
|
1b2f3a0698 | ||
|
|
519126d732 | ||
|
|
99a369f3d5 | ||
|
|
a1dbb11377 | ||
|
|
353348a83e | ||
|
|
dab6ee5822 | ||
|
|
fc138c108f | ||
|
|
61a23e801f | ||
|
|
313d54f033 | ||
|
|
de081eb70f | ||
|
|
06d11755c9 | ||
|
|
6f2afd3d80 | ||
|
|
d5a1cbee70 | ||
|
|
f9a0560a9c | ||
|
|
821b7f0624 | ||
|
|
f490f78563 | ||
|
|
884e080a19 | ||
|
|
eb76236e2f | ||
|
|
52e9bd67b7 | ||
|
|
135a05c524 | ||
|
|
30afebf8da | ||
|
|
8e4add7f74 | ||
|
|
0fbbc29632 | ||
|
|
2402126988 | ||
|
|
ca3c863428 | ||
|
|
28db2c2c5c | ||
|
|
685d4665fd | ||
|
|
280a8d39ec | ||
|
|
5c4f3e7cc7 | ||
|
|
de122c14f5 | ||
|
|
2a2dd5d32b | ||
|
|
508936853a | ||
|
|
5596222f6a | ||
|
|
fe67523b9c | ||
|
|
4486fa6324 | ||
|
|
ecd8a08c1c | ||
|
|
a75728432e | ||
|
|
9007dcbdba | ||
|
|
c519886be5 | ||
|
|
3ff1dbeb73 | ||
|
|
18da651c5b | ||
|
|
1b48a5cc3c | ||
|
|
561b4b601e | ||
|
|
67e64ca4c1 | ||
|
|
922e9a4615 | ||
|
|
323a38965a | ||
|
|
3f1dcb96e0 | ||
|
|
4b4fa26298 | ||
|
|
47c1d2a816 | ||
|
|
65aff94ec6 | ||
|
|
2d519077ea | ||
|
|
d967675852 | ||
|
|
22d5b7ed10 | ||
|
|
872aa4741c | ||
|
|
87bc4ef763 | ||
|
|
f0cbdeadf6 | ||
|
|
f1e8a877f4 | ||
|
|
55831bbd08 | ||
|
|
e757fa0039 | ||
|
|
95735a6fcc | ||
|
|
668d029a14 | ||
|
|
866ffd67d7 | ||
|
|
741e3f81af | ||
|
|
4efb49866d | ||
|
|
6506fb578c | ||
|
|
dfa7b80642 | ||
|
|
e1b45ebd05 | ||
|
|
0a48e18056 | ||
|
|
eed05a1464 | ||
|
|
10c7756764 | ||
|
|
9dadca2c64 | ||
|
|
b8e50568af | ||
|
|
94e48a16f2 | ||
|
|
efcb17dcb2 | ||
|
|
b9ecb9afe3 | ||
|
|
5266f3a5cb | ||
|
|
e502dde3c8 | ||
|
|
da6f701317 | ||
|
|
88f9733ffa | ||
|
|
8267c4b724 | ||
|
|
bc07a41693 | ||
|
|
c754c8ad7b | ||
|
|
e917028729 | ||
|
|
7b076bd8e1 | ||
|
|
bb5211fa1c | ||
|
|
7f4e37f3ea | ||
|
|
11192bd168 | ||
|
|
0bcdd88dd0 | ||
|
|
60aacac167 | ||
|
|
70a19472f1 | ||
|
|
6f7a8301a1 | ||
|
|
8b71f39516 | ||
|
|
955f2a5f78 | ||
|
|
2f782d8478 | ||
|
|
32f5293afa | ||
|
|
6d9dd2da49 | ||
|
|
40dbfa2de2 | ||
|
|
9afa1cfc85 | ||
|
|
7eacdcef19 | ||
|
|
b02d3a1744 | ||
|
|
03341204ff | ||
|
|
50eb5e4377 | ||
|
|
952484f73a | ||
|
|
4218d04183 | ||
|
|
cf5c813d2f | ||
|
|
9e5d1a30c3 | ||
|
|
8a97346eaf | ||
|
|
6eda52a3dc | ||
|
|
b9b1b19758 | ||
|
|
6f12eee8ab | ||
|
|
186a206a0a | ||
|
|
02f3933b69 | ||
|
|
0ae10bb154 | ||
|
|
95c31f7041 | ||
|
|
b93ffd4bdc | ||
|
|
8da51108e7 | ||
|
|
6bcdfef6ad | ||
|
|
44e539e1dc | ||
|
|
003459b795 | ||
|
|
6295b45920 | ||
|
|
a6b119d27a | ||
|
|
d0421ac7e1 | ||
|
|
dc538f6e81 | ||
|
|
62a59b6ace | ||
|
|
451ee5d9c1 | ||
|
|
f07f368fba | ||
|
|
09d5654488 | ||
|
|
3d10279826 | ||
|
|
5e9de5dcb6 | ||
|
|
5de1a08ebf | ||
|
|
4480fb997e | ||
|
|
b8fb6a7fb1 | ||
|
|
b4eb1f59d3 | ||
|
|
431f65305e | ||
|
|
f3fa758af9 | ||
|
|
ede24da964 | ||
|
|
d5b33cfad8 | ||
|
|
c39d7d9339 | ||
|
|
f95dbc28fd | ||
|
|
ac89f6f121 | ||
|
|
3d8ca8ad4a | ||
|
|
02c753583e | ||
|
|
3ed5090ca9 | ||
|
|
76bf96e9ce | ||
|
|
d3bdc3b663 | ||
|
|
0e88dceac2 | ||
|
|
e69a29d9c2 | ||
|
|
0083372bed | ||
|
|
f3c5f7ee29 | ||
|
|
40e04ec7a6 | ||
|
|
972ada9079 | ||
|
|
80e64691e1 | ||
|
|
98b0b66b6c | ||
|
|
cbaa04629a | ||
|
|
276a80895c | ||
|
|
94a6cbfbfe | ||
|
|
f8fba68ec0 | ||
|
|
c6f12a8971 | ||
|
|
323572610b | ||
|
|
f448a96685 | ||
|
|
68e824cbff | ||
|
|
086e56f1c4 | ||
|
|
b4020ec92b | ||
|
|
fe0bc4082d | ||
|
|
1780ed1091 | ||
|
|
b49af6a674 | ||
|
|
a743d5be4d | ||
|
|
187d1f57c9 | ||
|
|
950f4423f0 | ||
|
|
53bee2506d | ||
|
|
351396f971 | ||
|
|
b97faff6e2 | ||
|
|
b7817ab1b0 | ||
|
|
48aba21ea4 | ||
|
|
de129c289d | ||
|
|
e3f7f51f5f | ||
|
|
6f823e409c | ||
|
|
6d9b13cced | ||
|
|
f974d22c66 | ||
|
|
8d4ceb623d | ||
|
|
c0136d8b48 | ||
|
|
86f5df00ed | ||
|
|
41964580a8 | ||
|
|
ddeb71301c | ||
|
|
7154014785 | ||
|
|
628438d8d8 | ||
|
|
a24fd0ca6c | ||
|
|
95fb5cc268 | ||
|
|
44591fee23 | ||
|
|
2df1ad26a5 | ||
|
|
12c3712468 | ||
|
|
fa272edf84 | ||
|
|
d9d09b8b90 | ||
|
|
02a9d5fb28 | ||
|
|
c2f4fb5356 | ||
|
|
673d51f4f0 | ||
|
|
5cc761c865 | ||
|
|
ad7d96db47 | ||
|
|
e62bb8bf9f | ||
|
|
70589c3aef | ||
|
|
9a99c15270 | ||
|
|
550043c217 | ||
|
|
48940e6120 | ||
|
|
80f1ea9bec | ||
|
|
c3fb7e5adc | ||
|
|
94fdb674b1 | ||
|
|
ea3d605652 | ||
|
|
40435ec997 | ||
|
|
7137bf567d | ||
|
|
bd1672f0a4 | ||
|
|
9395db4c22 | ||
|
|
19a2eeb476 | ||
|
|
d7da22550d | ||
|
|
961dbff904 | ||
|
|
9e0fa7f1ca | ||
|
|
9e067ad2cb | ||
|
|
de5fb62b1a | ||
|
|
b208b24f83 | ||
|
|
78e55108e4 | ||
|
|
48817a798f |
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"rules": {
|
||||
"eol-last": "error",
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"no-trailing-spaces": "error",
|
||||
"no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }]
|
||||
}
|
||||
}
|
||||
8
.eslintrc.yml
Normal file
8
.eslintrc.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
root: true
|
||||
|
||||
rules:
|
||||
eol-last: error
|
||||
eqeqeq: [error, allow-null]
|
||||
indent: [error, 2, { MemberExpression: "off", SwitchCase: 1 }]
|
||||
no-trailing-spaces: error
|
||||
no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }]
|
||||
218
.github/workflows/ci.yml
vendored
Normal file
218
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
- pull_request
|
||||
- push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
- Node.js 19.x
|
||||
- Node.js 20.x
|
||||
- Node.js 21.x
|
||||
|
||||
include:
|
||||
- name: Node.js 0.10
|
||||
node-version: "0.10"
|
||||
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
|
||||
|
||||
- name: Node.js 0.12
|
||||
node-version: "0.12"
|
||||
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
|
||||
|
||||
- name: io.js 1.x
|
||||
node-version: "1.8"
|
||||
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
|
||||
|
||||
- name: io.js 2.x
|
||||
node-version: "2.5"
|
||||
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
|
||||
|
||||
- name: io.js 3.x
|
||||
node-version: "3.3"
|
||||
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
|
||||
|
||||
- name: Node.js 4.x
|
||||
node-version: "4.9"
|
||||
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
|
||||
|
||||
- name: Node.js 5.x
|
||||
node-version: "5.12"
|
||||
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
|
||||
|
||||
- name: Node.js 6.x
|
||||
node-version: "6.17"
|
||||
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2
|
||||
|
||||
- name: Node.js 7.x
|
||||
node-version: "7.10"
|
||||
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6
|
||||
|
||||
- name: Node.js 8.x
|
||||
node-version: "8.17"
|
||||
npm-i: mocha@7.2.0 nyc@14.1.1
|
||||
|
||||
- name: Node.js 9.x
|
||||
node-version: "9.11"
|
||||
npm-i: mocha@7.2.0 nyc@14.1.1
|
||||
|
||||
- name: Node.js 10.x
|
||||
node-version: "10.24"
|
||||
npm-i: mocha@8.4.0
|
||||
|
||||
- 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.20"
|
||||
|
||||
- name: Node.js 17.x
|
||||
node-version: "17.9"
|
||||
|
||||
- name: Node.js 18.x
|
||||
node-version: "18.19"
|
||||
|
||||
- name: Node.js 19.x
|
||||
node-version: "19.9"
|
||||
|
||||
- name: Node.js 20.x
|
||||
node-version: "20.11"
|
||||
|
||||
- name: Node.js 21.x
|
||||
node-version: "21.6"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js ${{ matrix.node-version }}
|
||||
shell: bash -eo pipefail -l {0}
|
||||
run: |
|
||||
nvm install --default ${{ matrix.node-version }}
|
||||
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Configure npm
|
||||
run: |
|
||||
npm config set loglevel error
|
||||
if [[ "$(npm config get package-lock)" == "true" ]]; then
|
||||
npm config set package-lock false
|
||||
else
|
||||
npm config set shrinkwrap false
|
||||
fi
|
||||
|
||||
- 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: Setup Node.js version-specific dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
# eslint for linting
|
||||
# - remove on Node.js < 12
|
||||
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then
|
||||
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
|
||||
grep -E '^eslint(-|$)' | \
|
||||
sort -r | \
|
||||
xargs -n1 npm rm --silent --save-dev
|
||||
fi
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: npm install
|
||||
|
||||
- name: List environment
|
||||
id: list_env
|
||||
shell: bash
|
||||
run: |
|
||||
echo "node@$(node -v)"
|
||||
echo "npm@$(npm -v)"
|
||||
npm -s ls ||:
|
||||
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
npm run test-ci
|
||||
cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov"
|
||||
|
||||
- name: Lint code
|
||||
if: steps.list_env.outputs.eslint != ''
|
||||
run: npm run lint
|
||||
|
||||
- name: Collect code coverage
|
||||
run: |
|
||||
mv ./coverage "./${{ matrix.name }}"
|
||||
mkdir ./coverage
|
||||
mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}"
|
||||
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage
|
||||
path: ./coverage
|
||||
retention-days: 1
|
||||
|
||||
coverage:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install lcov
|
||||
shell: bash
|
||||
run: sudo apt-get -y install lcov
|
||||
|
||||
- name: Collect coverage reports
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: coverage
|
||||
path: ./coverage
|
||||
|
||||
- name: Merge coverage reports
|
||||
shell: bash
|
||||
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./coverage/lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
21
.gitignore
vendored
21
.gitignore
vendored
@@ -1,26 +1,15 @@
|
||||
# OS X
|
||||
.DS_Store*
|
||||
Icon?
|
||||
._*
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Linux
|
||||
.directory
|
||||
*~
|
||||
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
package-lock.json
|
||||
*.log
|
||||
*.gz
|
||||
|
||||
|
||||
# Coveralls
|
||||
.nyc_output
|
||||
coverage
|
||||
|
||||
# Benchmarking
|
||||
benchmarks/graphs
|
||||
|
||||
# ignore additional files using core.excludesFile
|
||||
# https://git-scm.com/docs/gitignore
|
||||
|
||||
33
.travis.yml
33
.travis.yml
@@ -1,33 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.12"
|
||||
- "1.8"
|
||||
- "2.5"
|
||||
- "3.3"
|
||||
- "4.8"
|
||||
- "5.12"
|
||||
- "6.11"
|
||||
- "7.10"
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "8.0"
|
||||
env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
allow_failures:
|
||||
# Allow the nightly installs to fail
|
||||
- env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
before_install:
|
||||
# Remove all non-test dependencies
|
||||
- "npm rm --save-dev connect-redis"
|
||||
|
||||
# Update Node.js modules
|
||||
- "test ! -d node_modules || npm prune"
|
||||
- "test ! -d node_modules || npm rebuild"
|
||||
script:
|
||||
- "npm run test-ci"
|
||||
- "npm run lint"
|
||||
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
|
||||
92
Charter.md
Normal file
92
Charter.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Express Charter
|
||||
|
||||
## Section 0: Guiding Principles
|
||||
|
||||
The Express project is part of the OpenJS Foundation which operates
|
||||
transparently, openly, collaboratively, and ethically.
|
||||
Project proposals, timelines, and status must not merely be open, but
|
||||
also easily visible to outsiders.
|
||||
|
||||
## Section 1: Scope
|
||||
|
||||
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
|
||||
the ecosystem, we have an important place for new users and experts
|
||||
alike.
|
||||
|
||||
### 1.1: In-scope
|
||||
|
||||
Express is made of many modules spread between three GitHub Orgs:
|
||||
|
||||
- [expressjs](http://github.com/expressjs/): Top level middleware and
|
||||
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
|
||||
|
||||
### 1.2: Out-of-Scope
|
||||
|
||||
Section Intentionally Left Blank
|
||||
|
||||
## Section 2: Relationship with OpenJS Foundation CPC.
|
||||
|
||||
Technical leadership for the projects within the OpenJS Foundation is
|
||||
delegated to the projects through their project charters by the OpenJS
|
||||
Cross Project Council (CPC). In the case of the Express project, it is
|
||||
delegated to the Express Technical Committee ("TC").
|
||||
|
||||
This Technical Committee is in charge of both the day-to-day operations
|
||||
of the project, as well as its technical management. This charter can
|
||||
be amended by the TC requiring at least two approvals and a minimum two
|
||||
week comment period for other TC members or CPC members to object. Any
|
||||
changes the CPC wishes to propose will be considered a priority but
|
||||
will follow the same process.
|
||||
|
||||
### 2.1 Other Formal Project Relationships
|
||||
|
||||
Section Intentionally Left Blank
|
||||
|
||||
## Section 3: Express Governing Body
|
||||
|
||||
The Express project is managed by the Technical Committee ("TC").
|
||||
Members can be added to the TC at any time. Any committer 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.
|
||||
|
||||
## Section 4: Roles & Responsibilities
|
||||
|
||||
The Express TC manages all aspects of both the technical and community
|
||||
parts of the project. Members of the TC should attend the regular
|
||||
meetings when possible, and be available for discussion of time
|
||||
sensitive or important issues.
|
||||
|
||||
### Section 4.1 Project Operations & Management
|
||||
|
||||
Section Intentionally Left Blank
|
||||
|
||||
### Section 4.2: Decision-making, Voting, and/or Elections
|
||||
|
||||
The Express TC uses a "consensus seeking" process for issues that are
|
||||
escalated to the TC. The group tries to find a resolution that has no
|
||||
open objections among TC members. If a consensus cannot be reached
|
||||
that has no objections then a majority wins vote 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 will resolve all issues on its
|
||||
agenda during that meeting and may prefer to continue the discussion
|
||||
happening among the committers.
|
||||
|
||||
### Section 4.3: Other Project Roles
|
||||
|
||||
Section Intentionally Left Blank
|
||||
|
||||
## Section 5: Definitions
|
||||
|
||||
Section Intentionally Left Blank
|
||||
139
Code-Of-Conduct.md
Normal file
139
Code-Of-Conduct.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
As a member of the Open JS Foundation, Express has adopted the
|
||||
[Contributor Covenant 2.0][cc-20-doc].
|
||||
|
||||
If an issue arises and you cannot resolve it directly with the parties
|
||||
involved, you can report it to the Express project TC through the following
|
||||
email: express-coc@lists.openjsf.org
|
||||
|
||||
In addition, the OpenJS Foundation maintains a Code of Conduct Panel (CoCP).
|
||||
This is a foundation-wide team established to manage escalation when a reporter
|
||||
believes that a report to a member project or the CPC has not been properly
|
||||
handled. In order to escalate to the CoCP send an email to
|
||||
coc-escalation@lists.openjsf.org.
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances
|
||||
of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for
|
||||
moderation decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail
|
||||
address, posting via an official social media account, or acting as an
|
||||
appointed representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
express-coc@lists.openjsf.org. All complaints will be reviewed and
|
||||
investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited
|
||||
interaction with those enforcing the Code of Conduct, is allowed during this
|
||||
period. Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
project community.
|
||||
|
||||
## Attribution
|
||||
|
||||
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).
|
||||
|
||||
[cc-20-doc]: https://www.contributor-covenant.org/version/2/0/code_of_conduct/
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -1,3 +1,4 @@
|
||||
# Express Collaborator Guide
|
||||
|
||||
## Website Issues
|
||||
|
||||
@@ -21,7 +22,7 @@ a future release of Express.
|
||||
|
||||
1. [Create an issue](https://github.com/expressjs/express/issues/new) for the
|
||||
bug you want to fix or the feature that you want to add.
|
||||
2. Create your own [fork](https://github.com/expressjs/express) on github, then
|
||||
2. Create your own [fork](https://github.com/expressjs/express) on GitHub, then
|
||||
checkout your fork.
|
||||
3. Write your code in your local copy. It's good practice to create a branch for
|
||||
each new issue you work on, although not compulsory.
|
||||
|
||||
115
Contributing.md
115
Contributing.md
@@ -12,14 +12,16 @@ 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.
|
||||
|
||||
# Logging Issues
|
||||
## Logging Issues
|
||||
|
||||
Log an issue for any question or problem you might have. When in doubt, log an issue, and
|
||||
any additional policies about what to include will be provided in the responses. The only
|
||||
exception is security dislosures which should be sent privately.
|
||||
exception is security disclosures which should be sent privately.
|
||||
|
||||
Committers may direct you to another repository, ask for additional clarifications, and
|
||||
add appropriate metadata before the issue is addressed.
|
||||
@@ -27,7 +29,7 @@ add appropriate metadata before the issue is addressed.
|
||||
Please be courteous and respectful. Every participant is expected to follow the
|
||||
project's Code of Conduct.
|
||||
|
||||
# Contributions
|
||||
## Contributions
|
||||
|
||||
Any change to resources in this repository must be through pull requests. This applies to all changes
|
||||
to documentation, code, binary files, etc. Even long term committers and TC members must use
|
||||
@@ -41,7 +43,7 @@ weekends and other holiday periods to ensure active committers all have reasonab
|
||||
become involved in the discussion and review process if they wish.
|
||||
|
||||
The default for each contribution is that it is accepted once no committer has an objection.
|
||||
During review committers may also request that a specific contributor who is most versed in a
|
||||
During a review, committers may also request that a specific contributor who is most versed in a
|
||||
particular area gives a "LGTM" before the PR can be merged. There is no additional "sign off"
|
||||
process for contributions to land. Once all issues brought by committers are addressed it can
|
||||
be landed by any committer.
|
||||
@@ -56,7 +58,36 @@ discuss pending contributions in order to find a resolution. It is expected that
|
||||
small minority of issues be brought to the TC for resolution and that discussion and
|
||||
compromise among committers be the default resolution mechanism.
|
||||
|
||||
# Becoming a Committer
|
||||
## Becoming a Triager
|
||||
|
||||
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.
|
||||
|
||||
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!
|
||||
|
||||
## Becoming a Committer
|
||||
|
||||
All contributors who land a non-trivial contribution should be on-boarded in a timely manner,
|
||||
and added as a committer, and be given write access to the repository.
|
||||
@@ -64,7 +95,7 @@ and added as a committer, and be given write access to the repository.
|
||||
Committers are expected to follow this policy and continue to send pull requests, go through
|
||||
proper review, and have other committers merge their pull requests.
|
||||
|
||||
# TC Process
|
||||
## TC Process
|
||||
|
||||
The TC uses a "consensus seeking" process for issues that are escalated to the TC.
|
||||
The group tries to find a resolution that has no open objections among TC members.
|
||||
@@ -72,14 +103,74 @@ 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.
|
||||
|
||||
### Current Project Captains
|
||||
|
||||
- `expressjs/express`: @wesleytodd
|
||||
- `expressjs/discussions`: @wesleytodd
|
||||
- `expressjs/expressjs.com`: @crandmck
|
||||
- `expressjs/body-parser`: @wesleytodd
|
||||
- `expressjs/multer`: @LinusU
|
||||
- `expressjs/cookie-parser`: @wesleytodd
|
||||
- `expressjs/generator`: @wesleytodd
|
||||
- `expressjs/statusboard`: @wesleytodd
|
||||
- `pillarjs/path-to-regexp`: @blakeembrey
|
||||
- `pillarjs/router`: @dougwilson, @wesleytodd
|
||||
- `pillarjs/finalhandler`: @wesleytodd
|
||||
- `pillarjs/request`: @wesleytodd
|
||||
- `jshttp/http-errors`: @wesleytodd
|
||||
- `jshttp/cookie`: @wesleytodd
|
||||
- `jshttp/on-finished`: @wesleytodd
|
||||
- `jshttp/forwarded`: @wesleytodd
|
||||
- `jshttp/proxy-addr`: @wesleytodd
|
||||
|
||||
|
||||
349
History.md
349
History.md
@@ -1,3 +1,324 @@
|
||||
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
|
||||
===================
|
||||
|
||||
* Fix handling of `undefined` in `res.jsonp`
|
||||
* Fix handling of `undefined` when `"json escape"` is enabled
|
||||
* Fix incorrect middleware execution with unanchored `RegExp`s
|
||||
* Fix `res.jsonp(obj, status)` deprecation message
|
||||
* Fix typo in `res.is` JSDoc
|
||||
* deps: body-parser@1.19.1
|
||||
- deps: bytes@3.1.1
|
||||
- deps: http-errors@1.8.1
|
||||
- deps: qs@6.9.6
|
||||
- deps: raw-body@2.4.2
|
||||
- deps: safe-buffer@5.2.1
|
||||
- deps: type-is@~1.6.18
|
||||
* deps: content-disposition@0.5.4
|
||||
- deps: safe-buffer@5.2.1
|
||||
* deps: cookie@0.4.1
|
||||
- Fix `maxAge` option to reject invalid values
|
||||
* deps: proxy-addr@~2.0.7
|
||||
- Use `req.socket` over deprecated `req.connection`
|
||||
- deps: forwarded@0.2.0
|
||||
- deps: ipaddr.js@1.9.1
|
||||
* deps: qs@6.9.6
|
||||
* deps: safe-buffer@5.2.1
|
||||
* deps: send@0.17.2
|
||||
- deps: http-errors@1.8.1
|
||||
- deps: ms@2.1.3
|
||||
- pref: ignore empty http tokens
|
||||
* deps: serve-static@1.14.2
|
||||
- deps: send@0.17.2
|
||||
* deps: setprototypeof@1.2.0
|
||||
|
||||
4.17.1 / 2019-05-25
|
||||
===================
|
||||
|
||||
* Revert "Improve error message for `null`/`undefined` to `res.status`"
|
||||
|
||||
4.17.0 / 2019-05-16
|
||||
===================
|
||||
|
||||
* Add `express.raw` to parse bodies into `Buffer`
|
||||
* Add `express.text` to parse bodies into string
|
||||
* Improve error message for non-strings to `res.sendFile`
|
||||
* Improve error message for `null`/`undefined` to `res.status`
|
||||
* Support multiple hosts in `X-Forwarded-Host`
|
||||
* deps: accepts@~1.3.7
|
||||
* deps: body-parser@1.19.0
|
||||
- Add encoding MIK
|
||||
- Add petabyte (`pb`) support
|
||||
- Fix parsing array brackets after index
|
||||
- deps: bytes@3.1.0
|
||||
- deps: http-errors@1.7.2
|
||||
- deps: iconv-lite@0.4.24
|
||||
- deps: qs@6.7.0
|
||||
- deps: raw-body@2.4.0
|
||||
- deps: type-is@~1.6.17
|
||||
* deps: content-disposition@0.5.3
|
||||
* deps: cookie@0.4.0
|
||||
- Add `SameSite=None` support
|
||||
* deps: finalhandler@~1.1.2
|
||||
- Set stricter `Content-Security-Policy` header
|
||||
- deps: parseurl@~1.3.3
|
||||
- deps: statuses@~1.5.0
|
||||
* deps: parseurl@~1.3.3
|
||||
* deps: proxy-addr@~2.0.5
|
||||
- deps: ipaddr.js@1.9.0
|
||||
* deps: qs@6.7.0
|
||||
- Fix parsing array brackets after index
|
||||
* deps: range-parser@~1.2.1
|
||||
* deps: send@0.17.1
|
||||
- Set stricter CSP header in redirect & error responses
|
||||
- deps: http-errors@~1.7.2
|
||||
- deps: mime@1.6.0
|
||||
- deps: ms@2.1.1
|
||||
- deps: range-parser@~1.2.1
|
||||
- deps: statuses@~1.5.0
|
||||
- perf: remove redundant `path.normalize` call
|
||||
* deps: serve-static@1.14.1
|
||||
- Set stricter CSP header in redirect response
|
||||
- deps: parseurl@~1.3.3
|
||||
- deps: send@0.17.1
|
||||
* deps: setprototypeof@1.1.1
|
||||
* deps: statuses@~1.5.0
|
||||
- Add `103 Early Hints`
|
||||
* deps: type-is@~1.6.18
|
||||
- deps: mime-types@~2.1.24
|
||||
- perf: prevent internal `throw` on invalid type
|
||||
|
||||
4.16.4 / 2018-10-10
|
||||
===================
|
||||
|
||||
* Fix issue where `"Request aborted"` may be logged in `res.sendfile`
|
||||
* Fix JSDoc for `Router` constructor
|
||||
* deps: body-parser@1.18.3
|
||||
- Fix deprecation warnings on Node.js 10+
|
||||
- Fix stack trace for strict json parse error
|
||||
- deps: depd@~1.1.2
|
||||
- deps: http-errors@~1.6.3
|
||||
- deps: iconv-lite@0.4.23
|
||||
- deps: qs@6.5.2
|
||||
- deps: raw-body@2.3.3
|
||||
- deps: type-is@~1.6.16
|
||||
* deps: proxy-addr@~2.0.4
|
||||
- deps: ipaddr.js@1.8.0
|
||||
* deps: qs@6.5.2
|
||||
* deps: safe-buffer@5.1.2
|
||||
|
||||
4.16.3 / 2018-03-12
|
||||
===================
|
||||
|
||||
* deps: accepts@~1.3.5
|
||||
- deps: mime-types@~2.1.18
|
||||
* deps: depd@~1.1.2
|
||||
- perf: remove argument reassignment
|
||||
* deps: encodeurl@~1.0.2
|
||||
- Fix encoding `%` as last character
|
||||
* deps: finalhandler@1.1.1
|
||||
- Fix 404 output for bad / missing pathnames
|
||||
- deps: encodeurl@~1.0.2
|
||||
- deps: statuses@~1.4.0
|
||||
* deps: proxy-addr@~2.0.3
|
||||
- deps: ipaddr.js@1.6.0
|
||||
* deps: send@0.16.2
|
||||
- Fix incorrect end tag in default error & redirects
|
||||
- deps: depd@~1.1.2
|
||||
- deps: encodeurl@~1.0.2
|
||||
- deps: statuses@~1.4.0
|
||||
* deps: serve-static@1.13.2
|
||||
- Fix incorrect end tag in redirects
|
||||
- deps: encodeurl@~1.0.2
|
||||
- deps: send@0.16.2
|
||||
* deps: statuses@~1.4.0
|
||||
* deps: type-is@~1.6.16
|
||||
- deps: mime-types@~2.1.18
|
||||
|
||||
4.16.2 / 2017-10-09
|
||||
===================
|
||||
|
||||
* Fix `TypeError` in `res.send` when given `Buffer` and `ETag` header set
|
||||
* perf: skip parsing of entire `X-Forwarded-Proto` header
|
||||
|
||||
4.16.1 / 2017-09-29
|
||||
===================
|
||||
|
||||
* deps: send@0.16.1
|
||||
* deps: serve-static@1.13.1
|
||||
- Fix regression when `root` is incorrectly set to a file
|
||||
- deps: send@0.16.1
|
||||
|
||||
4.16.0 / 2017-09-28
|
||||
===================
|
||||
|
||||
* Add `"json escape"` setting for `res.json` and `res.jsonp`
|
||||
* Add `express.json` and `express.urlencoded` to parse bodies
|
||||
* Add `options` argument to `res.download`
|
||||
* Improve error message when autoloading invalid view engine
|
||||
* Improve error messages when non-function provided as middleware
|
||||
* Skip `Buffer` encoding when not generating ETag for small response
|
||||
* Use `safe-buffer` for improved Buffer API
|
||||
* deps: accepts@~1.3.4
|
||||
- deps: mime-types@~2.1.16
|
||||
* deps: content-type@~1.0.4
|
||||
- perf: remove argument reassignment
|
||||
- perf: skip parameter parsing when no parameters
|
||||
* deps: etag@~1.8.1
|
||||
- perf: replace regular expression with substring
|
||||
* deps: finalhandler@1.1.0
|
||||
- Use `res.headersSent` when available
|
||||
* deps: parseurl@~1.3.2
|
||||
- perf: reduce overhead for full URLs
|
||||
- perf: unroll the "fast-path" `RegExp`
|
||||
* deps: proxy-addr@~2.0.2
|
||||
- Fix trimming leading / trailing OWS in `X-Forwarded-For`
|
||||
- deps: forwarded@~0.1.2
|
||||
- deps: ipaddr.js@1.5.2
|
||||
- perf: reduce overhead when no `X-Forwarded-For` header
|
||||
* deps: qs@6.5.1
|
||||
- Fix parsing & compacting very deep objects
|
||||
* deps: send@0.16.0
|
||||
- Add 70 new types for file extensions
|
||||
- Add `immutable` option
|
||||
- Fix missing `</html>` in default error & redirects
|
||||
- Set charset as "UTF-8" for .js and .json
|
||||
- Use instance methods on steam to check for listeners
|
||||
- deps: mime@1.4.1
|
||||
- perf: improve path validation speed
|
||||
* deps: serve-static@1.13.0
|
||||
- Add 70 new types for file extensions
|
||||
- Add `immutable` option
|
||||
- Set charset as "UTF-8" for .js and .json
|
||||
- deps: send@0.16.0
|
||||
* deps: setprototypeof@1.1.0
|
||||
* deps: utils-merge@1.0.1
|
||||
* deps: vary@~1.1.2
|
||||
- perf: improve header token parsing speed
|
||||
* perf: re-use options object when generating ETags
|
||||
* perf: remove dead `.charset` set in `res.jsonp`
|
||||
|
||||
4.15.5 / 2017-09-24
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.9
|
||||
* deps: finalhandler@~1.0.6
|
||||
- deps: debug@2.6.9
|
||||
- deps: parseurl@~1.3.2
|
||||
* deps: fresh@0.5.2
|
||||
- Fix handling of modified headers with invalid dates
|
||||
- perf: improve ETag match loop
|
||||
- perf: improve `If-None-Match` token parsing
|
||||
* deps: send@0.15.6
|
||||
- Fix handling of modified headers with invalid dates
|
||||
- deps: debug@2.6.9
|
||||
- deps: etag@~1.8.1
|
||||
- deps: fresh@0.5.2
|
||||
- perf: improve `If-Match` token parsing
|
||||
* deps: serve-static@1.12.6
|
||||
- deps: parseurl@~1.3.2
|
||||
- deps: send@0.15.6
|
||||
- perf: improve slash collapsing
|
||||
|
||||
4.15.4 / 2017-08-06
|
||||
===================
|
||||
|
||||
@@ -162,7 +483,7 @@
|
||||
- Fix including type extensions in parameters in `Accept` parsing
|
||||
- Fix parsing `Accept` parameters with quoted equals
|
||||
- Fix parsing `Accept` parameters with quoted semicolons
|
||||
- Many performance improvments
|
||||
- Many performance improvements
|
||||
- deps: mime-types@~2.1.11
|
||||
- deps: negotiator@0.6.1
|
||||
* deps: content-type@~1.0.2
|
||||
@@ -177,7 +498,7 @@
|
||||
- perf: enable strict mode
|
||||
- perf: hoist regular expression
|
||||
- perf: use for loop in parse
|
||||
- perf: use string concatination for serialization
|
||||
- perf: use string concatenation for serialization
|
||||
* deps: finalhandler@0.5.0
|
||||
- Change invalid or non-numeric status code to 500
|
||||
- Overwrite status message to match set status code
|
||||
@@ -187,7 +508,7 @@
|
||||
* deps: proxy-addr@~1.1.2
|
||||
- Fix accepting various invalid netmasks
|
||||
- Fix IPv6-mapped IPv4 validation edge cases
|
||||
- IPv4 netmasks must be contingous
|
||||
- IPv4 netmasks must be contiguous
|
||||
- IPv6 addresses cannot be used as a netmask
|
||||
- deps: ipaddr.js@1.1.1
|
||||
* deps: qs@6.2.0
|
||||
@@ -965,13 +1286,13 @@
|
||||
- deps: negotiator@0.4.6
|
||||
* deps: debug@1.0.2
|
||||
* deps: send@0.4.3
|
||||
- Do not throw un-catchable error on file open race condition
|
||||
- Do not throw uncatchable error on file open race condition
|
||||
- Use `escape-html` for HTML escaping
|
||||
- deps: debug@1.0.2
|
||||
- deps: finished@1.2.2
|
||||
- deps: fresh@0.2.2
|
||||
* deps: serve-static@1.2.3
|
||||
- Do not throw un-catchable error on file open race condition
|
||||
- Do not throw uncatchable error on file open race condition
|
||||
- deps: send@0.4.3
|
||||
|
||||
4.4.2 / 2014-06-09
|
||||
@@ -1812,7 +2133,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
|
||||
@@ -1851,7 +2172,7 @@
|
||||
- deps: serve-static@1.2.3
|
||||
* deps: debug@1.0.2
|
||||
* deps: send@0.4.3
|
||||
- Do not throw un-catchable error on file open race condition
|
||||
- Do not throw uncatchable error on file open race condition
|
||||
- Use `escape-html` for HTML escaping
|
||||
- deps: debug@1.0.2
|
||||
- deps: finished@1.2.2
|
||||
@@ -3023,8 +3344,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
|
||||
|
||||
@@ -3036,7 +3357,7 @@ Shaw]
|
||||
* Updated haml submodule
|
||||
* Changed ETag; removed inode, modified time only
|
||||
* Fixed LF to CRLF for setting multiple cookies
|
||||
* Fixed cookie complation; values are now urlencoded
|
||||
* Fixed cookie compilation; values are now urlencoded
|
||||
* Fixed cookies parsing; accepts quoted values and url escaped cookies
|
||||
|
||||
0.11.0 / 2010-05-06
|
||||
@@ -3059,7 +3380,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
|
||||
@@ -3098,7 +3419,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)
|
||||
@@ -3231,7 +3552,7 @@ Shaw]
|
||||
|
||||
* Added "plot" format option for Profiler (for gnuplot processing)
|
||||
* Added request number to Profiler plugin
|
||||
* Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8
|
||||
* Fixed binary encoding for multipart file uploads, was previously defaulting to UTF8
|
||||
* Fixed issue with routes not firing when not files are present. Closes #184
|
||||
* Fixed process.Promise -> events.Promise
|
||||
|
||||
@@ -3277,7 +3598,7 @@ Shaw]
|
||||
* Updated sample chat app to show messages on load
|
||||
* Updated libxmljs parseString -> parseHtmlString
|
||||
* Fixed `make init` to work with older versions of git
|
||||
* Fixed specs can now run independent specs for those who cant build deps. Closes #127
|
||||
* Fixed specs can now run independent specs for those who can't build deps. Closes #127
|
||||
* Fixed issues introduced by the node url module changes. Closes 126.
|
||||
* Fixed two assertions failing due to Collection#keys() returning strings
|
||||
* Fixed faulty Collection#toArray() spec due to keys() returning strings
|
||||
|
||||
103
Readme.md
103
Readme.md
@@ -1,16 +1,14 @@
|
||||
[](http://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org).
|
||||
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
[![Linux Build][travis-image]][travis-url]
|
||||
[![Windows Build][appveyor-image]][appveyor-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
[![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
|
||||
var express = require('express')
|
||||
var app = express()
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
res.send('Hello World')
|
||||
@@ -21,10 +19,25 @@ app.listen(3000)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||
[npm registry](https://www.npmjs.com/).
|
||||
|
||||
Before installing, [download and install Node.js](https://nodejs.org/en/download/).
|
||||
Node.js 0.10 or higher is required.
|
||||
|
||||
If this is a brand new project, make sure to create a `package.json` first with
|
||||
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):
|
||||
|
||||
```console
|
||||
$ npm install express
|
||||
```
|
||||
|
||||
Follow [our installing guide](http://expressjs.com/en/starter/installing.html)
|
||||
for more information.
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
@@ -38,7 +51,7 @@ $ npm install express
|
||||
## 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
|
||||
@@ -46,42 +59,40 @@ $ npm install express
|
||||
|
||||
**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
|
||||
```
|
||||
|
||||
View the website at: http://localhost:3000
|
||||
|
||||
## Philosophy
|
||||
|
||||
The Express philosophy is to provide small, robust tooling for HTTP servers, making
|
||||
it a great solution for single page applications, web sites, hybrids, or public
|
||||
it a great solution for single page applications, websites, hybrids, or public
|
||||
HTTP APIs.
|
||||
|
||||
Express does not force you to use any specific ORM or template engine. With support for over
|
||||
@@ -92,32 +103,48 @@ $ 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
|
||||
## Contributing
|
||||
|
||||
To run the test suite, first install the dependencies, then run `npm test`:
|
||||
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
|
||||
[![Windows Build][appveyor-image]][appveyor-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
```bash
|
||||
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) [![TJ's Gratipay][gratipay-image-visionmedia]][gratipay-url-visionmedia]
|
||||
The original author of Express is [TJ Holowaychuk](https://github.com/tj)
|
||||
|
||||
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson) [![Doug's Gratipay][gratipay-image-dougwilson]][gratipay-url-dougwilson]
|
||||
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson)
|
||||
|
||||
[List of all contributors](https://github.com/expressjs/express/graphs/contributors)
|
||||
|
||||
@@ -125,17 +152,15 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[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://npmjs.org/package/express
|
||||
[travis-image]: https://img.shields.io/travis/expressjs/express/master.svg?label=linux
|
||||
[travis-url]: https://travis-ci.org/expressjs/express
|
||||
[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
|
||||
[gratipay-image-visionmedia]: https://img.shields.io/gratipay/visionmedia.svg
|
||||
[gratipay-url-visionmedia]: https://gratipay.com/visionmedia/
|
||||
[gratipay-image-dougwilson]: https://img.shields.io/gratipay/dougwilson.svg
|
||||
[gratipay-url-dougwilson]: https://gratipay.com/dougwilson/
|
||||
[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
|
||||
|
||||
@@ -184,3 +184,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,
|
||||
|
||||
@@ -16,6 +16,10 @@ contributions.
|
||||
|
||||
Report security bugs by emailing the lead maintainer in the Readme.md file.
|
||||
|
||||
To ensure the timely response to your report, please ensure that the entirety
|
||||
of the report is contained within the email body and not solely behind a web
|
||||
link or an attachment.
|
||||
|
||||
The lead maintainer will acknowledge your email within 48 hours, and will send a
|
||||
more detailed response within 48 hours indicating the next steps in handling
|
||||
your report. After the initial reply to your report, the security team will
|
||||
@@ -23,8 +27,7 @@ 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.
|
||||
|
||||
## Disclosure Policy
|
||||
|
||||
|
||||
63
Triager-Guide.md
Normal file
63
Triager-Guide.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Express Triager Guide
|
||||
|
||||
## Issue Triage Process
|
||||
|
||||
When a new issue or pull request is opened the issue will be labeled with `needs triage`.
|
||||
If a triage team member is available they can help make sure all the required information
|
||||
is provided. Depending on the issue or PR there are several next labels they can add for further
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Approaches and Best Practices for getting into triage contributions
|
||||
|
||||
Review the organization's [StatusBoard](https://expressjs.github.io/statusboard/),
|
||||
pay special attention to these columns: stars, watchers, open issues, and contributors.
|
||||
This gives you a general idea about the criticality and health of the repository.
|
||||
Pick a few projects based on that criteria, your interests, and skills (existing or aspiring).
|
||||
|
||||
Review the project's contribution guideline if present. In a nutshell,
|
||||
commit to the community's standards and values. Review the
|
||||
documentation, for most of the projects it is just the README.md, and
|
||||
make sure you understand the key APIs, semantics, configurations, and use cases.
|
||||
|
||||
It might be helpful to write your own test apps to re-affirm your
|
||||
understanding of the key functions. This may identify some gaps in
|
||||
documentation, record those as they might be good PR's to open.
|
||||
Skim through the issue backlog; identify low hanging issues and mostly new ones.
|
||||
From those, attempt to recreate issues based on the OP description and
|
||||
ask questions if required. No question is a bad question!
|
||||
|
||||
## Removal of Triage Role
|
||||
|
||||
There are a few cases where members can be removed as triagers:
|
||||
|
||||
- Breaking the CoC or project contributor guidelines
|
||||
- Abuse or misuse of the role as deemed by the TC
|
||||
- Lack of participation for more than 6 months
|
||||
|
||||
If any of these happen we will discuss as a part of the triage portion of the regular TC meetings.
|
||||
If you have questions feel free to reach out to any of the TC members.
|
||||
|
||||
## Other Helpful Hints:
|
||||
|
||||
- Everyone is welcome to attend the [Express Technical Committee Meetings](https://github.com/expressjs/discussions#expressjs-tc-meetings), and as a triager, it might help to get a better idea of what's happening with the project.
|
||||
- When exploring the module's functionality there are a few helpful steps:
|
||||
- Turn on `DEBUG=*` (see https://www.npmjs.com/package/debug) to get detailed log information
|
||||
- It is also a good idea to do live debugging to follow the control flow, try using `node --inspect`
|
||||
- It is a good idea to make at least one pass of reading through the entire source
|
||||
- When reviewing the list of open issues there are some common types and suggested actions:
|
||||
- New/unattended issues or simple questions: A good place to start
|
||||
- Hard bugs & ongoing discussions: always feel free to chime in and help
|
||||
- Issues that imply gaps in the documentation: open PRs with changes or help the user to do so
|
||||
- For recurring issues, it is helpful to create functional examples to demonstrate (publish as gists or a repo)
|
||||
- Review and identify the maintainers. If necessary, at-mention one or more of them if you are unsure what to do
|
||||
- Make sure all your interactions are professional, welcoming, and respectful to the parties involved.
|
||||
104
appveyor.yml
104
appveyor.yml
@@ -5,22 +5,108 @@ environment:
|
||||
- nodejs_version: "1.8"
|
||||
- nodejs_version: "2.5"
|
||||
- nodejs_version: "3.3"
|
||||
- nodejs_version: "4.8"
|
||||
- nodejs_version: "4.9"
|
||||
- nodejs_version: "5.12"
|
||||
- nodejs_version: "6.11"
|
||||
- nodejs_version: "6.17"
|
||||
- nodejs_version: "7.10"
|
||||
- nodejs_version: "8.17"
|
||||
- nodejs_version: "9.11"
|
||||
- nodejs_version: "10.24"
|
||||
- nodejs_version: "11.15"
|
||||
- nodejs_version: "12.22"
|
||||
- nodejs_version: "13.14"
|
||||
- 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"
|
||||
cache:
|
||||
- node_modules
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- npm rm --save-dev connect-redis
|
||||
- if exist node_modules npm prune
|
||||
- if exist node_modules npm rebuild
|
||||
# Install Node.js
|
||||
- ps: >-
|
||||
try { Install-Product node $env:nodejs_version -ErrorAction Stop }
|
||||
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64 }
|
||||
# Configure npm
|
||||
- ps: |
|
||||
npm config set loglevel error
|
||||
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
|
||||
npm rm --silent --save-dev connect-redis
|
||||
# Remove lint dependencies
|
||||
cmd.exe /c "node -pe `"Object.keys(require('./package').devDependencies).join('\n')`"" | `
|
||||
sls "^eslint(-|$)" | `
|
||||
%{ npm rm --silent --save-dev $_ }
|
||||
# Setup Node.js version-specific dependencies
|
||||
- ps: |
|
||||
# mocha for testing
|
||||
# - use 3.x for Node.js < 4
|
||||
# - use 5.x for Node.js < 6
|
||||
# - 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) {
|
||||
npm install --silent --save-dev mocha@5.2.0
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
|
||||
npm install --silent --save-dev mocha@6.2.2
|
||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
|
||||
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 < 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 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: |
|
||||
# Prune & rebuild node_modules
|
||||
if (Test-Path -Path node_modules) {
|
||||
npm prune
|
||||
npm rebuild
|
||||
}
|
||||
# Install Node.js modules
|
||||
- npm install
|
||||
build: off
|
||||
test_script:
|
||||
- node --version
|
||||
- npm --version
|
||||
# Output version data
|
||||
- ps: |
|
||||
node --version
|
||||
npm --version
|
||||
# Run test script
|
||||
- npm run test-ci
|
||||
- npm run lint
|
||||
version: "{build}"
|
||||
|
||||
@@ -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,10 +13,8 @@ while (n--) {
|
||||
});
|
||||
}
|
||||
|
||||
var body = new Buffer('Hello World');
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(body);
|
||||
app.use(function(req, res){
|
||||
res.send('Hello World')
|
||||
});
|
||||
|
||||
app.listen(3333);
|
||||
|
||||
@@ -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
|
||||
|
||||
29
examples/README.md
Normal file
29
examples/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Express examples
|
||||
|
||||
This page contains list of examples using Express.
|
||||
|
||||
- [auth](./auth) - Authentication with login and password
|
||||
- [content-negotiation](./content-negotiation) - HTTP content negotiation
|
||||
- [cookie-sessions](./cookie-sessions) - Working with cookie-based sessions
|
||||
- [cookies](./cookies) - Working with cookies
|
||||
- [downloads](./downloads) - Transferring files to client
|
||||
- [ejs](./ejs) - Working with Embedded JavaScript templating (ejs)
|
||||
- [error-pages](./error-pages) - Creating error pages
|
||||
- [error](./error) - Working with error middleware
|
||||
- [hello-world](./hello-world) - Simple request handler
|
||||
- [markdown](./markdown) - Markdown as template engine
|
||||
- [multi-router](./multi-router) - Working with multiple Express routers
|
||||
- [mvc](./mvc) - MVC-style controllers
|
||||
- [online](./online) - Tracking online user activity with `online` and `redis` packages
|
||||
- [params](./params) - Working with route parameters
|
||||
- [resource](./resource) - Multiple HTTP operations on the same resource
|
||||
- [route-map](./route-map) - Organizing routes using a map
|
||||
- [route-middleware](./route-middleware) - Working with route middleware
|
||||
- [route-separation](./route-separation) - Organizing routes per each resource
|
||||
- [search](./search) - Search API
|
||||
- [session](./session) - User sessions
|
||||
- [static-files](./static-files) - Serving static files
|
||||
- [vhost](./vhost) - Working with virtual hosts
|
||||
- [view-constructor](./view-constructor) - Rendering views dynamically
|
||||
- [view-locals](./view-locals) - Saving data in request object between middleware calls
|
||||
- [web-service](./web-service) - Simple API service
|
||||
@@ -1,9 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var bodyParser = require('body-parser');
|
||||
var hash = require('pbkdf2-password')()
|
||||
var path = require('path');
|
||||
var session = require('express-session');
|
||||
@@ -17,7 +18,7 @@ app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// middleware
|
||||
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(express.urlencoded({ extended: false }))
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
@@ -60,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'));
|
||||
if (hash === user.hash) return fn(null, user)
|
||||
fn(null, null)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -100,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
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title><%= title %></title>
|
||||
<style>
|
||||
body {
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
|
||||
<% var title = 'Authentication Example' %>
|
||||
<% include head %>
|
||||
<%- include('head', { title: 'Authentication Example' }) -%>
|
||||
|
||||
<h1>Login</h1>
|
||||
<%- message %>
|
||||
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">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<% include foot %>
|
||||
<%- include('foot') -%>
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -6,10 +8,9 @@ var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// custom log format
|
||||
if ('test' != process.env.NODE_ENV) app.use(logger(':method :url'));
|
||||
if (process.env.NODE_ENV !== 'test') app.use(logger(':method :url'))
|
||||
|
||||
// parses request cookies, populating
|
||||
// req.cookies and req.signedCookies
|
||||
@@ -18,7 +19,7 @@ if ('test' != process.env.NODE_ENV) app.use(logger(':method :url'));
|
||||
app.use(cookieParser('my secret here'));
|
||||
|
||||
// parses x-www-form-urlencoded
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(express.urlencoded({ extended: false }))
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
|
||||
3
examples/downloads/files/notes/groceries.txt
Normal file
3
examples/downloads/files/notes/groceries.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
* milk
|
||||
* eggs
|
||||
* bread
|
||||
@@ -1,27 +1,32 @@
|
||||
'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/amazing.txt">amazing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/CCTV大赛上海分赛区.txt">CCTV大赛上海分赛区.txt</a>.</li>'
|
||||
+ '</ul>');
|
||||
res.send('<ul>' +
|
||||
'<li>Download <a href="/files/notes/groceries.txt">notes/groceries.txt</a>.</li>' +
|
||||
'<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>' +
|
||||
'<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>' +
|
||||
'<li>Download <a href="/files/CCTV大赛上海分赛区.txt">CCTV大赛上海分赛区.txt</a>.</li>' +
|
||||
'</ul>')
|
||||
});
|
||||
|
||||
// /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 && err.status !== 404) return next(err); // non-404 error
|
||||
if (err.status !== 404) return next(err); // non-404 error
|
||||
// file for download not found
|
||||
res.statusCode = 404;
|
||||
res.send('Cant find that file, sorry!');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
body {
|
||||
padding: 50px 80px;
|
||||
font: 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif;
|
||||
font: 14px "Helvetica Neue", "Lucida Grande", Arial, sans-serif;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title><%= title %></title>
|
||||
<link rel="stylesheet" href="/stylesheets/style.css">
|
||||
</head>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<% include header.html %>
|
||||
<%- include('header.html') -%>
|
||||
|
||||
<h1>Users</h1>
|
||||
<ul id="users">
|
||||
@@ -7,4 +7,4 @@
|
||||
<% }) %>
|
||||
</ul>
|
||||
|
||||
<% include footer.html %>
|
||||
<%- include('footer.html') -%>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -6,7 +8,7 @@ var express = require('../../');
|
||||
var path = require('path');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var silent = 'test' == process.env.NODE_ENV;
|
||||
var silent = process.env.NODE_ENV === 'test'
|
||||
|
||||
// general config
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
@@ -19,7 +21,7 @@ app.enable('verbose errors');
|
||||
|
||||
// disable them in production
|
||||
// use $ NODE_ENV=production node examples/error-pages
|
||||
if ('production' == app.settings.env) app.disable('verbose errors');
|
||||
if (app.settings.env === 'production') app.disable('verbose errors')
|
||||
|
||||
silent || app.use(logger('dev'));
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<% include error_header %>
|
||||
<%- include('error_header') -%>
|
||||
<h2>Cannot find <%= url %></h2>
|
||||
<% include footer %>
|
||||
<%- include('footer') -%>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<% include error_header %>
|
||||
<%- include('error_header') -%>
|
||||
<h2>Error: <%= error.message %></h2>
|
||||
<% if (settings['verbose errors']) { %>
|
||||
<pre><%= error.stack %></pre>
|
||||
<% } else { %>
|
||||
<p>An error occurred!</p>
|
||||
<% } %>
|
||||
<% include footer %>
|
||||
<%- include('footer') -%>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Error</title>
|
||||
</head>
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Custom Pages Example</title>
|
||||
</head>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -5,7 +7,7 @@
|
||||
var express = require('../../');
|
||||
var logger = require('morgan');
|
||||
var app = module.exports = express();
|
||||
var test = app.get('env') == 'test';
|
||||
var test = app.get('env') === 'test'
|
||||
|
||||
if (!test) app.use(logger('dev'));
|
||||
|
||||
@@ -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();
|
||||
@@ -6,7 +8,7 @@ app.use('/api/v1', require('./controllers/api_v1'));
|
||||
app.use('/api/v2', require('./controllers/api_v2'));
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.send('Hello form root route.');
|
||||
res.send('Hello from root route.')
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -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,6 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Edit <%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title><%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Edit {{user.name}}</title>
|
||||
</head>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Users</title>
|
||||
</head>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>{{user.name}}</title>
|
||||
</head>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
// faux database
|
||||
|
||||
var pets = exports.pets = [];
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -6,7 +8,6 @@ var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var path = require('path');
|
||||
var session = require('express-session');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
|
||||
var app = module.exports = express();
|
||||
@@ -43,7 +44,7 @@ app.use(session({
|
||||
}));
|
||||
|
||||
// parse request bodies (req.body)
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
|
||||
// allow overriding methods in query (?_method=put)
|
||||
app.use(methodOverride('_method'));
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px "Helvetica Neue", Helvetica, Arial;
|
||||
font: 16px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #107aff;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Not Found</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Internal Server Error</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
// install redis first:
|
||||
// https://redis.io/
|
||||
|
||||
@@ -1,28 +1,23 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var createError = require('http-errors')
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
|
||||
// Faux database
|
||||
|
||||
var users = [
|
||||
{ name: 'tj' }
|
||||
{ name: 'tj' }
|
||||
, { name: 'tobi' }
|
||||
, { name: 'loki' }
|
||||
, { name: 'jane' }
|
||||
, { 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){
|
||||
@@ -56,7 +51,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 +59,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.
|
||||
*/
|
||||
@@ -26,7 +28,7 @@ app.resource = function(path, obj) {
|
||||
// Fake records
|
||||
|
||||
var users = [
|
||||
{ name: 'tj' }
|
||||
{ name: 'tj' }
|
||||
, { name: 'ciaran' }
|
||||
, { name: 'aaron' }
|
||||
, { name: 'guillermo' }
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var escapeHtml = require('escape-html')
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var verbose = process.env.NODE_ENV != 'test';
|
||||
var verbose = process.env.NODE_ENV !== 'test'
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -31,7 +34,7 @@ var users = {
|
||||
},
|
||||
|
||||
get: function(req, res){
|
||||
res.send('user ' + req.params.uid);
|
||||
res.send('user ' + escapeHtml(req.params.uid))
|
||||
},
|
||||
|
||||
delete: function(req, res){
|
||||
@@ -41,11 +44,11 @@ var users = {
|
||||
|
||||
var pets = {
|
||||
list: function(req, res){
|
||||
res.send('user ' + req.params.uid + '\'s pets');
|
||||
res.send('user ' + escapeHtml(req.params.uid) + '\'s pets')
|
||||
},
|
||||
|
||||
delete: function(req, res){
|
||||
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
|
||||
res.send('delete ' + escapeHtml(req.params.uid) + '\'s pet ' + escapeHtml(req.params.pid))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -15,7 +17,7 @@ var app = express();
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' }
|
||||
{ id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' }
|
||||
, { id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' }
|
||||
, { id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' }
|
||||
];
|
||||
@@ -34,7 +36,7 @@ function loadUser(req, res, next) {
|
||||
function andRestrictToSelf(req, res, next) {
|
||||
// If our authenticated user is the user we are viewing
|
||||
// then everything is fine :)
|
||||
if (req.authenticatedUser.id == req.user.id) {
|
||||
if (req.authenticatedUser.id === req.user.id) {
|
||||
next();
|
||||
} else {
|
||||
// You may want to implement specific exceptions
|
||||
@@ -47,7 +49,7 @@ function andRestrictToSelf(req, res, next) {
|
||||
|
||||
function andRestrictTo(role) {
|
||||
return function(req, res, next) {
|
||||
if (req.authenticatedUser.role == role) {
|
||||
if (req.authenticatedUser.role === role) {
|
||||
next();
|
||||
} else {
|
||||
next(new Error('Unauthorized'));
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -7,7 +9,6 @@ var path = require('path');
|
||||
var app = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
var site = require('./site');
|
||||
var post = require('./post');
|
||||
@@ -27,7 +28,7 @@ if (!module.parent) {
|
||||
|
||||
app.use(methodOverride('_method'));
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// General
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title><%= title %></title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<% include header %>
|
||||
<%- include('header') -%>
|
||||
|
||||
<h1><%= title %></h1>
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
<li>Visit the <a href="/posts">posts</a> page.</li>
|
||||
</ul>
|
||||
|
||||
<% include footer %>
|
||||
<%- include('footer') -%>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<% include ../header %>
|
||||
<%- include('../header') -%>
|
||||
|
||||
<h1>Posts</h1>
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
<% }) %>
|
||||
</dl>
|
||||
|
||||
<% include ../footer %>
|
||||
<%- include('../footer') -%>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<% include ../header %>
|
||||
<%- include('../header') -%>
|
||||
|
||||
<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]" />
|
||||
@@ -20,4 +20,4 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<% include ../footer %>
|
||||
<%- include('../footer') -%>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<% include ../header %>
|
||||
<%- include('../header') -%>
|
||||
|
||||
<h1><%= title %></h1>
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
<% }) %>
|
||||
</div>
|
||||
|
||||
<% include ../footer %>
|
||||
<%- include('../footer') -%>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<% include ../header %>
|
||||
<%- include('../header') -%>
|
||||
|
||||
<h1><%= user.name %></h1>
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
<p>Email: <%= user.email %></p>
|
||||
</div>
|
||||
|
||||
<% include ../footer %>
|
||||
<%- include('../footer') -%>
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -5,7 +7,7 @@ search.addEventListener('keyup', function(){
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open('GET', '/search/' + search.value, true);
|
||||
xhr.onreadystatechange = function(){
|
||||
if (4 == xhr.readyState) {
|
||||
if (xhr.readyState === 4) {
|
||||
code.textContent = xhr.responseText;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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;
|
||||
@@ -14,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 +1 @@
|
||||
foo
|
||||
// foo
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -13,7 +15,7 @@ app.set('view engine', 'ejs');
|
||||
// filter ferrets only
|
||||
|
||||
function ferrets(user) {
|
||||
return user.species == 'ferret';
|
||||
return user.species === 'ferret'
|
||||
}
|
||||
|
||||
// naive nesting approach,
|
||||
@@ -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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title><%= title %></title>
|
||||
<style media="screen">
|
||||
body {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -8,7 +10,7 @@ var app = module.exports = express();
|
||||
|
||||
// create an error with .status. we
|
||||
// can then use the property in our
|
||||
// custom error handler (Connect repects this prop as well)
|
||||
// custom error handler (Connect respects this prop as well)
|
||||
|
||||
function error(status, msg) {
|
||||
var err = new Error(msg);
|
||||
@@ -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;
|
||||
@@ -49,13 +51,13 @@ var apiKeys = ['foo', 'bar', 'baz'];
|
||||
// these two objects will serve as our faux database
|
||||
|
||||
var repos = [
|
||||
{ name: 'express', url: 'http://github.com/expressjs/express' }
|
||||
, { name: 'stylus', url: 'http://github.com/learnboost/stylus' }
|
||||
, { name: 'cluster', url: 'http://github.com/learnboost/cluster' }
|
||||
{ name: 'express', url: 'https://github.com/expressjs/express' },
|
||||
{ name: 'stylus', url: 'https://github.com/learnboost/stylus' },
|
||||
{ name: 'cluster', url: 'https://github.com/learnboost/cluster' }
|
||||
];
|
||||
|
||||
var users = [
|
||||
{ name: 'tobi' }
|
||||
{ name: 'tobi' }
|
||||
, { name: 'loki' }
|
||||
, { name: 'jane' }
|
||||
];
|
||||
@@ -69,14 +71,17 @@ var userRepos = {
|
||||
// we now can assume the api key is valid,
|
||||
// and simply expose the data
|
||||
|
||||
app.get('/api/users', function(req, res, next){
|
||||
// example: http://localhost:3000/api/users/?api-key=foo
|
||||
app.get('/api/users', function (req, res) {
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
app.get('/api/repos', function(req, res, next){
|
||||
// example: http://localhost:3000/api/repos/?api-key=foo
|
||||
app.get('/api/repos', function (req, res) {
|
||||
res.send(repos);
|
||||
});
|
||||
|
||||
// example: http://localhost:3000/api/user/tobi/repos/?api-key=foo
|
||||
app.get('/api/user/:name/repos', function(req, res, next){
|
||||
var name = req.params.name;
|
||||
var user = userRepos[name];
|
||||
@@ -102,7 +107,7 @@ app.use(function(err, req, res, next){
|
||||
// invoke next() and do not respond.
|
||||
app.use(function(req, res){
|
||||
res.status(404);
|
||||
res.send({ error: "Lame, can't find that" });
|
||||
res.send({ error: "Sorry, can't find that" })
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -207,7 +214,7 @@ app.use = function use(fn) {
|
||||
var fns = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (fns.length === 0) {
|
||||
throw new TypeError('app.use() requires middleware functions');
|
||||
throw new TypeError('app.use() requires a middleware function')
|
||||
}
|
||||
|
||||
// setup router
|
||||
@@ -276,7 +283,7 @@ app.route = function route(path) {
|
||||
* In this case EJS provides a `.renderFile()` method with
|
||||
* the same signature that Express expects: `(path, options, callback)`,
|
||||
* though note that it aliases this method as `ejs.__express` internally
|
||||
* so if you're using ".ejs" extensions you dont need to do anything.
|
||||
* so if you're using ".ejs" extensions you don't need to do anything.
|
||||
*
|
||||
* Some template engines do not follow this convention, the
|
||||
* [Consolidate.js](https://github.com/tj/consolidate.js)
|
||||
@@ -338,7 +345,7 @@ app.param = function param(name, fn) {
|
||||
* Assign `setting` to `val`, or return `setting`'s value.
|
||||
*
|
||||
* app.set('foo', 'bar');
|
||||
* app.get('foo');
|
||||
* app.set('foo');
|
||||
* // => "bar"
|
||||
*
|
||||
* Mounted servers inherit their parent server's settings.
|
||||
@@ -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);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var bodyParser = require('body-parser')
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var mixin = require('merge-descriptors');
|
||||
var proto = require('./application');
|
||||
@@ -74,16 +75,18 @@ exports.Router = Router;
|
||||
* Expose middleware
|
||||
*/
|
||||
|
||||
exports.json = bodyParser.json
|
||||
exports.query = require('./middleware/query');
|
||||
exports.raw = bodyParser.raw
|
||||
exports.static = require('serve-static');
|
||||
exports.text = bodyParser.text
|
||||
exports.urlencoded = bodyParser.urlencoded
|
||||
|
||||
/**
|
||||
* Replace removed middleware with an appropriate error message.
|
||||
*/
|
||||
|
||||
[
|
||||
'json',
|
||||
'urlencoded',
|
||||
var removedMiddlewares = [
|
||||
'bodyParser',
|
||||
'compress',
|
||||
'cookieSession',
|
||||
@@ -100,8 +103,10 @@ exports.static = require('serve-static');
|
||||
'directory',
|
||||
'limit',
|
||||
'multipart',
|
||||
'staticCache',
|
||||
].forEach(function (name) {
|
||||
'staticCache'
|
||||
]
|
||||
|
||||
removedMiddlewares.forEach(function (name) {
|
||||
Object.defineProperty(exports, name, {
|
||||
get: function () {
|
||||
throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
|
||||
|
||||
@@ -251,7 +251,7 @@ req.param = function param(name, defaultValue) {
|
||||
|
||||
/**
|
||||
* Check if the incoming request contains the "Content-Type"
|
||||
* header field, and it contains the give mime `type`.
|
||||
* header field, and it contains the given mime `type`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -315,8 +315,12 @@ defineGetter(req, 'protocol', function protocol(){
|
||||
|
||||
// Note: X-Forwarded-Proto is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
proto = this.get('X-Forwarded-Proto') || proto;
|
||||
return proto.split(/\s*,\s*/)[0];
|
||||
var header = this.get('X-Forwarded-Proto') || proto
|
||||
var index = header.indexOf(',')
|
||||
|
||||
return index !== -1
|
||||
? header.substring(0, index).trim()
|
||||
: header.trim()
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -426,6 +430,10 @@ defineGetter(req, 'hostname', function hostname(){
|
||||
|
||||
if (!host || !trust(this.connection.remoteAddress, 0)) {
|
||||
host = this.get('Host');
|
||||
} else if (host.indexOf(',') !== -1) {
|
||||
// Note: X-Forwarded-Host is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
host = host.substring(0, host.indexOf(',')).trimRight()
|
||||
}
|
||||
|
||||
if (!host) return;
|
||||
|
||||
215
lib/response.js
215
lib/response.js
@@ -12,7 +12,9 @@
|
||||
* @private
|
||||
*/
|
||||
|
||||
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');
|
||||
@@ -32,6 +34,7 @@ var extname = path.extname;
|
||||
var mime = send.mime;
|
||||
var resolve = path.resolve;
|
||||
var vary = require('vary');
|
||||
var urlParse = require('url').parse;
|
||||
|
||||
/**
|
||||
* Response prototype.
|
||||
@@ -63,6 +66,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;
|
||||
};
|
||||
@@ -95,7 +101,7 @@ res.links = function(links){
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.send(new Buffer('wahoo'));
|
||||
* res.send(Buffer.from('wahoo'));
|
||||
* res.send({ some: 'json' });
|
||||
* res.send('<p>some html</p>');
|
||||
*
|
||||
@@ -106,7 +112,6 @@ res.links = function(links){
|
||||
res.send = function send(body) {
|
||||
var chunk = body;
|
||||
var encoding;
|
||||
var len;
|
||||
var req = this.req;
|
||||
var type;
|
||||
|
||||
@@ -135,7 +140,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) {
|
||||
@@ -171,23 +176,33 @@ res.send = function send(body) {
|
||||
}
|
||||
}
|
||||
|
||||
// determine if ETag should be generated
|
||||
var etagFn = app.get('etag fn')
|
||||
var generateETag = !this.get('ETag') && typeof etagFn === 'function'
|
||||
|
||||
// populate Content-Length
|
||||
var len
|
||||
if (chunk !== undefined) {
|
||||
if (!Buffer.isBuffer(chunk)) {
|
||||
// convert chunk to Buffer; saves later double conversions
|
||||
chunk = new Buffer(chunk, encoding);
|
||||
if (Buffer.isBuffer(chunk)) {
|
||||
// get length of Buffer
|
||||
len = chunk.length
|
||||
} else if (!generateETag && chunk.length < 1000) {
|
||||
// just calculate length when no ETag + small chunk
|
||||
len = Buffer.byteLength(chunk, encoding)
|
||||
} else {
|
||||
// convert chunk to Buffer and calculate
|
||||
chunk = Buffer.from(chunk, encoding)
|
||||
encoding = undefined;
|
||||
len = chunk.length
|
||||
}
|
||||
|
||||
len = chunk.length;
|
||||
this.set('Content-Length', len);
|
||||
}
|
||||
|
||||
// populate ETag
|
||||
var etag;
|
||||
var generateETag = len !== undefined && app.get('etag fn');
|
||||
if (typeof generateETag === 'function' && !this.get('ETag')) {
|
||||
if ((etag = generateETag(chunk, encoding))) {
|
||||
if (generateETag && len !== undefined) {
|
||||
if ((etag = etagFn(chunk, encoding))) {
|
||||
this.set('ETag', etag);
|
||||
}
|
||||
}
|
||||
@@ -203,6 +218,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();
|
||||
@@ -244,9 +266,10 @@ res.json = function json(obj) {
|
||||
|
||||
// settings
|
||||
var app = this.app;
|
||||
var escape = app.get('json escape')
|
||||
var replacer = app.get('json replacer');
|
||||
var spaces = app.get('json spaces');
|
||||
var body = stringify(val, replacer, spaces);
|
||||
var body = stringify(val, replacer, spaces, escape)
|
||||
|
||||
// content-type
|
||||
if (!this.get('Content-Type')) {
|
||||
@@ -273,9 +296,9 @@ res.jsonp = function jsonp(obj) {
|
||||
|
||||
// allow status / body
|
||||
if (arguments.length === 2) {
|
||||
// res.json(body, status) backwards compat
|
||||
// res.jsonp(body, status) backwards compat
|
||||
if (typeof arguments[1] === 'number') {
|
||||
deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead');
|
||||
deprecate('res.jsonp(obj, status): Use res.status(status).jsonp(obj) instead');
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead');
|
||||
@@ -286,9 +309,10 @@ res.jsonp = function jsonp(obj) {
|
||||
|
||||
// settings
|
||||
var app = this.app;
|
||||
var escape = app.get('json escape')
|
||||
var replacer = app.get('json replacer');
|
||||
var spaces = app.get('json spaces');
|
||||
var body = stringify(val, replacer, spaces);
|
||||
var body = stringify(val, replacer, spaces, escape)
|
||||
var callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
@@ -304,17 +328,21 @@ res.jsonp = function jsonp(obj) {
|
||||
|
||||
// jsonp
|
||||
if (typeof callback === 'string' && callback.length !== 0) {
|
||||
this.charset = 'utf-8';
|
||||
this.set('X-Content-Type-Options', 'nosniff');
|
||||
this.set('Content-Type', 'text/javascript');
|
||||
|
||||
// restrict callback charset
|
||||
callback = callback.replace(/[^\[\]\w$.]/g, '');
|
||||
|
||||
// replace chars not allowed in JavaScript that are in JSON
|
||||
body = body
|
||||
.replace(/\u2028/g, '\\u2028')
|
||||
.replace(/\u2029/g, '\\u2029');
|
||||
if (body === undefined) {
|
||||
// empty argument
|
||||
body = ''
|
||||
} else if (typeof body === 'string') {
|
||||
// replace chars not allowed in JavaScript that are in JSON
|
||||
body = body
|
||||
.replace(/\u2028/g, '\\u2028')
|
||||
.replace(/\u2029/g, '\\u2029')
|
||||
}
|
||||
|
||||
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
|
||||
// the typeof check is just to reduce client error noise
|
||||
@@ -340,7 +368,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');
|
||||
@@ -353,7 +381,7 @@ res.sendStatus = function sendStatus(statusCode) {
|
||||
*
|
||||
* Automatically sets the _Content-Type_ response header field.
|
||||
* The callback `callback(err)` is invoked when the transfer is complete
|
||||
* or when an error occurs. Be sure to check `res.sentHeader`
|
||||
* or when an error occurs. Be sure to check `res.headersSent`
|
||||
* if you wish to attempt responding, as the header and some data
|
||||
* may have already been transferred.
|
||||
*
|
||||
@@ -400,6 +428,10 @@ res.sendFile = function sendFile(path, options, callback) {
|
||||
throw new TypeError('path argument is required to res.sendFile');
|
||||
}
|
||||
|
||||
if (typeof path !== 'string') {
|
||||
throw new TypeError('path must be a string to res.sendFile')
|
||||
}
|
||||
|
||||
// support function as second arg
|
||||
if (typeof options === 'function') {
|
||||
done = options;
|
||||
@@ -431,7 +463,7 @@ res.sendFile = function sendFile(path, options, callback) {
|
||||
*
|
||||
* Automatically sets the _Content-Type_ response header field.
|
||||
* The callback `callback(err)` is invoked when the transfer is complete
|
||||
* or when an error occurs. Be sure to check `res.sentHeader`
|
||||
* or when an error occurs. Be sure to check `res.headersSent`
|
||||
* if you wish to attempt responding, as the header and some data
|
||||
* may have already been transferred.
|
||||
*
|
||||
@@ -489,7 +521,7 @@ res.sendfile = function (path, options, callback) {
|
||||
if (err && err.code === 'EISDIR') return next();
|
||||
|
||||
// next() all but write errors
|
||||
if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') {
|
||||
if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
@@ -504,21 +536,38 @@ 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.
|
||||
*
|
||||
* This method uses `res.sendfile()`.
|
||||
* Optionally providing an `options` object to use with `res.sendFile()`.
|
||||
* This function will set the `Content-Disposition` header, overriding
|
||||
* any `Content-Disposition` header passed as header options in order
|
||||
* to set the attachment and filename.
|
||||
*
|
||||
* This method uses `res.sendFile()`.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.download = function download(path, filename, callback) {
|
||||
res.download = function download (path, filename, options, callback) {
|
||||
var done = callback;
|
||||
var name = filename;
|
||||
var opts = options || null
|
||||
|
||||
// support function as second arg
|
||||
// support function as second or third arg
|
||||
if (typeof filename === 'function') {
|
||||
done = filename;
|
||||
name = null;
|
||||
opts = null
|
||||
} else if (typeof options === 'function') {
|
||||
done = options
|
||||
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
|
||||
@@ -526,10 +575,28 @@ res.download = function download(path, filename, callback) {
|
||||
'Content-Disposition': contentDisposition(name || path)
|
||||
};
|
||||
|
||||
// Resolve the full path for sendFile
|
||||
var fullPath = resolve(path);
|
||||
// merge user-provided headers
|
||||
if (opts && opts.headers) {
|
||||
var keys = Object.keys(opts.headers)
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i]
|
||||
if (key.toLowerCase() !== 'content-disposition') {
|
||||
headers[key] = opts.headers[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.sendFile(fullPath, { headers: headers }, done);
|
||||
// merge user-provided options
|
||||
opts = Object.create(opts)
|
||||
opts.headers = headers
|
||||
|
||||
// Resolve the full path for sendFile
|
||||
var fullPath = !opts.root
|
||||
? resolve(path)
|
||||
: path
|
||||
|
||||
// send file
|
||||
return this.sendFile(fullPath, opts, done)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -582,7 +649,7 @@ res.type = function contentType(type) {
|
||||
* res.send('<p>hey</p>');
|
||||
* },
|
||||
*
|
||||
* 'appliation/json': function(){
|
||||
* 'application/json': function () {
|
||||
* res.send({ message: 'hey' });
|
||||
* }
|
||||
* });
|
||||
@@ -619,9 +686,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)
|
||||
@@ -632,13 +698,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;
|
||||
@@ -685,7 +750,7 @@ res.append = function append(field, val) {
|
||||
// concat the new and prev vals
|
||||
value = Array.isArray(prev) ? prev.concat(val)
|
||||
: Array.isArray(val) ? [prev].concat(val)
|
||||
: [prev, val];
|
||||
: [prev, val]
|
||||
}
|
||||
|
||||
return this.set(field, value);
|
||||
@@ -777,7 +842,7 @@ res.clearCookie = function clearCookie(name, options) {
|
||||
* // "Remember Me" for 15 minutes
|
||||
* res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
*
|
||||
* // save as above
|
||||
* // same as above
|
||||
* res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
|
||||
*
|
||||
* @param {String} name
|
||||
@@ -804,9 +869,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) {
|
||||
@@ -836,15 +905,32 @@ res.cookie = function (name, value, options) {
|
||||
*/
|
||||
|
||||
res.location = function location(url) {
|
||||
var loc = url;
|
||||
var loc = String(url);
|
||||
|
||||
// "back" is an alias for the referrer
|
||||
if (url === 'back') {
|
||||
loc = this.req.get('Referrer') || '/';
|
||||
}
|
||||
|
||||
var lowerLoc = loc.toLowerCase();
|
||||
var encodedUrl = encodeUrl(loc);
|
||||
if (lowerLoc.indexOf('https://') === 0 || lowerLoc.indexOf('http://') === 0) {
|
||||
try {
|
||||
var parsedUrl = urlParse(loc);
|
||||
var parsedEncodedUrl = urlParse(encodedUrl);
|
||||
// Because this can encode the host, check that we did not change the host
|
||||
if (parsedUrl.host !== parsedEncodedUrl.host) {
|
||||
// If the host changes after encodeUrl, return the original url
|
||||
return this.set('Location', loc);
|
||||
}
|
||||
} catch (e) {
|
||||
// If parse fails, return the original url
|
||||
return this.set('Location', loc);
|
||||
}
|
||||
}
|
||||
|
||||
// set location
|
||||
return this.set('Location', encodeUrl(loc));
|
||||
return this.set('Location', encodedUrl);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -887,12 +973,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 <a href="' + u + '">' + u + '</a></p>'
|
||||
},
|
||||
|
||||
default: function(){
|
||||
@@ -1063,14 +1149,39 @@ function sendfile(res, file, options, callback) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify JSON, like JSON.stringify, but v8 optimized.
|
||||
* Stringify JSON, like JSON.stringify, but v8 optimized, with the
|
||||
* ability to escape characters that can trigger HTML sniffing.
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {function} replacer
|
||||
* @param {number} spaces
|
||||
* @param {boolean} escape
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function stringify(value, replacer, spaces) {
|
||||
function stringify (value, replacer, spaces, escape) {
|
||||
// v8 checks arguments.length for optimizing simple call
|
||||
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
|
||||
return replacer || spaces
|
||||
var json = replacer || spaces
|
||||
? JSON.stringify(value, replacer, spaces)
|
||||
: JSON.stringify(value);
|
||||
|
||||
if (escape && typeof json === 'string') {
|
||||
json = json.replace(/[<>&]/g, function (c) {
|
||||
switch (c.charCodeAt(0)) {
|
||||
case 0x3c:
|
||||
return '\\u003c'
|
||||
case 0x3e:
|
||||
return '\\u003e'
|
||||
case 0x26:
|
||||
return '\\u0026'
|
||||
/* istanbul ignore next: unreachable default */
|
||||
default:
|
||||
return c
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ var toString = Object.prototype.toString;
|
||||
/**
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Router} which is an callable function
|
||||
* @param {Object} [options]
|
||||
* @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,19 +279,25 @@ 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.slice(0, layerPath.length)) {
|
||||
next(layerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate path breaks on a path separator
|
||||
var c = path[layerPath.length]
|
||||
if (c && c !== '/' && c !== '.') return next(layerError)
|
||||
@@ -295,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] !== '/') {
|
||||
@@ -448,14 +459,14 @@ proto.use = function use(fn) {
|
||||
var callbacks = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (callbacks.length === 0) {
|
||||
throw new TypeError('Router.use() requires middleware functions');
|
||||
throw new TypeError('Router.use() requires a middleware function')
|
||||
}
|
||||
|
||||
for (var i = 0; i < callbacks.length; i++) {
|
||||
var fn = callbacks[i];
|
||||
|
||||
if (typeof fn !== 'function') {
|
||||
throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
|
||||
throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))
|
||||
}
|
||||
|
||||
// add the middleware
|
||||
@@ -541,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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -175,7 +189,7 @@ Route.prototype.all = function all() {
|
||||
|
||||
if (typeof handle !== 'function') {
|
||||
var type = toString.call(handle);
|
||||
var msg = 'Route.all() requires callback functions but got a ' + type;
|
||||
var msg = 'Route.all() requires a callback function but got a ' + type
|
||||
throw new TypeError(msg);
|
||||
}
|
||||
|
||||
@@ -198,7 +212,7 @@ methods.forEach(function(method){
|
||||
|
||||
if (typeof handle !== 'function') {
|
||||
var type = toString.call(handle);
|
||||
var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
|
||||
var msg = 'Route.' + method + '() requires a callback function but got a ' + type
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
|
||||
53
lib/utils.js
53
lib/utils.js
@@ -12,6 +12,7 @@
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var contentDisposition = require('content-disposition');
|
||||
var contentType = require('content-type');
|
||||
var deprecate = require('depd')('express');
|
||||
@@ -31,13 +32,7 @@ var querystring = require('querystring');
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.etag = function (body, encoding) {
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
: body;
|
||||
|
||||
return etag(buf, {weak: false});
|
||||
};
|
||||
exports.etag = createETagGenerator({ weak: false })
|
||||
|
||||
/**
|
||||
* Return weak ETag for `body`.
|
||||
@@ -48,13 +43,7 @@ exports.etag = function (body, encoding) {
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.wetag = function wetag(body, encoding){
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
: body;
|
||||
|
||||
return etag(buf, {weak: true});
|
||||
};
|
||||
exports.wetag = createETagGenerator({ weak: true })
|
||||
|
||||
/**
|
||||
* Check if `path` looks absolute.
|
||||
@@ -128,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(/ *= */);
|
||||
@@ -168,6 +156,7 @@ exports.compileETag = function(val) {
|
||||
|
||||
switch (val) {
|
||||
case true:
|
||||
case 'weak':
|
||||
fn = exports.wetag;
|
||||
break;
|
||||
case false:
|
||||
@@ -175,9 +164,6 @@ exports.compileETag = function(val) {
|
||||
case 'strong':
|
||||
fn = exports.etag;
|
||||
break;
|
||||
case 'weak':
|
||||
fn = exports.wetag;
|
||||
break;
|
||||
default:
|
||||
throw new TypeError('unknown value for etag function: ' + val);
|
||||
}
|
||||
@@ -202,6 +188,7 @@ exports.compileQueryParser = function compileQueryParser(val) {
|
||||
|
||||
switch (val) {
|
||||
case true:
|
||||
case 'simple':
|
||||
fn = querystring.parse;
|
||||
break;
|
||||
case false:
|
||||
@@ -210,9 +197,6 @@ exports.compileQueryParser = function compileQueryParser(val) {
|
||||
case 'extended':
|
||||
fn = parseExtendedQueryString;
|
||||
break;
|
||||
case 'simple':
|
||||
fn = querystring.parse;
|
||||
break;
|
||||
default:
|
||||
throw new TypeError('unknown value for query parser function: ' + val);
|
||||
}
|
||||
@@ -243,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 || []);
|
||||
@@ -273,9 +258,29 @@ exports.setCharset = function setCharset(type, charset) {
|
||||
return contentType.format(parsed);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ETag generator function, generating ETags with
|
||||
* the given options.
|
||||
*
|
||||
* @param {object} options
|
||||
* @return {function}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function createETagGenerator (options) {
|
||||
return function generateETag (body, encoding) {
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? Buffer.from(body, encoding)
|
||||
: body
|
||||
|
||||
return etag(buf, options)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an extended query string with qs.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
|
||||
12
lib/view.js
12
lib/view.js
@@ -74,9 +74,17 @@ 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)
|
||||
opts.engines[this.ext] = require(mod).__express
|
||||
|
||||
// default engine export
|
||||
var fn = require(mod).__express
|
||||
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error('Module "' + mod + '" does not provide a view engine.')
|
||||
}
|
||||
|
||||
opts.engines[this.ext] = fn
|
||||
}
|
||||
|
||||
// store loaded engine
|
||||
|
||||
83
package.json
83
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Fast, unopinionated, minimalist web framework",
|
||||
"version": "4.15.4",
|
||||
"version": "4.19.1",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
|
||||
@@ -20,6 +20,7 @@
|
||||
"framework",
|
||||
"sinatra",
|
||||
"web",
|
||||
"http",
|
||||
"rest",
|
||||
"restful",
|
||||
"router",
|
||||
@@ -27,54 +28,54 @@
|
||||
"api"
|
||||
],
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.3",
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"content-disposition": "0.5.2",
|
||||
"content-type": "~1.0.2",
|
||||
"cookie": "0.3.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.8",
|
||||
"depd": "~1.1.1",
|
||||
"encodeurl": "~1.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.0",
|
||||
"finalhandler": "~1.0.4",
|
||||
"fresh": "0.5.0",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.1",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~1.1.5",
|
||||
"qs": "6.5.0",
|
||||
"range-parser": "~1.2.0",
|
||||
"send": "0.15.4",
|
||||
"serve-static": "1.12.4",
|
||||
"setprototypeof": "1.0.3",
|
||||
"statuses": "~1.3.1",
|
||||
"type-is": "~1.6.15",
|
||||
"utils-merge": "1.0.0",
|
||||
"vary": "~1.1.1"
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.11.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.18.0",
|
||||
"serve-static": "1.15.0",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.2",
|
||||
"body-parser": "1.17.2",
|
||||
"cookie-parser": "~1.4.3",
|
||||
"cookie-session": "1.3.0",
|
||||
"ejs": "2.5.7",
|
||||
"eslint": "2.13.1",
|
||||
"express-session": "1.15.5",
|
||||
"hbs": "4.0.1",
|
||||
"istanbul": "0.4.5",
|
||||
"marked": "0.3.6",
|
||||
"method-override": "2.3.9",
|
||||
"mocha": "3.5.0",
|
||||
"morgan": "1.8.2",
|
||||
"multiparty": "4.1.3",
|
||||
"connect-redis": "3.4.2",
|
||||
"cookie-parser": "1.4.6",
|
||||
"cookie-session": "2.0.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.2.0",
|
||||
"morgan": "1.10.0",
|
||||
"nyc": "15.1.0",
|
||||
"pbkdf2-password": "1.2.1",
|
||||
"should": "11.2.1",
|
||||
"supertest": "1.2.0",
|
||||
"connect-redis": "~2.4.1",
|
||||
"supertest": "6.3.0",
|
||||
"vhost": "~3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -90,8 +91,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 --reporter=lcovonly --reporter=text npm test",
|
||||
"test-cov": "nyc --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);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user