mirror of
https://github.com/expressjs/express.git
synced 2026-02-27 03:07:33 +00:00
Compare commits
573 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4ab7d65d7 | ||
|
|
c4cc78bdf5 | ||
|
|
925a1dff1e | ||
|
|
9c85a25c02 | ||
|
|
1140301f6a | ||
|
|
c76ed5ae05 | ||
|
|
2d4192ebb3 | ||
|
|
66404b347a | ||
|
|
d12772393c | ||
|
|
6b7ccfcf12 | ||
|
|
c9ecf7b658 | ||
|
|
a479419b16 | ||
|
|
5a4568abfe | ||
|
|
912893c07c | ||
|
|
ae265a90c7 | ||
|
|
9a3f7ff412 | ||
|
|
2cd372e34c | ||
|
|
04d3a49976 | ||
|
|
bc7d155f53 | ||
|
|
00bb633ca6 | ||
|
|
3c0ad4e8dc | ||
|
|
4ae96bdf5e | ||
|
|
6cd404eb28 | ||
|
|
3e81873b52 | ||
|
|
b5aae87594 | ||
|
|
b8fc000f31 | ||
|
|
c2fb76e99f | ||
|
|
9eb700151b | ||
|
|
dbac741a49 | ||
|
|
697547cde6 | ||
|
|
4007ad103b | ||
|
|
2f64f68c37 | ||
|
|
ed0ba3f1dc | ||
|
|
8eace4603c | ||
|
|
30bae81027 | ||
|
|
758d4355d4 | ||
|
|
77bcd5274a | ||
|
|
f33caf1f89 | ||
|
|
54af593b73 | ||
|
|
2551a7d8af | ||
|
|
4453d83cca | ||
|
|
db507669ca | ||
|
|
374fc1a0f9 | ||
|
|
1b196c8b82 | ||
|
|
64e7373d69 | ||
|
|
e4fb370ad8 | ||
|
|
60d4c16cc9 | ||
|
|
9e6760e186 | ||
|
|
ffa89f2ccf | ||
|
|
b9b9f52b2f | ||
|
|
9a7afb2886 | ||
|
|
2eb42059f3 | ||
|
|
aa907945cd | ||
|
|
89f198c6a5 | ||
|
|
d9a62f9833 | ||
|
|
8f21493cc5 | ||
|
|
6616e39d4d | ||
|
|
ed64290e4a | ||
|
|
b52ff7ca60 | ||
|
|
9420cd3f9b | ||
|
|
ef5f2e13ef | ||
|
|
c5b8d55a6a | ||
|
|
7a9311216a | ||
|
|
b0ed15b452 | ||
|
|
3910323d09 | ||
|
|
98c85eb0dd | ||
|
|
a039e49175 | ||
|
|
ffc562c7d1 | ||
|
|
52872b84ca | ||
|
|
b8ab46594d | ||
|
|
fedd60e642 | ||
|
|
99a0bd3354 | ||
|
|
dfd1851245 | ||
|
|
9f4dbe3a13 | ||
|
|
9784321e89 | ||
|
|
ee1ef41bd3 | ||
|
|
1ca803dd55 | ||
|
|
73555815b9 | ||
|
|
a1161b4686 | ||
|
|
f9954dd317 | ||
|
|
5da5a11a49 | ||
|
|
3dc96995df | ||
|
|
fa40ecfe76 | ||
|
|
cd7d4397c3 | ||
|
|
4c4f3ea105 | ||
|
|
cb4c56e9a7 | ||
|
|
7b44e1d850 | ||
|
|
eb6d12587a | ||
|
|
f1a2dc884d | ||
|
|
6b51e8ef97 | ||
|
|
1f311c59d4 | ||
|
|
9e97144222 | ||
|
|
29d09803c1 | ||
|
|
1d63162dbf | ||
|
|
4a2175dfc9 | ||
|
|
0bb00e1906 | ||
|
|
1e359f57fc | ||
|
|
9cbe2c2cbb | ||
|
|
35e15362ab | ||
|
|
90e522ac90 | ||
|
|
59703c2321 | ||
|
|
caa4f68ee8 | ||
|
|
6ed3439584 | ||
|
|
327af123a1 | ||
|
|
d2de128a32 | ||
|
|
2a53336e5d | ||
|
|
a42413d4e3 | ||
|
|
c2f576cbe9 | ||
|
|
99473c593a | ||
|
|
2d589b644a | ||
|
|
85e48bb8c1 | ||
|
|
55869f49a6 | ||
|
|
af7cd90893 | ||
|
|
ae6a4621bc | ||
|
|
8d39345902 | ||
|
|
a5cb681eb8 | ||
|
|
511d9dfca8 | ||
|
|
7f13d572c1 | ||
|
|
62336717bf | ||
|
|
3bbffdc41c | ||
|
|
ff86319ed5 | ||
|
|
1c5cf0fead | ||
|
|
256a3d1527 | ||
|
|
4f952a953b | ||
|
|
41113599af | ||
|
|
6a40af8293 | ||
|
|
246f6f5aee | ||
|
|
b11122be85 | ||
|
|
43020ff275 | ||
|
|
e4a61bd88e | ||
|
|
39f5d633b5 | ||
|
|
52ed64606f | ||
|
|
4e92ac9031 | ||
|
|
9f8589e31c | ||
|
|
cc751cff8f | ||
|
|
805ef52ae6 | ||
|
|
9e3dbb4374 | ||
|
|
b31910c542 | ||
|
|
c70197ad33 | ||
|
|
8cb53ea5c3 | ||
|
|
e162764f0f | ||
|
|
508c74091f | ||
|
|
b274047a5d | ||
|
|
082d6d1253 | ||
|
|
94546a3cc5 | ||
|
|
ab02240336 | ||
|
|
a46cfdc37f | ||
|
|
d14b2de782 | ||
|
|
2027b87a27 | ||
|
|
2cbf22721d | ||
|
|
3e1a1cedb2 | ||
|
|
6340d1509f | ||
|
|
344b022fc7 | ||
|
|
0c49926a9b | ||
|
|
b3906cbdde | ||
|
|
fed8c2a885 | ||
|
|
bdd81f8670 | ||
|
|
6c98f80b6a | ||
|
|
f9256ef36f | ||
|
|
e5feb9fcc9 | ||
|
|
21df421ebc | ||
|
|
4c9ddc1c47 | ||
|
|
9ebe5d500d | ||
|
|
ec4a01b6b8 | ||
|
|
54271f69b5 | ||
|
|
0264908903 | ||
|
|
4d713d2b76 | ||
|
|
125bb742a3 | ||
|
|
accafc652e | ||
|
|
05f40f4321 | ||
|
|
402e7f653f | ||
|
|
4e61d0100d | ||
|
|
7748475747 | ||
|
|
91a58b5b03 | ||
|
|
13e6894393 | ||
|
|
65b62065d2 | ||
|
|
2a980ad160 | ||
|
|
0b243b1aee | ||
|
|
a3e7e05e0a | ||
|
|
c5addb9a17 | ||
|
|
e35380a39d | ||
|
|
f5b6e67aed | ||
|
|
09831580ec | ||
|
|
41c054cff1 | ||
|
|
ecf762ff38 | ||
|
|
63992bb1d7 | ||
|
|
ea49706052 | ||
|
|
dde1f7d6e8 | ||
|
|
82fc12a40b | ||
|
|
9c756b0105 | ||
|
|
160b91cbf7 | ||
|
|
d106bf5324 | ||
|
|
723b5451bb | ||
|
|
2177f67f54 | ||
|
|
f4bd86ed36 | ||
|
|
c96c690dc0 | ||
|
|
088856c3f8 | ||
|
|
2ec589c113 | ||
|
|
4cf7eed927 | ||
|
|
6d084715ba | ||
|
|
61421a8c0c | ||
|
|
f42b160bbc | ||
|
|
689073d657 | ||
|
|
2803a2b35a | ||
|
|
ee40a881f5 | ||
|
|
a7d6d29ed3 | ||
|
|
897290b685 | ||
|
|
700349ffaf | ||
|
|
4b9cd2fd0e | ||
|
|
b44191eb3d | ||
|
|
8417c60fcf | ||
|
|
bf91946bd4 | ||
|
|
26801a0afd | ||
|
|
14439731f9 | ||
|
|
d97d79ed9a | ||
|
|
26e53f0fbc | ||
|
|
6abec204c0 | ||
|
|
4b3b8cc231 | ||
|
|
e9bcdd399b | ||
|
|
815f799310 | ||
|
|
7f9e5843b9 | ||
|
|
93cf646d5c | ||
|
|
2676a1f281 | ||
|
|
6da57c7819 | ||
|
|
d546f93f2f | ||
|
|
4771ba2bc3 | ||
|
|
3ae704f67f | ||
|
|
8b6d34963d | ||
|
|
36b8148110 | ||
|
|
6d98d2e110 | ||
|
|
51a76366e3 | ||
|
|
4e3f95c0ea | ||
|
|
88bd6d8e3a | ||
|
|
51595d402b | ||
|
|
94669f9289 | ||
|
|
cd7d79f92a | ||
|
|
5e2345e966 | ||
|
|
6415f7035b | ||
|
|
b28db2c12c | ||
|
|
0b746953c4 | ||
|
|
04bc62787b | ||
|
|
da4d763ff6 | ||
|
|
7091ec17f0 | ||
|
|
416ba025a1 | ||
|
|
60fb1d2acd | ||
|
|
e9f9aaeebd | ||
|
|
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 | ||
|
|
318fd4b543 | ||
|
|
3d7fce56a3 | ||
|
|
f9063712e0 | ||
|
|
6381bc6317 | ||
|
|
a007863096 | ||
|
|
6faf26d59f | ||
|
|
e98f5848a0 | ||
|
|
a65913776d | ||
|
|
5213bd9fe7 | ||
|
|
a39e409cf3 | ||
|
|
82de4de5ab | ||
|
|
669c805615 | ||
|
|
620df0e35e | ||
|
|
f6db4ee805 | ||
|
|
12310c5294 | ||
|
|
884657d546 | ||
|
|
7511d08328 | ||
|
|
a0276c6c91 | ||
|
|
2585f209f9 | ||
|
|
9d0976229d | ||
|
|
43cc56eb9e | ||
|
|
1c7bbcc143 | ||
|
|
9cbbc8ae74 | ||
|
|
6fbc269563 | ||
|
|
2bc734aa3f | ||
|
|
89bb531b31 | ||
|
|
744564fcf8 | ||
|
|
da6cb0ed8a | ||
|
|
00ad5bee96 | ||
|
|
141914e817 | ||
|
|
bd4fdfe5f7 | ||
|
|
215f484fb4 | ||
|
|
20047bb6e4 | ||
|
|
8b9757e8b8 | ||
|
|
a84e73b958 | ||
|
|
69997cbdbe | ||
|
|
3d05e85b0c | ||
|
|
c221b8596e | ||
|
|
450c468d04 | ||
|
|
af341b0f09 | ||
|
|
1574925cad | ||
|
|
c7d528cdc0 | ||
|
|
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 | ||
|
|
8aabecaf1f | ||
|
|
3ff1dbeb73 | ||
|
|
18da651c5b | ||
|
|
1b48a5cc3c | ||
|
|
561b4b601e | ||
|
|
67e64ca4c1 | ||
|
|
bd04d8a87f | ||
|
|
922e9a4615 | ||
|
|
323a38965a | ||
|
|
3f1dcb96e0 | ||
|
|
4b4fa26298 | ||
|
|
47c1d2a816 | ||
|
|
65aff94ec6 | ||
|
|
2d519077ea | ||
|
|
d967675852 | ||
|
|
22d5b7ed10 | ||
|
|
872aa4741c | ||
|
|
87bc4ef763 | ||
|
|
f0cbdeadf6 | ||
|
|
f1e8a877f4 | ||
|
|
55831bbd08 | ||
|
|
e757fa0039 | ||
|
|
95735a6fcc | ||
|
|
668d029a14 | ||
|
|
866ffd67d7 | ||
|
|
741e3f81af | ||
|
|
4efb49866d | ||
|
|
6506fb578c | ||
|
|
dfa7b80642 | ||
|
|
121fe9982b | ||
|
|
5f0c829d7c | ||
|
|
c82fa19447 | ||
|
|
fa22245cc6 | ||
|
|
302a6152b4 | ||
|
|
659fcc1598 | ||
|
|
a163e2cdf4 | ||
|
|
62e12fe710 | ||
|
|
8fabed82aa | ||
|
|
f4120a6453 | ||
|
|
19c8d64855 | ||
|
|
71395f5933 | ||
|
|
e3bd14dcca | ||
|
|
c319fe260a | ||
|
|
21f725e0ef | ||
|
|
e5dbb0cb4e | ||
|
|
a3a9166c52 | ||
|
|
06f423d4f5 | ||
|
|
501e24e0a9 | ||
|
|
c8d9223e93 | ||
|
|
4b39a01e6a | ||
|
|
ad4d52de29 | ||
|
|
07077c4457 | ||
|
|
bcbb9d56c5 | ||
|
|
ab1c9e924e | ||
|
|
25fdefac45 | ||
|
|
a856456a95 | ||
|
|
1dbfee6623 | ||
|
|
8a387d3ede | ||
|
|
943f28f05f | ||
|
|
7cafdb5824 | ||
|
|
2c668f87c7 | ||
|
|
6343288bef | ||
|
|
694869d2aa | ||
|
|
cec5780db4 | ||
|
|
21d52daafb | ||
|
|
a7d15f382e | ||
|
|
6c751191dd | ||
|
|
1e2951a832 | ||
|
|
3a1f27fcde | ||
|
|
b309b873f1 | ||
|
|
f90e045334 | ||
|
|
f6ec710534 | ||
|
|
a9ef9e13fb | ||
|
|
f56e2a2503 | ||
|
|
8a5ecd3d89 | ||
|
|
4052c15c7f | ||
|
|
97ccc52207 | ||
|
|
0fc4f0735a | ||
|
|
ccdbe4ea37 | ||
|
|
59f2b4048a | ||
|
|
7f2532808a | ||
|
|
be35e4927c | ||
|
|
f31dcff10c | ||
|
|
509ebb1aff | ||
|
|
78e50547f1 | ||
|
|
dcc4eaabe8 | ||
|
|
8c6f9c4253 | ||
|
|
88103063fe | ||
|
|
164638b24f | ||
|
|
e66625be50 | ||
|
|
085a29685a |
@@ -1,4 +1,4 @@
|
||||
# http://editorconfig.org
|
||||
# https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
root: true
|
||||
|
||||
env:
|
||||
es2022: true
|
||||
node: true
|
||||
rules:
|
||||
eol-last: error
|
||||
eqeqeq: [error, allow-null]
|
||||
indent: [error, 2, { SwitchCase: 1 }]
|
||||
indent: [error, 2, { MemberExpression: "off", SwitchCase: 1 }]
|
||||
no-trailing-spaces: error
|
||||
no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }]
|
||||
no-restricted-globals:
|
||||
- error
|
||||
- name: Buffer
|
||||
message: Use `import { Buffer } from "node:buffer"` instead of the global Buffer.
|
||||
|
||||
17
.github/dependabot.yml
vendored
Normal file
17
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "23:00"
|
||||
timezone: Europe/London
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
117
.github/workflows/ci.yml
vendored
Normal file
117
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- '4.x'
|
||||
- '5.x'
|
||||
- '5.0'
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Cancel in progress workflows
|
||||
# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install --ignore-scripts --include=dev
|
||||
|
||||
- name: Run lint
|
||||
run: npm run lint
|
||||
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node-version: [18, 19, 20, 21, 22, 23, 24, 25]
|
||||
# Node.js release schedule: https://nodejs.org/en/about/releases/
|
||||
|
||||
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Configure npm loglevel
|
||||
run: |
|
||||
npm config set loglevel error
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Output Node and NPM versions
|
||||
run: |
|
||||
echo "Node.js version: $(node -v)"
|
||||
echo "NPM version: $(npm -v)"
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: npm run test-ci
|
||||
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
|
||||
path: ./coverage/lcov.info
|
||||
retention-days: 1
|
||||
|
||||
coverage:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install lcov
|
||||
shell: bash
|
||||
run: sudo apt-get -y install lcov
|
||||
|
||||
- name: Collect coverage reports
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
path: ./coverage
|
||||
pattern: coverage-node-*
|
||||
|
||||
- name: Merge coverage reports
|
||||
shell: bash
|
||||
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
|
||||
with:
|
||||
file: ./lcov.info
|
||||
74
.github/workflows/codeql.yml
vendored
Normal file
74
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ["master"]
|
||||
schedule:
|
||||
- cron: "0 0 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [javascript, actions]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config: |
|
||||
paths-ignore:
|
||||
- test
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
# - name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
101
.github/workflows/legacy.yml
vendored
Normal file
101
.github/workflows/legacy.yml
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
name: legacy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- '4.x'
|
||||
- '5.x'
|
||||
- '5.0'
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Cancel in progress workflows
|
||||
# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node-version: [16, 17]
|
||||
# Node.js release schedule: https://nodejs.org/en/about/releases/
|
||||
|
||||
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Configure npm loglevel
|
||||
run: |
|
||||
npm config set loglevel error
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Output Node and NPM versions
|
||||
run: |
|
||||
echo "Node.js version: $(node -v)"
|
||||
echo "NPM version: $(npm -v)"
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: npm run test-ci
|
||||
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
|
||||
path: ./coverage/lcov.info
|
||||
retention-days: 1
|
||||
|
||||
coverage:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install lcov
|
||||
shell: bash
|
||||
run: sudo apt-get -y install lcov
|
||||
|
||||
- name: Collect coverage reports
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
path: ./coverage
|
||||
pattern: coverage-node-*
|
||||
|
||||
- name: Merge coverage reports
|
||||
shell: bash
|
||||
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
|
||||
with:
|
||||
file: ./lcov.info
|
||||
72
.github/workflows/scorecard.yml
vendored
Normal file
72
.github/workflows/scorecard.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# This workflow uses actions that are not certified by GitHub. They are provided
|
||||
# by a third-party and are governed by separate terms of service, privacy
|
||||
# policy, and support documentation.
|
||||
|
||||
name: Scorecard supply-chain security
|
||||
on:
|
||||
# For Branch-Protection check. Only the default branch is supported. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||
branch_protection_rule:
|
||||
# To guarantee Maintained check is occasionally updated. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||
schedule:
|
||||
- cron: '16 21 * * 1'
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
# Needed to publish results and get a badge (see publish_results below).
|
||||
id-token: write
|
||||
# Uncomment the permissions below if installing in a private repository.
|
||||
# contents: read
|
||||
# actions: read
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
||||
# - you want to enable the Branch-Protection check on a *public* repository, or
|
||||
# - you are installing Scorecard on a *private* repository
|
||||
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
|
||||
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
||||
|
||||
# Public repositories:
|
||||
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||
# - Allows the repository to include the Scorecard badge.
|
||||
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||
# For private repositories:
|
||||
# - `publish_results` will always be set to `false`, regardless
|
||||
# of the value entered here.
|
||||
publish_results: true
|
||||
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
23
.gitignore
vendored
23
.gitignore
vendored
@@ -1,27 +1,20 @@
|
||||
# OS X
|
||||
.DS_Store*
|
||||
Icon?
|
||||
._*
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Linux
|
||||
.directory
|
||||
*~
|
||||
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
package-lock.json
|
||||
npm-shrinkwrap.json
|
||||
*.log
|
||||
*.gz
|
||||
|
||||
# Yarn
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
|
||||
# Coveralls
|
||||
.nyc_output
|
||||
coverage
|
||||
|
||||
# Benchmarking
|
||||
benchmarks/graphs
|
||||
|
||||
# ignore additional files using core.excludesFile
|
||||
# https://git-scm.com/docs/gitignore
|
||||
|
||||
66
.travis.yml
66
.travis.yml
@@ -1,66 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.12"
|
||||
- "1.8"
|
||||
- "2.5"
|
||||
- "3.3"
|
||||
- "4.9"
|
||||
- "5.12"
|
||||
- "6.17"
|
||||
- "7.10"
|
||||
- "8.16"
|
||||
- "9.11"
|
||||
- "10.15"
|
||||
- "11.15"
|
||||
- "12.3"
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "13"
|
||||
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:
|
||||
# Configure npm
|
||||
- |
|
||||
# Skip updating shrinkwrap / lock
|
||||
npm config set shrinkwrap false
|
||||
# Remove all non-test dependencies
|
||||
- |
|
||||
# Remove example dependencies
|
||||
npm rm --silent --save-dev connect-redis
|
||||
# Setup Node.js version-specific dependencies
|
||||
- |
|
||||
# mocha for testing
|
||||
# - use 3.x for Node.js < 6
|
||||
if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then
|
||||
npm install --silent --save-dev mocha@3.5.3
|
||||
fi
|
||||
- |
|
||||
# supertest for http calls
|
||||
# - use 2.0.0 for Node.js < 4
|
||||
if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then
|
||||
npm install --silent --save-dev supertest@2.0.0
|
||||
fi
|
||||
# Update Node.js modules
|
||||
- |
|
||||
# Prune and rebuild node_modules
|
||||
if [[ -d node_modules ]]; then
|
||||
npm prune
|
||||
npm rebuild
|
||||
fi
|
||||
script:
|
||||
# Run test script
|
||||
- npm run test-ci
|
||||
# Run linting
|
||||
- npm run lint
|
||||
after_script:
|
||||
- |
|
||||
# Upload coverage to coveralls
|
||||
npm install --save-dev coveralls@2.12.0
|
||||
coveralls < ./coverage/lcov.info
|
||||
@@ -1,50 +0,0 @@
|
||||
|
||||
## Website Issues
|
||||
|
||||
Open issues for the expressjs.com website in https://github.com/expressjs/expressjs.com.
|
||||
|
||||
## PRs and Code contributions
|
||||
|
||||
* Tests must pass.
|
||||
* Follow the [JavaScript Standard Style](http://standardjs.com/) and `npm run lint`.
|
||||
* If you fix a bug, add a test.
|
||||
|
||||
## Branches
|
||||
|
||||
Use the `master` branch for bug fixes or minor work that is intended for the
|
||||
current release stream.
|
||||
|
||||
Use the correspondingly named branch, e.g. `5.0`, for anything intended for
|
||||
a future release of Express.
|
||||
|
||||
## Steps for contributing
|
||||
|
||||
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
|
||||
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.
|
||||
4. To run the test suite, first install the dependencies by running `npm install`,
|
||||
then run `npm test`.
|
||||
5. Ensure your code is linted by running `npm run lint` -- fix any issue you
|
||||
see listed.
|
||||
6. If the tests pass, you can commit your changes to your fork and then create
|
||||
a pull request from there. Make sure to reference your issue from the pull
|
||||
request comments by including the issue number e.g. `#123`.
|
||||
|
||||
## Issues which are questions
|
||||
|
||||
We will typically close any vague issues or questions that are specific to some
|
||||
app you are writing. Please double check the docs and other references before
|
||||
being trigger happy with posting a question issue.
|
||||
|
||||
Things that will help get your question issue looked at:
|
||||
|
||||
* Full and runnable JS code.
|
||||
* Clear description of the problem or unexpected behavior.
|
||||
* Clear description of the expected result.
|
||||
* Steps you have taken to debug it yourself.
|
||||
|
||||
If you post a question and do not outline the above items or make it easy for
|
||||
us to understand and reproduce your issue, it will be closed.
|
||||
@@ -1,85 +0,0 @@
|
||||
# Express.js Community Contributing Guide 1.0
|
||||
|
||||
The goal of this document is to create a contribution process that:
|
||||
|
||||
* Encourages new contributions.
|
||||
* Encourages contributors to remain involved.
|
||||
* Avoids unnecessary processes and bureaucracy whenever possible.
|
||||
* Creates a transparent decision making process that makes it clear how
|
||||
contributors can be involved in decision making.
|
||||
|
||||
## Vocabulary
|
||||
|
||||
* 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 **TC (Technical Committee)** is a group of committers representing the required technical
|
||||
expertise to resolve rare disputes.
|
||||
|
||||
# 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 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.
|
||||
|
||||
Please be courteous and respectful. Every participant is expected to follow the
|
||||
project's Code of Conduct.
|
||||
|
||||
# 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
|
||||
pull requests.
|
||||
|
||||
No pull request can be merged without being reviewed.
|
||||
|
||||
For non-trivial contributions, pull requests should sit for at least 36 hours to ensure that
|
||||
contributors in other timezones have time to review. Consideration should also be given to
|
||||
weekends and other holiday periods to ensure active committers all have reasonable time to
|
||||
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
|
||||
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.
|
||||
|
||||
In the case of an objection being raised in a pull request by another committer, all involved
|
||||
committers should seek to arrive at a consensus by way of addressing concerns being expressed
|
||||
by discussion, compromise on the proposed change, or withdrawal of the proposed change.
|
||||
|
||||
If a contribution is controversial and committers cannot agree about how to get it to land
|
||||
or if it should land then it should be escalated to the TC. TC members should regularly
|
||||
discuss pending contributions in order to find a resolution. It is expected that only a
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
420
History.md
420
History.md
@@ -1,3 +1,413 @@
|
||||
# Unreleased Changes
|
||||
|
||||
## 🚀 Improvements
|
||||
|
||||
* Improve HTML structure in `res.redirect()` responses when HTML format is accepted by adding `<!DOCTYPE html>`, `<title>`, and `<body>` tags for better browser compatibility - by [@Bernice55231](https://github.com/Bernice55231) in [#5167](https://github.com/expressjs/express/pull/5167)
|
||||
|
||||
* When calling `app.render` with options set to null, the locals object is handled correctly, preventing unexpected errors and making the method behave the same as when options is omitted or an empty object is passed - by [AkaHarshit](https://github.com/AkaHarshit) in [#6903](https://github.com/expressjs/express/pull/6903)
|
||||
|
||||
```js
|
||||
app.render('index', null, callback); // now works as expected
|
||||
```
|
||||
|
||||
## ⚡ Performance
|
||||
|
||||
* Avoid duplicate Content-Type header processing in `res.send()` when sending string responses without an explicit Content-Type header - by [@bjohansebas](https://github.com/bjohansebas) in [#6991](https://github.com/expressjs/express/pull/6991)
|
||||
|
||||
5.2.1 / 2025-12-01
|
||||
=======================
|
||||
|
||||
* Revert security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))
|
||||
* The prior release (5.2.0) included an erroneous breaking change related to the extended query parser. There is no actual security vulnerability associated with this behavior (CVE-2024-51999 has been rejected). The change has been fully reverted in this release.
|
||||
|
||||
5.2.0 / 2025-12-01
|
||||
========================
|
||||
|
||||
* Security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))
|
||||
* deps: `body-parser@^2.2.1`
|
||||
* A deprecation warning was added when using `res.redirect` with undefined arguments, Express now emits a warning to help detect calls that pass undefined as the status or URL and make them easier to fix.
|
||||
|
||||
5.1.0 / 2025-03-31
|
||||
========================
|
||||
|
||||
* Add support for `Uint8Array` in `res.send()`
|
||||
* Add support for ETag option in `res.sendFile()`
|
||||
* Add support for multiple links with the same rel in `res.links()`
|
||||
* Add funding field to package.json
|
||||
* perf: use loop for acceptParams
|
||||
* refactor: prefix built-in node module imports
|
||||
* deps: remove `setprototypeof`
|
||||
* deps: remove `safe-buffer`
|
||||
* deps: remove `utils-merge`
|
||||
* deps: remove `methods`
|
||||
* deps: remove `depd`
|
||||
* deps: `debug@^4.4.0`
|
||||
* deps: `body-parser@^2.2.0`
|
||||
* deps: `router@^2.2.0`
|
||||
* deps: `content-type@^1.0.5`
|
||||
* deps: `finalhandler@^2.1.0`
|
||||
* deps: `qs@^6.14.0`
|
||||
* deps: `server-static@2.2.0`
|
||||
* deps: `type-is@2.0.1`
|
||||
|
||||
5.0.1 / 2024-10-08
|
||||
==========
|
||||
|
||||
* Update `cookie` semver lock to address [CVE-2024-47764](https://nvd.nist.gov/vuln/detail/CVE-2024-47764)
|
||||
|
||||
5.0.0 / 2024-09-10
|
||||
=========================
|
||||
* remove:
|
||||
- `path-is-absolute` dependency - use `path.isAbsolute` instead
|
||||
* breaking:
|
||||
* `res.status()` accepts only integers, and input must be greater than 99 and less than 1000
|
||||
* will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range
|
||||
* will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs
|
||||
* deps: send@1.0.0
|
||||
* `res.redirect('back')` and `res.location('back')` is no longer a supported magic string, explicitly use `req.get('Referrer') || '/'`.
|
||||
* change:
|
||||
- `res.clearCookie` will ignore user provided `maxAge` and `expires` options
|
||||
* deps: cookie-signature@^1.2.1
|
||||
* deps: debug@4.3.6
|
||||
* deps: merge-descriptors@^2.0.0
|
||||
* deps: serve-static@^2.1.0
|
||||
* deps: qs@6.13.0
|
||||
* deps: accepts@^2.0.0
|
||||
* deps: mime-types@^3.0.0
|
||||
- `application/javascript` => `text/javascript`
|
||||
* deps: type-is@^2.0.0
|
||||
* deps: content-disposition@^1.0.0
|
||||
* deps: finalhandler@^2.0.0
|
||||
* deps: fresh@^2.0.0
|
||||
* deps: body-parser@^2.0.1
|
||||
* deps: send@^1.1.0
|
||||
|
||||
5.0.0-beta.3 / 2024-03-25
|
||||
=========================
|
||||
|
||||
This incorporates all changes after 4.19.1 up to 4.19.2.
|
||||
|
||||
5.0.0-beta.2 / 2024-03-20
|
||||
=========================
|
||||
|
||||
This incorporates all changes after 4.17.2 up to 4.19.1.
|
||||
|
||||
5.0.0-beta.1 / 2022-02-14
|
||||
=========================
|
||||
|
||||
This is the first Express 5.0 beta release, based off 4.17.2 and includes
|
||||
changes from 5.0.0-alpha.8.
|
||||
|
||||
* change:
|
||||
- Default "query parser" setting to `'simple'`
|
||||
- Requires Node.js 4+
|
||||
- Use `mime-types` for file to content type mapping
|
||||
* deps: array-flatten@3.0.0
|
||||
* deps: body-parser@2.0.0-beta.1
|
||||
- `req.body` is no longer always initialized to `{}`
|
||||
- `urlencoded` parser now defaults `extended` to `false`
|
||||
- Use `on-finished` to determine when body read
|
||||
* deps: router@2.0.0-beta.1
|
||||
- Add new `?`, `*`, and `+` parameter modifiers
|
||||
- Internalize private `router.process_params` method
|
||||
- Matching group expressions are only RegExp syntax
|
||||
- Named matching groups no longer available by position in `req.params`
|
||||
- Regular expressions can only be used in a matching group
|
||||
- Remove `debug` dependency
|
||||
- Special `*` path segment behavior removed
|
||||
- deps: array-flatten@3.0.0
|
||||
- deps: parseurl@~1.3.3
|
||||
- deps: path-to-regexp@3.2.0
|
||||
- deps: setprototypeof@1.2.0
|
||||
* deps: send@1.0.0-beta.1
|
||||
- Change `dotfiles` option default to `'ignore'`
|
||||
- Remove `hidden` option; use `dotfiles` option instead
|
||||
- Use `mime-types` for file to content type mapping
|
||||
- deps: debug@3.1.0
|
||||
* deps: serve-static@2.0.0-beta.1
|
||||
- Change `dotfiles` option default to `'ignore'`
|
||||
- Remove `hidden` option; use `dotfiles` option instead
|
||||
- Use `mime-types` for file to content type mapping
|
||||
- Remove `express.static.mime` export; use `mime-types` package instead
|
||||
- deps: send@1.0.0-beta.1
|
||||
|
||||
5.0.0-alpha.8 / 2020-03-25
|
||||
==========================
|
||||
|
||||
This is the eighth Express 5.0 alpha release, based off 4.17.1 and includes
|
||||
changes from 5.0.0-alpha.7.
|
||||
|
||||
5.0.0-alpha.7 / 2018-10-26
|
||||
==========================
|
||||
|
||||
This is the seventh Express 5.0 alpha release, based off 4.16.4 and includes
|
||||
changes from 5.0.0-alpha.6.
|
||||
|
||||
The major change with this alpha is the basic support for returned, rejected
|
||||
Promises in the router.
|
||||
|
||||
* remove:
|
||||
- `path-to-regexp` dependency
|
||||
* deps: debug@3.1.0
|
||||
- Add `DEBUG_HIDE_DATE` environment variable
|
||||
- Change timer to per-namespace instead of global
|
||||
- Change non-TTY date format
|
||||
- Remove `DEBUG_FD` environment variable support
|
||||
- Support 256 namespace colors
|
||||
* deps: router@2.0.0-alpha.1
|
||||
- Add basic support for returned, rejected Promises
|
||||
- Fix JSDoc for `Router` constructor
|
||||
- deps: debug@3.1.0
|
||||
- deps: parseurl@~1.3.2
|
||||
- deps: setprototypeof@1.1.0
|
||||
- deps: utils-merge@1.0.1
|
||||
|
||||
5.0.0-alpha.6 / 2017-09-24
|
||||
==========================
|
||||
|
||||
This is the sixth Express 5.0 alpha release, based off 4.15.5 and includes
|
||||
changes from 5.0.0-alpha.5.
|
||||
|
||||
* remove:
|
||||
- `res.redirect(url, status)` signature - use `res.redirect(status, url)`
|
||||
- `res.send(status, body)` signature - use `res.status(status).send(body)`
|
||||
* deps: router@~1.3.1
|
||||
- deps: debug@2.6.8
|
||||
|
||||
5.0.0-alpha.5 / 2017-03-06
|
||||
==========================
|
||||
|
||||
This is the fifth Express 5.0 alpha release, based off 4.15.2 and includes
|
||||
changes from 5.0.0-alpha.4.
|
||||
|
||||
5.0.0-alpha.4 / 2017-03-01
|
||||
==========================
|
||||
|
||||
This is the fourth Express 5.0 alpha release, based off 4.15.0 and includes
|
||||
changes from 5.0.0-alpha.3.
|
||||
|
||||
* remove:
|
||||
- Remove Express 3.x middleware error stubs
|
||||
* deps: router@~1.3.0
|
||||
- Add `next("router")` to exit from router
|
||||
- Fix case where `router.use` skipped requests routes did not
|
||||
- Skip routing when `req.url` is not set
|
||||
- Use `%o` in path debug to tell types apart
|
||||
- deps: debug@2.6.1
|
||||
- deps: setprototypeof@1.0.3
|
||||
- perf: add fast match path for `*` route
|
||||
|
||||
5.0.0-alpha.3 / 2017-01-28
|
||||
==========================
|
||||
|
||||
This is the third Express 5.0 alpha release, based off 4.14.1 and includes
|
||||
changes from 5.0.0-alpha.2.
|
||||
|
||||
* remove:
|
||||
- `res.json(status, obj)` signature - use `res.status(status).json(obj)`
|
||||
- `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)`
|
||||
- `res.vary()` (no arguments) -- provide a field name as an argument
|
||||
* deps: array-flatten@2.1.1
|
||||
* deps: path-is-absolute@1.0.1
|
||||
* deps: router@~1.1.5
|
||||
- deps: array-flatten@2.0.1
|
||||
- deps: methods@~1.1.2
|
||||
- deps: parseurl@~1.3.1
|
||||
- deps: setprototypeof@1.0.2
|
||||
|
||||
5.0.0-alpha.2 / 2015-07-06
|
||||
==========================
|
||||
|
||||
This is the second Express 5.0 alpha release, based off 4.13.1 and includes
|
||||
changes from 5.0.0-alpha.1.
|
||||
|
||||
* remove:
|
||||
- `app.param(fn)`
|
||||
- `req.param()` -- use `req.params`, `req.body`, or `req.query` instead
|
||||
* change:
|
||||
- `res.render` callback is always async, even for sync view engines
|
||||
- The leading `:` character in `name` for `app.param(name, fn)` is no longer removed
|
||||
- Use `router` module for routing
|
||||
- Use `path-is-absolute` module for absolute path detection
|
||||
|
||||
5.0.0-alpha.1 / 2014-11-06
|
||||
==========================
|
||||
|
||||
This is the first Express 5.0 alpha release, based off 4.10.1.
|
||||
|
||||
* remove:
|
||||
- `app.del` - use `app.delete`
|
||||
- `req.acceptsCharset` - use `req.acceptsCharsets`
|
||||
- `req.acceptsEncoding` - use `req.acceptsEncodings`
|
||||
- `req.acceptsLanguage` - use `req.acceptsLanguages`
|
||||
- `res.json(obj, status)` signature - use `res.json(status, obj)`
|
||||
- `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)`
|
||||
- `res.send(body, status)` signature - use `res.send(status, body)`
|
||||
- `res.send(status)` signature - use `res.sendStatus(status)`
|
||||
- `res.sendfile` - use `res.sendFile` instead
|
||||
- `express.query` middleware
|
||||
* change:
|
||||
- `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname
|
||||
- `req.query` is now a getter instead of a plain property
|
||||
* add:
|
||||
- `app.router` is a reference to the base router
|
||||
|
||||
4.20.0 / 2024-09-10
|
||||
==========
|
||||
* deps: serve-static@0.16.0
|
||||
* Remove link renderization in html while redirecting
|
||||
* deps: send@0.19.0
|
||||
* Remove link renderization in html while redirecting
|
||||
* deps: body-parser@0.6.0
|
||||
* add `depth` option to customize the depth level in the parser
|
||||
* IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`)
|
||||
* Remove link renderization in html while using `res.redirect`
|
||||
* deps: path-to-regexp@0.1.10
|
||||
- Adds support for named matching groups in the routes using a regex
|
||||
- Adds backtracking protection to parameters without regexes defined
|
||||
* deps: encodeurl@~2.0.0
|
||||
- Removes encoding of `\`, `|`, and `^` to align better with URL spec
|
||||
* Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie`
|
||||
- Will be ignored in v5, clearCookie will set a cookie with an expires in the past to instruct clients to delete the cookie
|
||||
|
||||
4.19.2 / 2024-03-25
|
||||
==========
|
||||
|
||||
* Improved fix for open redirect allow list bypass
|
||||
|
||||
4.19.1 / 2024-03-20
|
||||
==========
|
||||
|
||||
* Allow passing non-strings to res.location with new encoding handling checks
|
||||
|
||||
4.19.0 / 2024-03-20
|
||||
==========
|
||||
|
||||
* Prevent open redirect allow list bypass due to encodeurl
|
||||
* deps: cookie@0.6.0
|
||||
|
||||
4.18.3 / 2024-02-29
|
||||
==========
|
||||
|
||||
* Fix routing requests without method
|
||||
* deps: body-parser@1.20.2
|
||||
- Fix strict json error message on Node.js 19+
|
||||
- deps: content-type@~1.0.5
|
||||
- deps: raw-body@2.5.2
|
||||
* deps: cookie@0.6.0
|
||||
- Add `partitioned` option
|
||||
|
||||
4.18.2 / 2022-10-08
|
||||
===================
|
||||
|
||||
* Fix regression routing a large stack in a single route
|
||||
* deps: body-parser@1.20.1
|
||||
- deps: qs@6.11.0
|
||||
- perf: remove unnecessary object clone
|
||||
* deps: qs@6.11.0
|
||||
|
||||
4.18.1 / 2022-04-29
|
||||
===================
|
||||
|
||||
* Fix hanging on large stack of sync routes
|
||||
|
||||
4.18.0 / 2022-04-25
|
||||
===================
|
||||
|
||||
* Add "root" option to `res.download`
|
||||
* Allow `options` without `filename` in `res.download`
|
||||
* Deprecate string and non-integer arguments to `res.status`
|
||||
* Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie`
|
||||
* Fix handling very large stacks of sync middleware
|
||||
* Ignore `Object.prototype` values in settings through `app.set`/`app.get`
|
||||
* Invoke `default` with same arguments as types in `res.format`
|
||||
* Support proper 205 responses using `res.send`
|
||||
* Use `http-errors` for `res.format` error
|
||||
* deps: body-parser@1.20.0
|
||||
- Fix error message for json parse whitespace in `strict`
|
||||
- Fix internal error when inflated body exceeds limit
|
||||
- Prevent loss of async hooks context
|
||||
- Prevent hanging when request already read
|
||||
- deps: depd@2.0.0
|
||||
- deps: http-errors@2.0.0
|
||||
- deps: on-finished@2.4.1
|
||||
- deps: qs@6.10.3
|
||||
- deps: raw-body@2.5.1
|
||||
* deps: cookie@0.5.0
|
||||
- Add `priority` option
|
||||
- Fix `expires` option to reject invalid dates
|
||||
* deps: depd@2.0.0
|
||||
- Replace internal `eval` usage with `Function` constructor
|
||||
- Use instance methods on `process` to check for listeners
|
||||
* deps: finalhandler@1.2.0
|
||||
- Remove set content headers that break response
|
||||
- deps: on-finished@2.4.1
|
||||
- deps: statuses@2.0.1
|
||||
* deps: on-finished@2.4.1
|
||||
- Prevent loss of async hooks context
|
||||
* deps: qs@6.10.3
|
||||
* deps: send@0.18.0
|
||||
- Fix emitted 416 error missing headers property
|
||||
- Limit the headers removed for 304 response
|
||||
- deps: depd@2.0.0
|
||||
- deps: destroy@1.2.0
|
||||
- deps: http-errors@2.0.0
|
||||
- deps: on-finished@2.4.1
|
||||
- deps: statuses@2.0.1
|
||||
* deps: serve-static@1.15.0
|
||||
- deps: send@0.18.0
|
||||
* deps: statuses@2.0.1
|
||||
- Remove code 306
|
||||
- Rename `425 Unordered Collection` to standard `425 Too Early`
|
||||
|
||||
4.17.3 / 2022-02-16
|
||||
===================
|
||||
|
||||
* deps: accepts@~1.3.8
|
||||
- deps: mime-types@~2.1.34
|
||||
- deps: negotiator@0.6.3
|
||||
* deps: body-parser@1.19.2
|
||||
- deps: bytes@3.1.2
|
||||
- deps: qs@6.9.7
|
||||
- deps: raw-body@2.4.3
|
||||
* deps: cookie@0.4.2
|
||||
* deps: qs@6.9.7
|
||||
* Fix handling of `__proto__` keys
|
||||
* pref: remove unnecessary regexp for trust proxy
|
||||
|
||||
4.17.2 / 2021-12-16
|
||||
===================
|
||||
|
||||
* 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
|
||||
===================
|
||||
|
||||
@@ -2000,7 +2410,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
|
||||
@@ -3211,8 +3621,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
|
||||
|
||||
@@ -3247,7 +3657,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
|
||||
@@ -3286,7 +3696,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)
|
||||
|
||||
125
Readme-Guide.md
125
Readme-Guide.md
@@ -1,125 +0,0 @@
|
||||
# README guidelines
|
||||
|
||||
Every module in the expressjs, pillarjs, and jshttp organizations should have
|
||||
a README file named `README.md`. The purpose of the README is to:
|
||||
|
||||
- Explain the purpose of the module and how to use it.
|
||||
- Act as a landing page (both on GitHub and npmjs.com) for the module to help
|
||||
people find it via search. Middleware module READMEs are also incorporated
|
||||
into https://expressjs.com/en/resources/middleware.html.
|
||||
- Encourage community contributions and participation.
|
||||
|
||||
Use the [README template](https://github.com/expressjs/express/wiki/README-template)
|
||||
to quickly create a new README file.
|
||||
|
||||
## Top-level items
|
||||
|
||||
**Badges** (optional): At the very top (with no subheading), include any
|
||||
applicable badges, such as npm version/downloads, build status, test coverage,
|
||||
and so on. Badges should resolve properly (not display a broken image).
|
||||
|
||||
Possible badges include:
|
||||
- npm version: `[![NPM Version][npm-image]][npm-url]`
|
||||
- npm downloads: `[![NPM Downloads][downloads-image]][downloads-url]`
|
||||
- Build status: `[![Build Status][travis-image]][travis-url]`
|
||||
- Test coverage: `[![Test Coverage][coveralls-image]][coveralls-url]`
|
||||
- Tips: `[![Gratipay][gratipay-image]][gratipay-url]`
|
||||
|
||||
**Summary**: Following badges, provide a one- or two-sentence description of
|
||||
what the module does. This should be the same as the npmjs.org blurb (which
|
||||
comes from the description property of `package.json`). Since npm doesn't
|
||||
handle markdown for the blurb, avoid using markdown in the summary sentence.
|
||||
|
||||
**TOC** (Optional): For longer READMEs, provide a table of contents that has
|
||||
a relative link to each section. A tool such as
|
||||
[doctoc](https://www.npmjs.com/package/doctoc) makes it very easy to generate
|
||||
a TOC.
|
||||
|
||||
## Overview
|
||||
|
||||
Optionally, include a section of one or two paragraphs with more high-level
|
||||
information on what the module does, what problems it solves, why one would
|
||||
use it and how. Don't just repeat what's in the summary.
|
||||
|
||||
## Installation
|
||||
|
||||
Required. This section is typically just:
|
||||
|
||||
```sh
|
||||
$ npm install module-name
|
||||
```
|
||||
|
||||
But include any other steps or requirements.
|
||||
|
||||
NOTE: Use the `sh` code block to make the shell command display properly on
|
||||
the website.
|
||||
|
||||
## Basic use
|
||||
|
||||
- Provide a general description of how to use the module with code sample.
|
||||
Include any important caveats or restrictions.
|
||||
- Explain the most common use cases.
|
||||
- Optional: a simple "hello world" type example (where applicable). This
|
||||
example is in addition to the more comprehensive [example section](#examples)
|
||||
later.
|
||||
|
||||
## API
|
||||
|
||||
Provide complete API documentation.
|
||||
|
||||
Formatting conventions: Each function is listed in a 3rd-level heading (`###`),
|
||||
like this:
|
||||
|
||||
```
|
||||
### Function_name(arg, options [, optional_arg] ... )
|
||||
```
|
||||
|
||||
**Options objects**
|
||||
|
||||
For arguments that are objects (for example, options object), describe the
|
||||
properties in a table, as follows. This matches the formatting used in the
|
||||
[Express API docs](https://expressjs.com/en/4x/api.html).
|
||||
|
||||
|Property | Description | Type | Default|
|
||||
|----------|-----------|------------|-------------|
|
||||
|Name of the property in `monospace`. | Brief description | String, Number, Boolean, etc. | If applicable.|
|
||||
|
||||
If all the properties are required (i.e. there are no defaults), then you
|
||||
can omit the default column.
|
||||
|
||||
Instead of very lengthy descriptions, link out to subsequent paragraphs for
|
||||
more detailed explanation of specific cases (e.g. "When this property is set
|
||||
to 'foobar', xyz happens; see <link to following section >.)
|
||||
|
||||
If there are options properties that are themselves options, use additional
|
||||
tables. See [`trust proxy` and `etag` properties](https://expressjs.com/en/4x/api.html#app.settings.table).
|
||||
|
||||
## Examples
|
||||
|
||||
Every README should have at least one example; ideally more. For code samples,
|
||||
be sure to use the `js` code block, for proper display in the website, e.g.:
|
||||
|
||||
```js
|
||||
var csurf = require('csurf')
|
||||
...
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
What tests are included.
|
||||
|
||||
How to run them.
|
||||
|
||||
The convention for running tests is `npm test`. All our projects should follow
|
||||
this convention.
|
||||
|
||||
## Contributors
|
||||
|
||||
Names of module "owners" (lead developers) and other developers who have
|
||||
contributed.
|
||||
|
||||
## License
|
||||
|
||||
Link to the license, with a short description of what it is, e.g. "MIT" or
|
||||
whatever. Ideally, avoid putting the license text directly in the README; link
|
||||
to it instead.
|
||||
235
Readme.md
235
Readme.md
@@ -1,22 +1,48 @@
|
||||
[](http://expressjs.com/)
|
||||
[](https://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
**Fast, unopinionated, minimalist web framework for [Node.js](https://nodejs.org).**
|
||||
|
||||
**This project has a [Code of Conduct].**
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Installation](#installation)
|
||||
- [Features](#features)
|
||||
- [Docs \& Community](#docs--community)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Philosophy](#philosophy)
|
||||
- [Examples](#examples)
|
||||
- [Contributing](#contributing)
|
||||
- [Security Issues](#security-issues)
|
||||
- [Running Tests](#running-tests)
|
||||
- [Current project team members](#current-project-team-members)
|
||||
- [TC (Technical Committee)](#tc-technical-committee)
|
||||
- [TC emeriti members](#tc-emeriti-members)
|
||||
- [Triagers](#triagers)
|
||||
- [Emeritus Triagers](#emeritus-triagers)
|
||||
- [License](#license)
|
||||
|
||||
|
||||
[![NPM Version][npm-version-image]][npm-url]
|
||||
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
|
||||
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
|
||||
|
||||
[![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]
|
||||
|
||||
```js
|
||||
const express = require('express')
|
||||
import express from 'express'
|
||||
|
||||
const app = express()
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World')
|
||||
})
|
||||
|
||||
app.listen(3000)
|
||||
app.listen(3000, () => {
|
||||
console.log('Server is running on http://localhost:3000')
|
||||
})
|
||||
```
|
||||
|
||||
## Installation
|
||||
@@ -25,16 +51,19 @@ 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.
|
||||
Node.js 18 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):
|
||||
|
||||
```bash
|
||||
$ npm install express
|
||||
npm install express
|
||||
```
|
||||
|
||||
Follow [our installing guide](http://expressjs.com/en/starter/installing.html)
|
||||
Follow [our installing guide](https://expressjs.com/en/starter/installing.html)
|
||||
for more information.
|
||||
|
||||
## Features
|
||||
@@ -49,18 +78,11 @@ for more information.
|
||||
|
||||
## Docs & Community
|
||||
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
|
||||
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
|
||||
* [Website and Documentation](https://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
|
||||
* [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
|
||||
* [Gitter](https://gitter.im/expressjs/express) for support and discussion
|
||||
* [Github Discussions](https://github.com/expressjs/discussions) for discussion on the development and usage of 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).
|
||||
**PROTIP** Be sure to read the [migration guide to v5](https://expressjs.com/en/guide/migrating-5)
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -69,25 +91,25 @@ If you discover a security vulnerability in Express, please see [Security Polici
|
||||
Install the executable. The executable's major version will match Express's:
|
||||
|
||||
```bash
|
||||
$ npm install -g express-generator@4
|
||||
npm install -g express-generator@4
|
||||
```
|
||||
|
||||
Create the app:
|
||||
|
||||
```bash
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
express /tmp/foo && cd /tmp/foo
|
||||
```
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
npm install
|
||||
```
|
||||
|
||||
Start the server:
|
||||
|
||||
```bash
|
||||
$ npm start
|
||||
npm start
|
||||
```
|
||||
|
||||
View the website at: http://localhost:3000
|
||||
@@ -95,61 +117,162 @@ $ npm start
|
||||
## 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
|
||||
14 template engines via [Consolidate.js](https://github.com/tj/consolidate.js),
|
||||
14 template engines via [@ladjs/consolidate](https://github.com/ladjs/consolidate),
|
||||
you can quickly craft your perfect framework.
|
||||
|
||||
## Examples
|
||||
|
||||
To view the examples, clone the Express repo and install the dependencies:
|
||||
To view the examples, clone the Express repository:
|
||||
|
||||
```bash
|
||||
$ git clone git://github.com/expressjs/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
git clone https://github.com/expressjs/express.git --depth 1 && cd express
|
||||
```
|
||||
|
||||
Then install the dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Then run whichever example you want:
|
||||
|
||||
```bash
|
||||
$ node examples/content-negotiation
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
To run the test suite, first install the dependencies, then run `npm test`:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
$ npm test
|
||||
node examples/content-negotiation
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
[Contributing Guide](Contributing.md)
|
||||
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!
|
||||
|
||||
## People
|
||||
See the [Contributing Guide] for more technical details on contributing.
|
||||
|
||||
### Security Issues
|
||||
|
||||
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](https://github.com/expressjs/express/security/policy).
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the test suite, first install the dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Then run `npm test`:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
## Current project team members
|
||||
|
||||
For information about the governance of the express.js project, see [GOVERNANCE.md](https://github.com/expressjs/discussions/blob/HEAD/docs/GOVERNANCE.md).
|
||||
|
||||
The original author of Express is [TJ Holowaychuk](https://github.com/tj)
|
||||
|
||||
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson)
|
||||
|
||||
[List of all contributors](https://github.com/expressjs/express/graphs/contributors)
|
||||
|
||||
### TC (Technical Committee)
|
||||
|
||||
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
|
||||
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
|
||||
* [wesleytodd](https://github.com/wesleytodd) - **Wes Todd**
|
||||
* [LinusU](https://github.com/LinusU) - **Linus Unnebäck**
|
||||
* [blakeembrey](https://github.com/blakeembrey) - **Blake Embrey**
|
||||
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
|
||||
* [crandmck](https://github.com/crandmck) - **Rand McKinney**
|
||||
* [ctcpip](https://github.com/ctcpip) - **Chris de Almeida**
|
||||
|
||||
<details>
|
||||
<summary>TC emeriti members</summary>
|
||||
|
||||
#### TC emeriti members
|
||||
|
||||
* [dougwilson](https://github.com/dougwilson) - **Douglas Wilson**
|
||||
* [hacksparrow](https://github.com/hacksparrow) - **Hage Yaapa**
|
||||
* [jonathanong](https://github.com/jonathanong) - **jongleberry**
|
||||
* [niftylettuce](https://github.com/niftylettuce) - **niftylettuce**
|
||||
* [troygoode](https://github.com/troygoode) - **Troy Goode**
|
||||
</details>
|
||||
|
||||
|
||||
### Triagers
|
||||
|
||||
* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair**
|
||||
* [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran**
|
||||
* [carpasse](https://github.com/carpasse) - **Carlos Serrano**
|
||||
* [CBID2](https://github.com/CBID2) - **Christine Belzie**
|
||||
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
|
||||
* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him)
|
||||
* [Phillip9587](https://github.com/Phillip9587) - **Phillip Barta**
|
||||
* [efekrskl](https://github.com/efekrskl) - **Efe Karasakal**
|
||||
* [rxmarbles](https://github.com/rxmarbles) - **Rick Markins** (he/him)
|
||||
* [krzysdz](https://github.com/krzysdz)
|
||||
* [GroophyLifefor](https://github.com/GroophyLifefor) - **Murat Kirazkaya**
|
||||
|
||||
<details>
|
||||
<summary>Triagers emeriti members</summary>
|
||||
|
||||
#### Emeritus Triagers
|
||||
|
||||
* [AuggieH](https://github.com/AuggieH) - **Auggie Hudak**
|
||||
* [G-Rath](https://github.com/G-Rath) - **Gareth Jones**
|
||||
* [MohammadXroid](https://github.com/MohammadXroid) - **Mohammad Ayashi**
|
||||
* [NawafSwe](https://github.com/NawafSwe) - **Nawaf Alsharqi**
|
||||
* [NotMoni](https://github.com/NotMoni) - **Moni**
|
||||
* [VigneshMurugan](https://github.com/VigneshMurugan) - **Vignesh Murugan**
|
||||
* [davidmashe](https://github.com/davidmashe) - **David Ashe**
|
||||
* [digitaIfabric](https://github.com/digitaIfabric) - **David**
|
||||
* [e-l-i-s-e](https://github.com/e-l-i-s-e) - **Elise Bonner**
|
||||
* [fed135](https://github.com/fed135) - **Frederic Charette**
|
||||
* [firmanJS](https://github.com/firmanJS) - **Firman Abdul Hakim**
|
||||
* [getspooky](https://github.com/getspooky) - **Yasser Ameur**
|
||||
* [ghinks](https://github.com/ghinks) - **Glenn**
|
||||
* [ghousemohamed](https://github.com/ghousemohamed) - **Ghouse Mohamed**
|
||||
* [gireeshpunathil](https://github.com/gireeshpunathil) - **Gireesh Punathil**
|
||||
* [jake32321](https://github.com/jake32321) - **Jake Reed**
|
||||
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
|
||||
* [lekanikotun](https://github.com/lekanikotun) - **Troy Goode**
|
||||
* [marsonya](https://github.com/marsonya) - **Lekan Ikotun**
|
||||
* [mastermatt](https://github.com/mastermatt) - **Matt R. Wilson**
|
||||
* [maxakuru](https://github.com/maxakuru) - **Max Edell**
|
||||
* [mlrawlings](https://github.com/mlrawlings) - **Michael Rawlings**
|
||||
* [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov**
|
||||
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
|
||||
* [tarunyadav1](https://github.com/tarunyadav1) - **Tarun yadav**
|
||||
* [tunniclm](https://github.com/tunniclm) - **Mike Tunnicliffe**
|
||||
* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**
|
||||
* [0ss](https://github.com/0ss) - **Salah**
|
||||
* [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him)
|
||||
* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)
|
||||
* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**
|
||||
* [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin**
|
||||
* [dpopp07](https://github.com/dpopp07) - **Dustin Popp**
|
||||
* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
|
||||
* [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi**
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## License
|
||||
|
||||
[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-url]: https://ci.appveyor.com/project/dougwilson/express
|
||||
[coveralls-image]: https://img.shields.io/coveralls/expressjs/express/master.svg
|
||||
[coveralls-image]: https://img.shields.io/coverallsCoverage/github/expressjs/express?branch=master
|
||||
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
|
||||
[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/express/ci.yml?branch=master&label=ci
|
||||
[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml
|
||||
[npm-downloads-image]: https://img.shields.io/npm/dm/express
|
||||
[npm-downloads-url]: https://npmcharts.com/compare/express?minimal=true
|
||||
[npm-url]: https://npmjs.org/package/express
|
||||
[npm-version-image]: https://img.shields.io/npm/v/express
|
||||
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge
|
||||
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/express
|
||||
[Code of Conduct]: https://github.com/expressjs/.github/blob/HEAD/CODE_OF_CONDUCT.md
|
||||
[Contributing Guide]: https://github.com/expressjs/.github/blob/HEAD/CONTRIBUTING.md
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
# Express Release Process
|
||||
|
||||
This document contains the technical aspects of the Express release process. The
|
||||
intended audience is those who have been authorized by the Express Technical
|
||||
Committee (TC) to create, promote and sign official release builds for Express,
|
||||
as npm packages hosted on https://npmjs.com/package/express.
|
||||
|
||||
## Who can make releases?
|
||||
|
||||
Release authorization is given by the Express TC. Once authorized, an individual
|
||||
must have the following access permissions:
|
||||
|
||||
### 1. Github release access
|
||||
|
||||
The individual making the release will need to be a member of the
|
||||
expressjs/express team with Write permission level so they are able to tag the
|
||||
release commit and push changes to the expressjs/express repository
|
||||
(see Steps 4 and 5).
|
||||
|
||||
### 2. npmjs.com release access
|
||||
|
||||
The individual making the release will need to be made an owner on the
|
||||
`express` package on npmjs.com so they are able to publish the release
|
||||
(see Step 6).
|
||||
|
||||
## How to publish a release
|
||||
|
||||
Before publishing, the following preconditions should be met:
|
||||
|
||||
- A release proposal issue or tracking pull request (see "Proposal branch"
|
||||
below) will exist documenting:
|
||||
- the proposed changes
|
||||
- the type of release: patch, minor or major
|
||||
- the version number (according to semantic versioning - http://semver.org)
|
||||
- The proposed changes should be complete.
|
||||
|
||||
There are two main release flows: patch and non-patch.
|
||||
|
||||
The patch flow is for making **patch releases**. As per semantic versioning,
|
||||
patch releases are for simple changes, eg: typo fixes, patch dependency updates,
|
||||
and simple/low-risk bug fixes. Every other type of change is made via the
|
||||
non-patch flow.
|
||||
|
||||
### Branch terminology
|
||||
|
||||
"Master branch"
|
||||
|
||||
- There is a branch in git used for the current major version of Express, named
|
||||
`master`.
|
||||
- This branch contains the completed commits for the next patch release of the
|
||||
current major version.
|
||||
- Releases for the current major version are published from this branch.
|
||||
|
||||
"Version branch"
|
||||
|
||||
- For any given major version of Express (current, previous or next) there is
|
||||
a branch in git for that release named `<major-version>.x` (eg: `4.x`).
|
||||
- This branch points to the commit of the latest tag for the given major version.
|
||||
|
||||
"Release branch"
|
||||
|
||||
- For any given major version of Express, there is a branch used for publishing
|
||||
releases.
|
||||
- For the current major version of Express, the release branch is the
|
||||
"Master branch" named `master`.
|
||||
- For all other major versions of Express, the release branch is the
|
||||
"Version branch" named `<major-version>.x`.
|
||||
|
||||
"Proposal branch"
|
||||
|
||||
- A branch in git representing a proposed new release of Express. This can be a
|
||||
minor or major release, named `<major-version>.0` for a major release,
|
||||
`<major-version>.<minor-version>` for a minor release.
|
||||
- A tracking pull request should exist to document the proposed release,
|
||||
targeted at the appropriate release branch. Prior to opening the tracking
|
||||
pull request the content of the release may have be discussed in an issue.
|
||||
- This branch contains the commits accepted so far that implement the proposal
|
||||
in the tracking pull request.
|
||||
|
||||
### Patch flow
|
||||
|
||||
In the patch flow, simple changes are committed to the release branch which
|
||||
acts as an ever-present branch for the next patch release of the associated
|
||||
major version of Express.
|
||||
|
||||
The release branch is usually kept in a state where it is ready to release.
|
||||
Releases are made when sufficient time or change has been made to warrant it.
|
||||
This is usually proposed and decided using a github issue.
|
||||
|
||||
### Non-patch flow
|
||||
|
||||
In the non-patch flow, changes are committed to a temporary proposal branch
|
||||
created specifically for that release proposal. The branch is based on the
|
||||
most recent release of the major version of Express that the release targets.
|
||||
|
||||
Releases are made when all the changes on a proposal branch are complete and
|
||||
approved. This is done by merging the proposal branch into the release branch
|
||||
(using a fast-forward merge), tagging it with the new version number and
|
||||
publishing the release package to npmjs.com.
|
||||
|
||||
### Flow
|
||||
|
||||
Below is a detailed description of the steps to publish a release.
|
||||
|
||||
#### Step 1. Check the release is ready to publish
|
||||
|
||||
Check any relevant information to ensure the release is ready, eg: any
|
||||
milestone, label, issue or tracking pull request for the release. The release
|
||||
is ready when all proposed code, tests and documentation updates are complete
|
||||
(either merged, closed or re-targeted to another release).
|
||||
|
||||
#### Step 2. (Non-patch flow only) Merge the proposal branch into the release branch
|
||||
|
||||
In the patch flow: skip this step.
|
||||
|
||||
In the non-patch flow:
|
||||
```sh
|
||||
$ git checkout <release-branch>
|
||||
$ git merge --ff-only <proposal-branch>
|
||||
```
|
||||
|
||||
<release-branch> - see "Release branch" of "Branches" above.
|
||||
<proposal-branch> - see "Proposal branch" of "Non-patch flow" above.
|
||||
|
||||
**NOTE:** You may need to rebase the proposal branch to allow a fast-forward
|
||||
merge. Using a fast-forward merge keeps the history clean as it does
|
||||
not introduce merge commits.
|
||||
|
||||
### Step 3. Update the History.md and package.json to the new version number
|
||||
|
||||
The changes so far for the release should already be documented under the
|
||||
"unreleased" section at the top of the History.md file, as per the usual
|
||||
development practice. Change "unreleased" to the new release version / date.
|
||||
Example diff fragment:
|
||||
|
||||
```diff
|
||||
-unreleased
|
||||
-==========
|
||||
+4.13.3 / 2015-08-02
|
||||
+===================
|
||||
```
|
||||
|
||||
The version property in the package.json should already contain the version of
|
||||
the previous release. Change it to the new release version.
|
||||
|
||||
Commit these changes together under a single commit with the message set to
|
||||
the new release version (eg: `4.13.3`):
|
||||
|
||||
```sh
|
||||
$ git checkout <release-branch>
|
||||
<..edit files..>
|
||||
$ git add History.md package.json
|
||||
$ git commit -m '<version-number>'
|
||||
```
|
||||
|
||||
### Step 4. Identify and tag the release commit with the new release version
|
||||
|
||||
Create a lightweight tag (rather than an annotated tag) named after the new
|
||||
release version (eg: `4.13.3`).
|
||||
|
||||
```sh
|
||||
$ git tag <version-number>
|
||||
```
|
||||
|
||||
### Step 5. Push the release branch changes and tag to github
|
||||
|
||||
The branch and tag should be pushed directly to the main repository
|
||||
(https://github.com/expressjs/express).
|
||||
|
||||
```sh
|
||||
$ git push origin <release-branch>
|
||||
$ git push origin <version-number>
|
||||
```
|
||||
|
||||
### Step 6. Publish to npmjs.com
|
||||
|
||||
Ensure your local working copy is completely clean (no extra or changed files).
|
||||
You can use `git status` for this purpose.
|
||||
|
||||
```sh
|
||||
$ npm login <npm-username>
|
||||
$ npm publish
|
||||
```
|
||||
|
||||
**NOTE:** The version number to publish will be picked up automatically from
|
||||
package.json.
|
||||
43
Security.md
43
Security.md
@@ -1,43 +0,0 @@
|
||||
# Security Policies and Procedures
|
||||
|
||||
This document outlines security procedures and general policies for the Express
|
||||
project.
|
||||
|
||||
* [Reporting a Bug](#reporting-a-bug)
|
||||
* [Disclosure Policy](#disclosure-policy)
|
||||
* [Comments on this Policy](#comments-on-this-policy)
|
||||
|
||||
## Reporting a Bug
|
||||
|
||||
The Express team and community take all security bugs in Express seriously.
|
||||
Thank you for improving the security of Express. We appreciate your efforts and
|
||||
responsible disclosure and will make every effort to acknowledge your
|
||||
contributions.
|
||||
|
||||
Report security bugs by emailing the lead maintainer in the Readme.md file.
|
||||
|
||||
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
|
||||
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).
|
||||
|
||||
## Disclosure Policy
|
||||
|
||||
When the security team receives a security bug report, they will assign it to a
|
||||
primary handler. This person will coordinate the fix and release process,
|
||||
involving the following steps:
|
||||
|
||||
* Confirm the problem and determine the affected versions.
|
||||
* Audit code to find any potential similar problems.
|
||||
* Prepare fixes for all releases still under maintenance. These fixes will be
|
||||
released as fast as possible to npm.
|
||||
|
||||
## Comments on this Policy
|
||||
|
||||
If you have suggestions on how this process could be improved please submit a
|
||||
pull request.
|
||||
64
appveyor.yml
64
appveyor.yml
@@ -1,64 +0,0 @@
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "0.12"
|
||||
- nodejs_version: "1.8"
|
||||
- nodejs_version: "2.5"
|
||||
- nodejs_version: "3.3"
|
||||
- nodejs_version: "4.9"
|
||||
- nodejs_version: "5.12"
|
||||
- nodejs_version: "6.17"
|
||||
- nodejs_version: "7.10"
|
||||
- nodejs_version: "8.16"
|
||||
- nodejs_version: "9.11"
|
||||
- nodejs_version: "10.15"
|
||||
- nodejs_version: "11.15"
|
||||
- nodejs_version: "12.3"
|
||||
cache:
|
||||
- node_modules
|
||||
install:
|
||||
# Install Node.js
|
||||
- ps: >-
|
||||
try { Install-Product node $env:nodejs_version -ErrorAction Stop }
|
||||
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) }
|
||||
# Configure npm
|
||||
- ps: |
|
||||
# Skip updating shrinkwrap / lock
|
||||
npm config set shrinkwrap false
|
||||
# Remove all non-test dependencies
|
||||
- ps: |
|
||||
# Remove example dependencies
|
||||
npm rm --silent --save-dev connect-redis
|
||||
# Setup Node.js version-specific dependencies
|
||||
- ps: |
|
||||
# mocha for testing
|
||||
# - use 3.x for Node.js < 6
|
||||
if ($env:nodejs_version.split(".")[0] -lt 6) {
|
||||
npm install --silent --save-dev mocha@3.5.3
|
||||
}
|
||||
- ps: |
|
||||
# supertest for http calls
|
||||
# - use 2.0.0 for Node.js < 4
|
||||
if ($env:nodejs_version.split(".")[0] -lt 4) {
|
||||
npm install --silent --save-dev supertest@2.0.0
|
||||
}
|
||||
# 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:
|
||||
# Output version data
|
||||
- ps: |
|
||||
node --version
|
||||
npm --version
|
||||
# Run test script
|
||||
- npm run test-ci
|
||||
# Run linting
|
||||
- npm run lint
|
||||
version: "{build}"
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
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
|
||||
@echo
|
||||
|
||||
.PHONY: all
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
var express = require('..');
|
||||
var app = express();
|
||||
|
||||
// number of middleware
|
||||
|
||||
var n = parseInt(process.env.MW || '1', 10);
|
||||
console.log(' %s middleware', n);
|
||||
|
||||
while (n--) {
|
||||
app.use(function(req, res, next){
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send('Hello World')
|
||||
});
|
||||
|
||||
app.listen(3333);
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo
|
||||
MW=$1 node $2 &
|
||||
pid=$!
|
||||
|
||||
sleep 2
|
||||
|
||||
wrk 'http://localhost:3333/?foo[bar]=baz' \
|
||||
-d 3 \
|
||||
-c 50 \
|
||||
-t 8 \
|
||||
| grep 'Requests/sec' \
|
||||
| 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,10 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var hash = require('pbkdf2-password')()
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
var session = require('express-session');
|
||||
|
||||
var app = module.exports = express();
|
||||
@@ -16,7 +18,7 @@ app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// middleware
|
||||
|
||||
app.use(express.urlencoded({ extended: false }))
|
||||
app.use(express.urlencoded())
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
@@ -59,14 +61,14 @@ function authenticate(name, pass, fn) {
|
||||
if (!module.parent) console.log('authenticating %s:%s', name, pass);
|
||||
var user = users[name];
|
||||
// query the db for the given username
|
||||
if (!user) return fn(new Error('cannot find user'));
|
||||
if (!user) return fn(null, null)
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the hash against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {
|
||||
if (err) return fn(err);
|
||||
if (hash === user.hash) return fn(null, user)
|
||||
fn(new Error('invalid password'));
|
||||
fn(null, null)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,8 +101,10 @@ app.get('/login', function(req, res){
|
||||
res.render('login');
|
||||
});
|
||||
|
||||
app.post('/login', function(req, res){
|
||||
app.post('/login', function (req, res, next) {
|
||||
if (!req.body) return res.sendStatus(400)
|
||||
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
|
||||
@@ -112,7 +116,7 @@ app.post('/login', function(req, res){
|
||||
req.session.success = 'Authenticated as ' + user.name
|
||||
+ ' click to <a href="/logout">logout</a>. '
|
||||
+ ' You may now access <a href="/restricted">/restricted</a>.';
|
||||
res.redirect('back');
|
||||
res.redirect(req.get('Referrer') || '/');
|
||||
});
|
||||
} else {
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
|
||||
@@ -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 {
|
||||
@@ -8,7 +10,7 @@
|
||||
font: 13px Helvetica, Arial, sans-serif;
|
||||
}
|
||||
.error {
|
||||
color: red
|
||||
color: red;
|
||||
}
|
||||
.success {
|
||||
color: green;
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -17,7 +19,7 @@ if (process.env.NODE_ENV !== 'test') app.use(logger(':method :url'))
|
||||
app.use(cookieParser('my secret here'));
|
||||
|
||||
// parses x-www-form-urlencoded
|
||||
app.use(express.urlencoded({ extended: false }))
|
||||
app.use(express.urlencoded())
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
@@ -31,13 +33,17 @@ app.get('/', function(req, res){
|
||||
|
||||
app.get('/forget', function(req, res){
|
||||
res.clearCookie('remember');
|
||||
res.redirect('back');
|
||||
res.redirect(req.get('Referrer') || '/');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
var minute = 60000;
|
||||
if (req.body.remember) res.cookie('remember', 1, { maxAge: minute });
|
||||
res.redirect('back');
|
||||
|
||||
if (req.body && req.body.remember) {
|
||||
res.cookie('remember', 1, { maxAge: minute })
|
||||
}
|
||||
|
||||
res.redirect(req.get('Referrer') || '/');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
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,25 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('path');
|
||||
var path = require('node: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) {
|
||||
app.get('/files/*file', function (req, res, next) {
|
||||
res.download(req.params.file.join('/'), { root: FILES_DIR }, function (err) {
|
||||
if (!err) return; // file sent
|
||||
if (err.status !== 404) return next(err); // non-404 error
|
||||
// file for download not found
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
|
||||
@@ -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,9 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var silent = process.env.NODE_ENV === 'test'
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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,12 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var escapeHtml = require('escape-html');
|
||||
var express = require('../..');
|
||||
var fs = require('fs');
|
||||
var fs = require('node:fs');
|
||||
var marked = require('marked');
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -24,7 +26,7 @@ app.engine('md', function(path, options, fn){
|
||||
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// make it the default so we dont need .md
|
||||
// make it the default, so we don't need .md
|
||||
app.set('view engine', 'md');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv1 = express.Router();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv2 = express.Router();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../..');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var multiparty = require('multiparty');
|
||||
var format = require('util').format;
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<form method="post" enctype="multipart/form-data">'
|
||||
+ '<p>Title: <input type="text" name="title" /></p>'
|
||||
+ '<p>Image: <input type="file" name="image" /></p>'
|
||||
+ '<p><input type="submit" value="Upload" /></p>'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res, next){
|
||||
// create a form to begin parsing
|
||||
var form = new multiparty.Form();
|
||||
var image;
|
||||
var title;
|
||||
|
||||
form.on('error', next);
|
||||
form.on('close', function(){
|
||||
res.send(format('\nuploaded %s (%d Kb) as %s'
|
||||
, image.filename
|
||||
, image.size / 1024 | 0
|
||||
, title));
|
||||
});
|
||||
|
||||
// listen on field event for title
|
||||
form.on('field', function(name, val){
|
||||
if (name !== 'title') return;
|
||||
title = val;
|
||||
});
|
||||
|
||||
// listen on part event for image file
|
||||
form.on('part', function(part){
|
||||
if (!part.filename) return;
|
||||
if (part.name !== 'image') return part.resume();
|
||||
image = {};
|
||||
image.filename = part.filename;
|
||||
image.size = 0;
|
||||
part.on('data', function(buf){
|
||||
image.size += buf.length;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// parse the form
|
||||
form.parse(req);
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(4000);
|
||||
console.log('Express started on port 4000');
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,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,10 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
var session = require('express-session');
|
||||
var methodOverride = require('method-override');
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../..');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var fs = require('node:fs');
|
||||
var path = require('node:path');
|
||||
|
||||
module.exports = function(parent, options){
|
||||
var dir = path.join(__dirname, '..', 'controllers');
|
||||
|
||||
@@ -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){
|
||||
@@ -37,7 +32,8 @@ app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
// Load user by id
|
||||
|
||||
app.param('user', function(req, res, next, id){
|
||||
if (req.user = users[id]) {
|
||||
req.user = users[id]
|
||||
if (req.user) {
|
||||
next();
|
||||
} else {
|
||||
next(createError(404, 'failed to find user'));
|
||||
@@ -56,7 +52,7 @@ app.get('/', function(req, res){
|
||||
* GET :user.
|
||||
*/
|
||||
|
||||
app.get('/user/:user', function(req, res, next){
|
||||
app.get('/user/:user', function (req, res) {
|
||||
res.send('user ' + req.user.name);
|
||||
});
|
||||
|
||||
@@ -64,7 +60,7 @@ app.get('/user/:user', function(req, res, next){
|
||||
* GET users :from - :to.
|
||||
*/
|
||||
|
||||
app.get('/users/:from-:to', function(req, res, next){
|
||||
app.get('/users/:from-:to', function (req, res) {
|
||||
var from = req.params.from;
|
||||
var to = req.params.to;
|
||||
var names = users.map(function(user){ return user.name; });
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -10,7 +12,7 @@ var app = module.exports = express();
|
||||
|
||||
app.resource = function(path, obj) {
|
||||
this.get(path, obj.index);
|
||||
this.get(path + '/:a..:b.:format?', function(req, res){
|
||||
this.get(path + '/:a..:b{.:format}', function(req, res){
|
||||
var a = parseInt(req.params.a, 10);
|
||||
var b = parseInt(req.params.b, 10);
|
||||
var format = req.params.format;
|
||||
@@ -26,7 +28,7 @@ app.resource = function(path, obj) {
|
||||
// Fake records
|
||||
|
||||
var users = [
|
||||
{ name: 'tj' }
|
||||
{ name: 'tj' }
|
||||
, { name: 'ciaran' }
|
||||
, { name: 'aaron' }
|
||||
, { name: 'guillermo' }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var escapeHtml = require('escape-html')
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var verbose = process.env.NODE_ENV !== 'test'
|
||||
@@ -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' }
|
||||
];
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
var app = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
@@ -36,7 +38,7 @@ app.get('/', site.index);
|
||||
// User
|
||||
|
||||
app.get('/users', user.list);
|
||||
app.all('/user/:id/:op?', user.load);
|
||||
app.all('/user/:id{/:op}', user.load);
|
||||
app.get('/user/:id', user.view);
|
||||
app.get('/user/:id/view', user.view);
|
||||
app.get('/user/:id/edit', user.edit);
|
||||
|
||||
@@ -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 = [
|
||||
@@ -41,5 +43,5 @@ exports.update = function(req, res){
|
||||
var user = req.body.user;
|
||||
req.user.name = user.name;
|
||||
req.user.email = user.email;
|
||||
res.redirect('back');
|
||||
res.redirect(req.get('Referrer') || '/');
|
||||
};
|
||||
|
||||
@@ -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/
|
||||
@@ -11,35 +12,51 @@
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
var redis = require('redis');
|
||||
|
||||
var db = redis.createClient();
|
||||
|
||||
// npm install redis
|
||||
|
||||
var app = express();
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// populate search
|
||||
// npm install redis
|
||||
|
||||
db.sadd('ferret', 'tobi');
|
||||
db.sadd('ferret', 'loki');
|
||||
db.sadd('ferret', 'jane');
|
||||
db.sadd('cat', 'manny');
|
||||
db.sadd('cat', 'luna');
|
||||
/**
|
||||
* Redis Initialization
|
||||
*/
|
||||
|
||||
async function initializeRedis() {
|
||||
try {
|
||||
// connect to Redis
|
||||
|
||||
await db.connect();
|
||||
|
||||
// populate search
|
||||
|
||||
await db.sAdd('ferret', 'tobi');
|
||||
await db.sAdd('ferret', 'loki');
|
||||
await db.sAdd('ferret', 'jane');
|
||||
await db.sAdd('cat', 'manny');
|
||||
await db.sAdd('cat', 'luna');
|
||||
} catch (err) {
|
||||
console.error('Error initializing Redis:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET search for :query.
|
||||
*/
|
||||
|
||||
app.get('/search/:query?', function(req, res){
|
||||
var query = req.params.query;
|
||||
db.smembers(query, function(err, vals){
|
||||
if (err) return res.send(500);
|
||||
res.send(vals);
|
||||
});
|
||||
app.get('/search/{:query}', function (req, res, next) {
|
||||
var query = req.params.query || '';
|
||||
db.sMembers(query)
|
||||
.then((vals) => res.send(vals))
|
||||
.catch((err) => {
|
||||
console.error(`Redis error for query "${query}":`, err);
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -53,8 +70,14 @@ app.get('/client.js', function(req, res){
|
||||
res.sendFile(path.join(__dirname, 'client.js'));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
/**
|
||||
* Start the Server
|
||||
*/
|
||||
|
||||
(async () => {
|
||||
await initializeRedis();
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
var search = document.querySelector('[type=search]');
|
||||
var code = document.querySelector('pre');
|
||||
|
||||
|
||||
@@ -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,10 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
var app = express();
|
||||
|
||||
// log requests
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var https = require('https');
|
||||
var path = require('path');
|
||||
var https = require('node:https');
|
||||
var path = require('node:path');
|
||||
var extname = path.extname;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var path = require('path');
|
||||
var path = require('node:path');
|
||||
var User = require('./user');
|
||||
var app = express();
|
||||
|
||||
@@ -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;
|
||||
@@ -55,7 +57,7 @@ var repos = [
|
||||
];
|
||||
|
||||
var users = [
|
||||
{ name: 'tobi' }
|
||||
{ name: 'tobi' }
|
||||
, { name: 'loki' }
|
||||
, { name: 'jane' }
|
||||
];
|
||||
@@ -70,12 +72,12 @@ var userRepos = {
|
||||
// and simply expose the data
|
||||
|
||||
// example: http://localhost:3000/api/users/?api-key=foo
|
||||
app.get('/api/users', function(req, res, next){
|
||||
app.get('/api/users', function (req, res) {
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
// example: http://localhost:3000/api/repos/?api-key=foo
|
||||
app.get('/api/repos', function(req, res, next){
|
||||
app.get('/api/repos', function (req, res) {
|
||||
res.send(repos);
|
||||
});
|
||||
|
||||
@@ -105,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 */
|
||||
|
||||
@@ -14,22 +14,24 @@
|
||||
*/
|
||||
|
||||
var finalhandler = require('finalhandler');
|
||||
var Router = require('./router');
|
||||
var methods = require('methods');
|
||||
var middleware = require('./middleware/init');
|
||||
var query = require('./middleware/query');
|
||||
var debug = require('debug')('express:application');
|
||||
var View = require('./view');
|
||||
var http = require('http');
|
||||
var http = require('node:http');
|
||||
var methods = require('./utils').methods;
|
||||
var compileETag = require('./utils').compileETag;
|
||||
var compileQueryParser = require('./utils').compileQueryParser;
|
||||
var compileTrust = require('./utils').compileTrust;
|
||||
var deprecate = require('depd')('express');
|
||||
var flatten = require('array-flatten');
|
||||
var merge = require('utils-merge');
|
||||
var resolve = require('path').resolve;
|
||||
var setPrototypeOf = require('setprototypeof')
|
||||
var resolve = require('node:path').resolve;
|
||||
var once = require('once')
|
||||
var Router = require('router');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var slice = Array.prototype.slice;
|
||||
var flatten = Array.prototype.flat;
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
@@ -55,11 +57,29 @@ var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
|
||||
*/
|
||||
|
||||
app.init = function init() {
|
||||
this.cache = {};
|
||||
this.engines = {};
|
||||
this.settings = {};
|
||||
var router = null;
|
||||
|
||||
this.cache = Object.create(null);
|
||||
this.engines = Object.create(null);
|
||||
this.settings = Object.create(null);
|
||||
|
||||
this.defaultConfiguration();
|
||||
|
||||
// Setup getting to lazily add base router
|
||||
Object.defineProperty(this, 'router', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function getrouter() {
|
||||
if (router === null) {
|
||||
router = new Router({
|
||||
caseSensitive: this.enabled('case sensitive routing'),
|
||||
strict: this.enabled('strict routing')
|
||||
});
|
||||
}
|
||||
|
||||
return router;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -74,7 +94,7 @@ app.defaultConfiguration = function defaultConfiguration() {
|
||||
this.enable('x-powered-by');
|
||||
this.set('etag', 'weak');
|
||||
this.set('env', env);
|
||||
this.set('query parser', 'extended');
|
||||
this.set('query parser', 'simple')
|
||||
this.set('subdomain offset', 2);
|
||||
this.set('trust proxy', false);
|
||||
|
||||
@@ -95,10 +115,10 @@ app.defaultConfiguration = function defaultConfiguration() {
|
||||
}
|
||||
|
||||
// inherit protos
|
||||
setPrototypeOf(this.request, parent.request)
|
||||
setPrototypeOf(this.response, parent.response)
|
||||
setPrototypeOf(this.engines, parent.engines)
|
||||
setPrototypeOf(this.settings, parent.settings)
|
||||
Object.setPrototypeOf(this.request, parent.request)
|
||||
Object.setPrototypeOf(this.response, parent.response)
|
||||
Object.setPrototypeOf(this.engines, parent.engines)
|
||||
Object.setPrototypeOf(this.settings, parent.settings)
|
||||
});
|
||||
|
||||
// setup locals
|
||||
@@ -118,32 +138,6 @@ app.defaultConfiguration = function defaultConfiguration() {
|
||||
if (env === 'production') {
|
||||
this.enable('view cache');
|
||||
}
|
||||
|
||||
Object.defineProperty(this, 'router', {
|
||||
get: function() {
|
||||
throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* lazily adds the base router if it has not yet been added.
|
||||
*
|
||||
* We cannot add the base router in the defaultConfiguration because
|
||||
* it reads app settings which might be set after that has run.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
app.lazyrouter = function lazyrouter() {
|
||||
if (!this._router) {
|
||||
this._router = new Router({
|
||||
caseSensitive: this.enabled('case sensitive routing'),
|
||||
strict: this.enabled('strict routing')
|
||||
});
|
||||
|
||||
this._router.use(query(this.get('query parser fn')));
|
||||
this._router.use(middleware.init(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -156,22 +150,31 @@ app.lazyrouter = function lazyrouter() {
|
||||
*/
|
||||
|
||||
app.handle = function handle(req, res, callback) {
|
||||
var router = this._router;
|
||||
|
||||
// final handler
|
||||
var done = callback || finalhandler(req, res, {
|
||||
env: this.get('env'),
|
||||
onerror: logerror.bind(this)
|
||||
});
|
||||
|
||||
// no routes
|
||||
if (!router) {
|
||||
debug('no routes defined on app');
|
||||
done();
|
||||
return;
|
||||
// set powered by header
|
||||
if (this.enabled('x-powered-by')) {
|
||||
res.setHeader('X-Powered-By', 'Express');
|
||||
}
|
||||
|
||||
router.handle(req, res, done);
|
||||
// set circular references
|
||||
req.res = res;
|
||||
res.req = req;
|
||||
|
||||
// alter the prototypes
|
||||
Object.setPrototypeOf(req, this.request)
|
||||
Object.setPrototypeOf(res, this.response)
|
||||
|
||||
// setup locals
|
||||
if (!res.locals) {
|
||||
res.locals = Object.create(null);
|
||||
}
|
||||
|
||||
this.router.handle(req, res, done);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -204,15 +207,14 @@ app.use = function use(fn) {
|
||||
}
|
||||
}
|
||||
|
||||
var fns = flatten(slice.call(arguments, offset));
|
||||
var fns = flatten.call(slice.call(arguments, offset), Infinity);
|
||||
|
||||
if (fns.length === 0) {
|
||||
throw new TypeError('app.use() requires a middleware function')
|
||||
}
|
||||
|
||||
// setup router
|
||||
this.lazyrouter();
|
||||
var router = this._router;
|
||||
// get router
|
||||
var router = this.router;
|
||||
|
||||
fns.forEach(function (fn) {
|
||||
// non-express app
|
||||
@@ -228,8 +230,8 @@ app.use = function use(fn) {
|
||||
router.use(path, function mounted_app(req, res, next) {
|
||||
var orig = req.app;
|
||||
fn.handle(req, res, function (err) {
|
||||
setPrototypeOf(req, orig.request)
|
||||
setPrototypeOf(res, orig.response)
|
||||
Object.setPrototypeOf(req, orig.request)
|
||||
Object.setPrototypeOf(res, orig.response)
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
@@ -252,8 +254,7 @@ app.use = function use(fn) {
|
||||
*/
|
||||
|
||||
app.route = function route(path) {
|
||||
this.lazyrouter();
|
||||
return this._router.route(path);
|
||||
return this.router.route(path);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -276,7 +277,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)
|
||||
@@ -319,8 +320,6 @@ app.engine = function engine(ext, fn) {
|
||||
*/
|
||||
|
||||
app.param = function param(name, fn) {
|
||||
this.lazyrouter();
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
this.param(name[i], fn);
|
||||
@@ -329,7 +328,7 @@ app.param = function param(name, fn) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this._router.param(name, fn);
|
||||
this.router.param(name, fn);
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -469,16 +468,14 @@ app.disable = function disable(setting) {
|
||||
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
|
||||
*/
|
||||
|
||||
methods.forEach(function(method){
|
||||
app[method] = function(path){
|
||||
methods.forEach(function (method) {
|
||||
app[method] = function (path) {
|
||||
if (method === 'get' && arguments.length === 1) {
|
||||
// app.get(setting)
|
||||
return this.set(path);
|
||||
}
|
||||
|
||||
this.lazyrouter();
|
||||
|
||||
var route = this._router.route(path);
|
||||
var route = this.route(path);
|
||||
route[method].apply(route, slice.call(arguments, 1));
|
||||
return this;
|
||||
};
|
||||
@@ -495,9 +492,7 @@ methods.forEach(function(method){
|
||||
*/
|
||||
|
||||
app.all = function all(path) {
|
||||
this.lazyrouter();
|
||||
|
||||
var route = this._router.route(path);
|
||||
var route = this.route(path);
|
||||
var args = slice.call(arguments, 1);
|
||||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
@@ -507,10 +502,6 @@ app.all = function all(path) {
|
||||
return this;
|
||||
};
|
||||
|
||||
// del -> delete alias
|
||||
|
||||
app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');
|
||||
|
||||
/**
|
||||
* Render the given view `name` name with `options`
|
||||
* and a callback accepting an error and the
|
||||
@@ -532,8 +523,7 @@ app.render = function render(name, options, callback) {
|
||||
var cache = this.cache;
|
||||
var done = callback;
|
||||
var engines = this.engines;
|
||||
var opts = options;
|
||||
var renderOptions = {};
|
||||
var opts = options || {};
|
||||
var view;
|
||||
|
||||
// support callback function as second arg
|
||||
@@ -542,16 +532,8 @@ app.render = function render(name, options, callback) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
// merge app.locals
|
||||
merge(renderOptions, this.locals);
|
||||
|
||||
// merge options._locals
|
||||
if (opts._locals) {
|
||||
merge(renderOptions, opts._locals);
|
||||
}
|
||||
|
||||
// merge options
|
||||
merge(renderOptions, opts);
|
||||
var renderOptions = { ...this.locals, ...opts._locals, ...opts };
|
||||
|
||||
// set .cache unless explicitly provided
|
||||
if (renderOptions.cache == null) {
|
||||
@@ -601,8 +583,8 @@ app.render = function render(name, options, callback) {
|
||||
* and HTTPS server you may do so with the "http"
|
||||
* and "https" modules as shown here:
|
||||
*
|
||||
* var http = require('http')
|
||||
* , https = require('https')
|
||||
* var http = require('node:http')
|
||||
* , https = require('node:https')
|
||||
* , express = require('express')
|
||||
* , app = express();
|
||||
*
|
||||
@@ -614,9 +596,14 @@ app.render = function render(name, options, callback) {
|
||||
*/
|
||||
|
||||
app.listen = function listen() {
|
||||
var server = http.createServer(this);
|
||||
return server.listen.apply(server, arguments);
|
||||
};
|
||||
var server = http.createServer(this)
|
||||
var args = slice.call(arguments)
|
||||
if (typeof args[args.length - 1] === 'function') {
|
||||
var done = args[args.length - 1] = once(args[args.length - 1])
|
||||
server.once('error', done)
|
||||
}
|
||||
return server.listen.apply(server, args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Log error using console.error.
|
||||
|
||||
@@ -13,11 +13,10 @@
|
||||
*/
|
||||
|
||||
var bodyParser = require('body-parser')
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var EventEmitter = require('node:events').EventEmitter;
|
||||
var mixin = require('merge-descriptors');
|
||||
var proto = require('./application');
|
||||
var Route = require('./router/route');
|
||||
var Router = require('./router');
|
||||
var Router = require('router');
|
||||
var req = require('./request');
|
||||
var res = require('./response');
|
||||
|
||||
@@ -68,7 +67,7 @@ exports.response = res;
|
||||
* Expose constructors.
|
||||
*/
|
||||
|
||||
exports.Route = Route;
|
||||
exports.Route = Router.Route;
|
||||
exports.Router = Router;
|
||||
|
||||
/**
|
||||
@@ -76,41 +75,7 @@ exports.Router = Router;
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
var removedMiddlewares = [
|
||||
'bodyParser',
|
||||
'compress',
|
||||
'cookieSession',
|
||||
'session',
|
||||
'logger',
|
||||
'cookieParser',
|
||||
'favicon',
|
||||
'responseTime',
|
||||
'errorHandler',
|
||||
'timeout',
|
||||
'methodOverride',
|
||||
'vhost',
|
||||
'csrf',
|
||||
'directory',
|
||||
'limit',
|
||||
'multipart',
|
||||
'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.');
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var setPrototypeOf = require('setprototypeof')
|
||||
|
||||
/**
|
||||
* Initialization middleware, exposing the
|
||||
* request and response to each other, as well
|
||||
* as defaulting the X-Powered-By header field.
|
||||
*
|
||||
* @param {Function} app
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.init = function(app){
|
||||
return function expressInit(req, res, next){
|
||||
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
|
||||
req.res = res;
|
||||
res.req = req;
|
||||
req.next = next;
|
||||
|
||||
setPrototypeOf(req, app.request)
|
||||
setPrototypeOf(res, app.response)
|
||||
|
||||
res.locals = res.locals || Object.create(null);
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var merge = require('utils-merge')
|
||||
var parseUrl = require('parseurl');
|
||||
var qs = require('qs');
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function query(options) {
|
||||
var opts = merge({}, options)
|
||||
var queryparse = qs.parse;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
queryparse = options;
|
||||
opts = undefined;
|
||||
}
|
||||
|
||||
if (opts !== undefined && opts.allowPrototypes === undefined) {
|
||||
// back-compat for qs module
|
||||
opts.allowPrototypes = true;
|
||||
}
|
||||
|
||||
return function query(req, res, next){
|
||||
if (!req.query) {
|
||||
var val = parseUrl(req).query;
|
||||
req.query = queryparse(val, opts);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
144
lib/request.js
144
lib/request.js
@@ -14,10 +14,9 @@
|
||||
*/
|
||||
|
||||
var accepts = require('accepts');
|
||||
var deprecate = require('depd')('express');
|
||||
var isIP = require('net').isIP;
|
||||
var isIP = require('node:net').isIP;
|
||||
var typeis = require('type-is');
|
||||
var http = require('http');
|
||||
var http = require('node:http');
|
||||
var fresh = require('fresh');
|
||||
var parseRange = require('range-parser');
|
||||
var parse = require('parseurl');
|
||||
@@ -84,16 +83,13 @@ req.header = function header(name) {
|
||||
};
|
||||
|
||||
/**
|
||||
* To do: update docs.
|
||||
*
|
||||
* Check if the given `type(s)` is acceptable, returning
|
||||
* the best match when true, otherwise `undefined`, in which
|
||||
* the best match when true, otherwise `false`, in which
|
||||
* case you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* The `type` value may be a single MIME type string
|
||||
* such as "application/json", an extension name
|
||||
* such as "json", a comma-delimited list such as "json, html, text/plain",
|
||||
* an argument list such as `"json", "html", "text/plain"`,
|
||||
* such as "json", an argument list such as `"json", "html", "text/plain"`,
|
||||
* or an array `["json", "html", "text/plain"]`. When a list
|
||||
* or array is given, the _best_ match, if any is returned.
|
||||
*
|
||||
@@ -108,7 +104,7 @@ req.header = function header(name) {
|
||||
* // => "html"
|
||||
* req.accepts('text/html');
|
||||
* // => "text/html"
|
||||
* req.accepts('json, text');
|
||||
* req.accepts('json', 'text');
|
||||
* // => "json"
|
||||
* req.accepts('application/json');
|
||||
* // => "application/json"
|
||||
@@ -116,12 +112,11 @@ req.header = function header(name) {
|
||||
* // Accept: text/*, application/json
|
||||
* req.accepts('image/png');
|
||||
* req.accepts('png');
|
||||
* // => undefined
|
||||
* // => false
|
||||
*
|
||||
* // Accept: text/*;q=.5, application/json
|
||||
* req.accepts(['html', 'json']);
|
||||
* req.accepts('html', 'json');
|
||||
* req.accepts('html, json');
|
||||
* // => "json"
|
||||
*
|
||||
* @param {String|Array} type(s)
|
||||
@@ -147,26 +142,37 @@ req.acceptsEncodings = function(){
|
||||
return accept.encodings.apply(accept, arguments);
|
||||
};
|
||||
|
||||
req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
|
||||
'req.acceptsEncoding: Use acceptsEncodings instead');
|
||||
|
||||
/**
|
||||
* Check if the given `charset`s are acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
* Checks if the specified `charset`s are acceptable based on the request's `Accept-Charset` header.
|
||||
* Returns the best matching charset or an array of acceptable charsets.
|
||||
*
|
||||
* @param {String} ...charset
|
||||
* @return {String|Array}
|
||||
* The `charset` argument(s) can be:
|
||||
* - A single charset string (e.g., "utf-8")
|
||||
* - Multiple charset strings as arguments (e.g., `"utf-8", "iso-8859-1"`)
|
||||
* - A comma-delimited list of charsets (e.g., `"utf-8, iso-8859-1"`)
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* // Accept-Charset: utf-8, iso-8859-1
|
||||
* req.acceptsCharsets('utf-8');
|
||||
* // => "utf-8"
|
||||
*
|
||||
* req.acceptsCharsets('utf-8', 'iso-8859-1');
|
||||
* // => "utf-8"
|
||||
*
|
||||
* req.acceptsCharsets('utf-8, utf-16');
|
||||
* // => "utf-8"
|
||||
*
|
||||
* @param {...String} charsets - The charset(s) to check against the `Accept-Charset` header.
|
||||
* @return {String|Array} - The best matching charset, or an array of acceptable charsets.
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.acceptsCharsets = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.charsets.apply(accept, arguments);
|
||||
req.acceptsCharsets = function(...charsets) {
|
||||
const accept = accepts(this);
|
||||
return accept.charsets(...charsets);
|
||||
};
|
||||
|
||||
req.acceptsCharset = deprecate.function(req.acceptsCharsets,
|
||||
'req.acceptsCharset: Use acceptsCharsets instead');
|
||||
|
||||
/**
|
||||
* Check if the given `lang`s are acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
@@ -176,14 +182,10 @@ req.acceptsCharset = deprecate.function(req.acceptsCharsets,
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.acceptsLanguages = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.languages.apply(accept, arguments);
|
||||
req.acceptsLanguages = function(...languages) {
|
||||
return accepts(this).languages(...languages);
|
||||
};
|
||||
|
||||
req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
|
||||
'req.acceptsLanguage: Use acceptsLanguages instead');
|
||||
|
||||
/**
|
||||
* Parse Range header field, capping to the given `size`.
|
||||
*
|
||||
@@ -216,42 +218,31 @@ req.range = function range(size, options) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the value of param `name` when present or `defaultValue`.
|
||||
* Parse the query string of `req.url`.
|
||||
*
|
||||
* - Checks route placeholders, ex: _/user/:id_
|
||||
* - Checks body params, ex: id=12, {"id":12}
|
||||
* - Checks query string params, ex: ?id=12
|
||||
* This uses the "query parser" setting to parse the raw
|
||||
* string into an object.
|
||||
*
|
||||
* To utilize request bodies, `req.body`
|
||||
* should be an object. This can be done by using
|
||||
* the `bodyParser()` middleware.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Mixed} [defaultValue]
|
||||
* @return {String}
|
||||
* @public
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.param = function param(name, defaultValue) {
|
||||
var params = this.params || {};
|
||||
var body = this.body || {};
|
||||
var query = this.query || {};
|
||||
defineGetter(req, 'query', function query(){
|
||||
var queryparse = this.app.get('query parser fn');
|
||||
|
||||
var args = arguments.length === 1
|
||||
? 'name'
|
||||
: 'name, default';
|
||||
deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead');
|
||||
if (!queryparse) {
|
||||
// parsing is disabled
|
||||
return Object.create(null);
|
||||
}
|
||||
|
||||
if (null != params[name] && params.hasOwnProperty(name)) return params[name];
|
||||
if (null != body[name]) return body[name];
|
||||
if (null != query[name]) return query[name];
|
||||
var querystring = parse(this).query;
|
||||
|
||||
return defaultValue;
|
||||
};
|
||||
return queryparse(querystring);
|
||||
});
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
@@ -304,12 +295,12 @@ req.is = function is(types) {
|
||||
*/
|
||||
|
||||
defineGetter(req, 'protocol', function protocol(){
|
||||
var proto = this.connection.encrypted
|
||||
var proto = this.socket.encrypted
|
||||
? 'https'
|
||||
: 'http';
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
|
||||
if (!trust(this.connection.remoteAddress, 0)) {
|
||||
if (!trust(this.socket.remoteAddress, 0)) {
|
||||
return proto;
|
||||
}
|
||||
|
||||
@@ -414,7 +405,7 @@ defineGetter(req, 'path', function path() {
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse the "Host" header field to a hostname.
|
||||
* Parse the "Host" header field to a host.
|
||||
*
|
||||
* When the "trust proxy" setting trusts the socket
|
||||
* address, the "X-Forwarded-Host" header field will
|
||||
@@ -424,18 +415,35 @@ defineGetter(req, 'path', function path() {
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'hostname', function hostname(){
|
||||
defineGetter(req, 'host', function host(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var host = this.get('X-Forwarded-Host');
|
||||
var val = this.get('X-Forwarded-Host');
|
||||
|
||||
if (!host || !trust(this.connection.remoteAddress, 0)) {
|
||||
host = this.get('Host');
|
||||
} else if (host.indexOf(',') !== -1) {
|
||||
if (!val || !trust(this.socket.remoteAddress, 0)) {
|
||||
val = this.get('Host');
|
||||
} else if (val.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()
|
||||
val = val.substring(0, val.indexOf(',')).trimRight()
|
||||
}
|
||||
|
||||
return val || undefined;
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse the "Host" header field to a hostname.
|
||||
*
|
||||
* When the "trust proxy" setting trusts the socket
|
||||
* address, the "X-Forwarded-Host" header field will
|
||||
* be trusted.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'hostname', function hostname(){
|
||||
var host = this.host;
|
||||
|
||||
if (!host) return;
|
||||
|
||||
// IPv6 literal support
|
||||
@@ -449,15 +457,9 @@ defineGetter(req, 'hostname', function hostname(){
|
||||
: host;
|
||||
});
|
||||
|
||||
// TODO: change req.host to return host in next major
|
||||
|
||||
defineGetter(req, 'host', deprecate.function(function host(){
|
||||
return this.hostname;
|
||||
}, 'req.host: Use req.hostname instead'));
|
||||
|
||||
/**
|
||||
* Check if the request is fresh, aka
|
||||
* Last-Modified and/or the ETag
|
||||
* Last-Modified or the ETag
|
||||
* still match.
|
||||
*
|
||||
* @return {Boolean}
|
||||
|
||||
359
lib/response.js
359
lib/response.js
@@ -12,17 +12,17 @@
|
||||
* @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');
|
||||
var http = require('http');
|
||||
var isAbsolute = require('./utils').isAbsolute;
|
||||
var http = require('node:http');
|
||||
var onFinished = require('on-finished');
|
||||
var path = require('path');
|
||||
var mime = require('mime-types')
|
||||
var path = require('node:path');
|
||||
var pathIsAbsolute = require('node:path').isAbsolute;
|
||||
var statuses = require('statuses')
|
||||
var merge = require('utils-merge');
|
||||
var sign = require('cookie-signature').sign;
|
||||
var normalizeType = require('./utils').normalizeType;
|
||||
var normalizeTypes = require('./utils').normalizeTypes;
|
||||
@@ -30,9 +30,9 @@ var setCharset = require('./utils').setCharset;
|
||||
var cookie = require('cookie');
|
||||
var send = require('send');
|
||||
var extname = path.extname;
|
||||
var mime = send.mime;
|
||||
var resolve = path.resolve;
|
||||
var vary = require('vary');
|
||||
const { Buffer } = require('node:buffer');
|
||||
|
||||
/**
|
||||
* Response prototype.
|
||||
@@ -49,21 +49,28 @@ var res = Object.create(http.ServerResponse.prototype)
|
||||
module.exports = res
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var charsetRegExp = /;\s*charset\s*=/;
|
||||
|
||||
/**
|
||||
* Set status `code`.
|
||||
* Set the HTTP status code for the response.
|
||||
*
|
||||
* @param {Number} code
|
||||
* @return {ServerResponse}
|
||||
* Expects an integer value between 100 and 999 inclusive.
|
||||
* Throws an error if the provided status code is not an integer or if it's outside the allowable range.
|
||||
*
|
||||
* @param {number} code - The HTTP status code to set.
|
||||
* @return {ServerResponse} - Returns itself for chaining methods.
|
||||
* @throws {TypeError} If `code` is not an integer.
|
||||
* @throws {RangeError} If `code` is outside the range 100 to 999.
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.status = function status(code) {
|
||||
// Check if the status code is not an integer
|
||||
if (!Number.isInteger(code)) {
|
||||
throw new TypeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be an integer.`);
|
||||
}
|
||||
// Check if the status code is outside of Node's valid range
|
||||
if (code < 100 || code > 999) {
|
||||
throw new RangeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be greater than 99 and less than 1000.`);
|
||||
}
|
||||
|
||||
this.statusCode = code;
|
||||
return this;
|
||||
};
|
||||
@@ -75,7 +82,11 @@ res.status = function status(code) {
|
||||
*
|
||||
* res.links({
|
||||
* next: 'http://api.example.com/users?page=2',
|
||||
* last: 'http://api.example.com/users?page=5'
|
||||
* last: 'http://api.example.com/users?page=5',
|
||||
* pages: [
|
||||
* 'http://api.example.com/users?page=1',
|
||||
* 'http://api.example.com/users?page=2'
|
||||
* ]
|
||||
* });
|
||||
*
|
||||
* @param {Object} links
|
||||
@@ -83,11 +94,18 @@ res.status = function status(code) {
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.links = function(links){
|
||||
res.links = function(links) {
|
||||
var link = this.get('Link') || '';
|
||||
if (link) link += ', ';
|
||||
return this.set('Link', link + Object.keys(links).map(function(rel){
|
||||
return '<' + links[rel] + '>; rel="' + rel + '"';
|
||||
return this.set('Link', link + Object.keys(links).map(function(rel) {
|
||||
// Allow multiple links if links[rel] is an array
|
||||
if (Array.isArray(links[rel])) {
|
||||
return links[rel].map(function (singleLink) {
|
||||
return `<${singleLink}>; rel="${rel}"`;
|
||||
}).join(', ');
|
||||
} else {
|
||||
return `<${links[rel]}>; rel="${rel}"`;
|
||||
}
|
||||
}).join(', '));
|
||||
};
|
||||
|
||||
@@ -108,40 +126,19 @@ res.send = function send(body) {
|
||||
var chunk = body;
|
||||
var encoding;
|
||||
var req = this.req;
|
||||
var type;
|
||||
|
||||
// settings
|
||||
var app = this.app;
|
||||
|
||||
// allow status / body
|
||||
if (arguments.length === 2) {
|
||||
// res.send(body, status) backwards compat
|
||||
if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
|
||||
deprecate('res.send(body, status): Use res.status(status).send(body) instead');
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
deprecate('res.send(status, body): Use res.status(status).send(body) instead');
|
||||
this.statusCode = arguments[0];
|
||||
chunk = arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
// disambiguate res.send(status) and res.send(status, num)
|
||||
if (typeof chunk === 'number' && arguments.length === 1) {
|
||||
// res.send(status) will set status message as text string
|
||||
if (!this.get('Content-Type')) {
|
||||
this.type('txt');
|
||||
}
|
||||
|
||||
deprecate('res.send(status): Use res.sendStatus(status) instead');
|
||||
this.statusCode = chunk;
|
||||
chunk = statuses[chunk]
|
||||
}
|
||||
|
||||
switch (typeof chunk) {
|
||||
// string defaulting to html
|
||||
case 'string':
|
||||
if (!this.get('Content-Type')) {
|
||||
encoding = 'utf8';
|
||||
const type = this.get('Content-Type');
|
||||
|
||||
if (typeof type === 'string') {
|
||||
this.set('Content-Type', setCharset(type, 'utf-8'));
|
||||
} else {
|
||||
this.type('html');
|
||||
}
|
||||
break;
|
||||
@@ -150,7 +147,7 @@ res.send = function send(body) {
|
||||
case 'object':
|
||||
if (chunk === null) {
|
||||
chunk = '';
|
||||
} else if (Buffer.isBuffer(chunk)) {
|
||||
} else if (ArrayBuffer.isView(chunk)) {
|
||||
if (!this.get('Content-Type')) {
|
||||
this.type('bin');
|
||||
}
|
||||
@@ -160,17 +157,6 @@ res.send = function send(body) {
|
||||
break;
|
||||
}
|
||||
|
||||
// write strings in utf-8
|
||||
if (typeof chunk === 'string') {
|
||||
encoding = 'utf8';
|
||||
type = this.get('Content-Type');
|
||||
|
||||
// reflect this in content-type
|
||||
if (typeof type === 'string') {
|
||||
this.set('Content-Type', setCharset(type, 'utf-8'));
|
||||
}
|
||||
}
|
||||
|
||||
// determine if ETag should be generated
|
||||
var etagFn = app.get('etag fn')
|
||||
var generateETag = !this.get('ETag') && typeof etagFn === 'function'
|
||||
@@ -203,7 +189,7 @@ res.send = function send(body) {
|
||||
}
|
||||
|
||||
// freshness
|
||||
if (req.fresh) this.statusCode = 304;
|
||||
if (req.fresh) this.status(304);
|
||||
|
||||
// strip irrelevant headers
|
||||
if (204 === this.statusCode || 304 === this.statusCode) {
|
||||
@@ -213,6 +199,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();
|
||||
@@ -237,27 +230,12 @@ res.send = function send(body) {
|
||||
*/
|
||||
|
||||
res.json = function json(obj) {
|
||||
var val = obj;
|
||||
|
||||
// allow status / body
|
||||
if (arguments.length === 2) {
|
||||
// res.json(body, status) backwards compat
|
||||
if (typeof arguments[1] === 'number') {
|
||||
deprecate('res.json(obj, status): Use res.status(status).json(obj) instead');
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
deprecate('res.json(status, obj): Use res.status(status).json(obj) instead');
|
||||
this.statusCode = arguments[0];
|
||||
val = arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
// 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, escape)
|
||||
var body = stringify(obj, replacer, spaces, escape)
|
||||
|
||||
// content-type
|
||||
if (!this.get('Content-Type')) {
|
||||
@@ -280,27 +258,12 @@ res.json = function json(obj) {
|
||||
*/
|
||||
|
||||
res.jsonp = function jsonp(obj) {
|
||||
var val = obj;
|
||||
|
||||
// allow status / body
|
||||
if (arguments.length === 2) {
|
||||
// res.json(body, status) backwards compat
|
||||
if (typeof arguments[1] === 'number') {
|
||||
deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead');
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead');
|
||||
this.statusCode = arguments[0];
|
||||
val = arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
// 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, escape)
|
||||
var body = stringify(obj, replacer, spaces, escape)
|
||||
var callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
@@ -322,10 +285,15 @@ res.jsonp = function jsonp(obj) {
|
||||
// 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
|
||||
@@ -351,9 +319,9 @@ 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.status(statusCode);
|
||||
this.type('txt');
|
||||
|
||||
return this.send(body);
|
||||
@@ -364,7 +332,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.
|
||||
*
|
||||
@@ -421,12 +389,15 @@ res.sendFile = function sendFile(path, options, callback) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
if (!opts.root && !isAbsolute(path)) {
|
||||
if (!opts.root && !pathIsAbsolute(path)) {
|
||||
throw new TypeError('path must be absolute or specify root to res.sendFile');
|
||||
}
|
||||
|
||||
// create file stream
|
||||
var pathname = encodeURI(path);
|
||||
|
||||
// wire application etag option to send
|
||||
opts.etag = this.app.enabled('etag');
|
||||
var file = send(req, pathname, opts);
|
||||
|
||||
// transfer
|
||||
@@ -441,85 +412,13 @@ res.sendFile = function sendFile(path, options, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
* 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`
|
||||
* if you wish to attempt responding, as the header and some data
|
||||
* may have already been transferred.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` defaulting to 0 (can be string converted by `ms`)
|
||||
* - `root` root directory for relative filenames
|
||||
* - `headers` object of headers to serve with file
|
||||
* - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
|
||||
*
|
||||
* Other options are passed along to `send`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* The following example illustrates how `res.sendfile()` may
|
||||
* be used as an alternative for the `static()` middleware for
|
||||
* dynamic situations. The code backing `res.sendfile()` is actually
|
||||
* the same code, so HTTP cache support etc is identical.
|
||||
*
|
||||
* app.get('/user/:uid/photos/:file', function(req, res){
|
||||
* var uid = req.params.uid
|
||||
* , file = req.params.file;
|
||||
*
|
||||
* req.user.mayViewFilesFrom(uid, function(yes){
|
||||
* if (yes) {
|
||||
* res.sendfile('/uploads/' + uid + '/' + file);
|
||||
* } else {
|
||||
* res.send(403, 'Sorry! you cant see that.');
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.sendfile = function (path, options, callback) {
|
||||
var done = callback;
|
||||
var req = this.req;
|
||||
var res = this;
|
||||
var next = req.next;
|
||||
var opts = options || {};
|
||||
|
||||
// support function as second arg
|
||||
if (typeof options === 'function') {
|
||||
done = options;
|
||||
opts = {};
|
||||
}
|
||||
|
||||
// create file stream
|
||||
var file = send(req, path, opts);
|
||||
|
||||
// transfer
|
||||
sendfile(res, file, opts, function (err) {
|
||||
if (done) return done(err);
|
||||
if (err && err.code === 'EISDIR') return next();
|
||||
|
||||
// next() all but write errors
|
||||
if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
res.sendfile = deprecate.function(res.sendfile,
|
||||
'res.sendfile: Use res.sendFile instead');
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path` as an attachment.
|
||||
*
|
||||
* Optionally providing an alternate attachment `filename`,
|
||||
* and optional callback `callback(err)`. The callback is invoked
|
||||
* when the data transfer is complete, or when an error has
|
||||
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
|
||||
* occurred. Be sure to check `res.headersSent` if you plan to respond.
|
||||
*
|
||||
* Optionally providing an `options` object to use with `res.sendFile()`.
|
||||
* This function will set the `Content-Disposition` header, overriding
|
||||
@@ -546,6 +445,13 @@ res.download = function download (path, filename, options, callback) {
|
||||
opts = null
|
||||
}
|
||||
|
||||
// support optional filename, where options may be in it's place
|
||||
if (typeof filename === 'object' &&
|
||||
(typeof options === 'function' || options === undefined)) {
|
||||
name = null
|
||||
opts = filename
|
||||
}
|
||||
|
||||
// set Content-Disposition when file is sent
|
||||
var headers = {
|
||||
'Content-Disposition': contentDisposition(name || path)
|
||||
@@ -567,15 +473,19 @@ res.download = function download (path, filename, options, callback) {
|
||||
opts.headers = headers
|
||||
|
||||
// Resolve the full path for sendFile
|
||||
var fullPath = resolve(path);
|
||||
var fullPath = !opts.root
|
||||
? resolve(path)
|
||||
: path
|
||||
|
||||
// send file
|
||||
return this.sendFile(fullPath, opts, done)
|
||||
};
|
||||
|
||||
/**
|
||||
* Set _Content-Type_ response header with `type` through `mime.lookup()`
|
||||
* Set _Content-Type_ response header with `type` through `mime.contentType()`
|
||||
* when it does not contain "/", or set the Content-Type to `type` otherwise.
|
||||
* When no mapping is found though `mime.contentType()`, the type is set to
|
||||
* "application/octet-stream".
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -593,7 +503,7 @@ res.download = function download (path, filename, options, callback) {
|
||||
res.contentType =
|
||||
res.type = function contentType(type) {
|
||||
var ct = type.indexOf('/') === -1
|
||||
? mime.lookup(type)
|
||||
? (mime.contentType(type) || 'application/octet-stream')
|
||||
: type;
|
||||
|
||||
return this.set('Content-Type', ct);
|
||||
@@ -623,7 +533,7 @@ res.type = function contentType(type) {
|
||||
* res.send('<p>hey</p>');
|
||||
* },
|
||||
*
|
||||
* 'appliation/json': function(){
|
||||
* 'application/json': function () {
|
||||
* res.send({ message: 'hey' });
|
||||
* }
|
||||
* });
|
||||
@@ -660,9 +570,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)
|
||||
@@ -673,13 +582,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;
|
||||
@@ -726,7 +634,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);
|
||||
@@ -744,6 +652,9 @@ res.append = function append(field, val) {
|
||||
*
|
||||
* Aliased as `res.header()`.
|
||||
*
|
||||
* When the set header is "Content-Type", the type is expanded to include
|
||||
* the charset if not present using `mime.contentType()`.
|
||||
*
|
||||
* @param {String|Object} field
|
||||
* @param {String|Array} val
|
||||
* @return {ServerResponse} for chaining
|
||||
@@ -762,10 +673,7 @@ res.header = function header(field, val) {
|
||||
if (Array.isArray(value)) {
|
||||
throw new TypeError('Content-Type cannot be set to an Array');
|
||||
}
|
||||
if (!charsetRegExp.test(value)) {
|
||||
var charset = mime.charsets.lookup(value.split(';')[0]);
|
||||
if (charset) value += '; charset=' + charset.toLowerCase();
|
||||
}
|
||||
value = mime.contentType(value)
|
||||
}
|
||||
|
||||
this.setHeader(field, value);
|
||||
@@ -799,7 +707,10 @@ res.get = function(field){
|
||||
*/
|
||||
|
||||
res.clearCookie = function clearCookie(name, options) {
|
||||
var opts = merge({ expires: new Date(1), path: '/' }, options);
|
||||
// Force cookie expiration by setting expires to the past
|
||||
const opts = { path: '/', ...options, expires: new Date(1)};
|
||||
// ensure maxAge is not passed
|
||||
delete opts.maxAge
|
||||
|
||||
return this.cookie(name, '', opts);
|
||||
};
|
||||
@@ -829,7 +740,7 @@ res.clearCookie = function clearCookie(name, options) {
|
||||
*/
|
||||
|
||||
res.cookie = function (name, value, options) {
|
||||
var opts = merge({}, options);
|
||||
var opts = { ...options };
|
||||
var secret = this.req.secret;
|
||||
var signed = opts.signed;
|
||||
|
||||
@@ -845,9 +756,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) {
|
||||
@@ -877,25 +792,13 @@ res.cookie = function (name, value, options) {
|
||||
*/
|
||||
|
||||
res.location = function location(url) {
|
||||
var loc = url;
|
||||
|
||||
// "back" is an alias for the referrer
|
||||
if (url === 'back') {
|
||||
loc = this.req.get('Referrer') || '/';
|
||||
}
|
||||
|
||||
// set location
|
||||
return this.set('Location', encodeUrl(loc));
|
||||
return this.set('Location', encodeUrl(url));
|
||||
};
|
||||
|
||||
/**
|
||||
* Redirect to the given `url` with optional response `status`
|
||||
* defaulting to 302.
|
||||
*
|
||||
* The resulting `url` is determined by `res.location()`, so
|
||||
* it will play nicely with mounted apps, relative paths,
|
||||
* `"back"` etc.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.redirect('/foo/bar');
|
||||
@@ -913,13 +816,20 @@ res.redirect = function redirect(url) {
|
||||
|
||||
// allow status / url
|
||||
if (arguments.length === 2) {
|
||||
if (typeof arguments[0] === 'number') {
|
||||
status = arguments[0];
|
||||
address = arguments[1];
|
||||
} else {
|
||||
deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
|
||||
status = arguments[1];
|
||||
}
|
||||
status = arguments[0]
|
||||
address = arguments[1]
|
||||
}
|
||||
|
||||
if (!address) {
|
||||
deprecate('Provide a url argument');
|
||||
}
|
||||
|
||||
if (typeof address !== 'string') {
|
||||
deprecate('Url must be a string');
|
||||
}
|
||||
|
||||
if (typeof status !== 'number') {
|
||||
deprecate('Status must be a number');
|
||||
}
|
||||
|
||||
// Set location header
|
||||
@@ -928,12 +838,13 @@ 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 = '<!DOCTYPE html><head><title>' + statuses.message[status] + '</title></head>'
|
||||
+ '<body><p>' + statuses.message[status] + '. Redirecting to ' + u + '</p></body>'
|
||||
},
|
||||
|
||||
default: function(){
|
||||
@@ -942,7 +853,7 @@ res.redirect = function redirect(url) {
|
||||
});
|
||||
|
||||
// Respond
|
||||
this.statusCode = status;
|
||||
this.status(status);
|
||||
this.set('Content-Length', Buffer.byteLength(body));
|
||||
|
||||
if (this.req.method === 'HEAD') {
|
||||
@@ -962,12 +873,6 @@ res.redirect = function redirect(url) {
|
||||
*/
|
||||
|
||||
res.vary = function(field){
|
||||
// checks for back-compat
|
||||
if (!field || (Array.isArray(field) && !field.length)) {
|
||||
deprecate('res.vary(): Provide a field name');
|
||||
return this;
|
||||
}
|
||||
|
||||
vary(this, field);
|
||||
|
||||
return this;
|
||||
@@ -1108,7 +1013,7 @@ function sendfile(res, file, options, callback) {
|
||||
* ability to escape characters that can trigger HTML sniffing.
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {function} replaces
|
||||
* @param {function} replacer
|
||||
* @param {number} spaces
|
||||
* @param {boolean} escape
|
||||
* @returns {string}
|
||||
@@ -1122,7 +1027,7 @@ function stringify (value, replacer, spaces, escape) {
|
||||
? JSON.stringify(value, replacer, spaces)
|
||||
: JSON.stringify(value);
|
||||
|
||||
if (escape) {
|
||||
if (escape && typeof json === 'string') {
|
||||
json = json.replace(/[<>&]/g, function (c) {
|
||||
switch (c.charCodeAt(0)) {
|
||||
case 0x3c:
|
||||
|
||||
@@ -1,662 +0,0 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Route = require('./route');
|
||||
var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
var mixin = require('utils-merge');
|
||||
var debug = require('debug')('express:router');
|
||||
var deprecate = require('depd')('express');
|
||||
var flatten = require('array-flatten');
|
||||
var parseUrl = require('parseurl');
|
||||
var setPrototypeOf = require('setprototypeof')
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var objectRegExp = /^\[object (\S+)\]$/;
|
||||
var slice = Array.prototype.slice;
|
||||
var toString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @return {Router} which is an callable function
|
||||
* @public
|
||||
*/
|
||||
|
||||
var proto = module.exports = function(options) {
|
||||
var opts = options || {};
|
||||
|
||||
function router(req, res, next) {
|
||||
router.handle(req, res, next);
|
||||
}
|
||||
|
||||
// mixin Router class functions
|
||||
setPrototypeOf(router, proto)
|
||||
|
||||
router.params = {};
|
||||
router._params = [];
|
||||
router.caseSensitive = opts.caseSensitive;
|
||||
router.mergeParams = opts.mergeParams;
|
||||
router.strict = opts.strict;
|
||||
router.stack = [];
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map the given param placeholder `name`(s) to the given callback.
|
||||
*
|
||||
* Parameter mapping is used to provide pre-conditions to routes
|
||||
* which use normalized placeholders. For example a _:user_id_ parameter
|
||||
* could automatically load a user's information from the database without
|
||||
* any additional code,
|
||||
*
|
||||
* The callback uses the same signature as middleware, the only difference
|
||||
* being that the value of the placeholder is passed, in this case the _id_
|
||||
* of the user. Once the `next()` function is invoked, just like middleware
|
||||
* it will continue on to execute the route, or subsequent parameter functions.
|
||||
*
|
||||
* Just like in middleware, you must either respond to the request or call next
|
||||
* to avoid stalling the request.
|
||||
*
|
||||
* app.param('user_id', function(req, res, next, id){
|
||||
* User.find(id, function(err, user){
|
||||
* if (err) {
|
||||
* return next(err);
|
||||
* } else if (!user) {
|
||||
* return next(new Error('failed to load user'));
|
||||
* }
|
||||
* req.user = user;
|
||||
* next();
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @public
|
||||
*/
|
||||
|
||||
proto.param = function param(name, fn) {
|
||||
// param logic
|
||||
if (typeof name === 'function') {
|
||||
deprecate('router.param(fn): Refactor to use path params');
|
||||
this._params.push(name);
|
||||
return;
|
||||
}
|
||||
|
||||
// apply param functions
|
||||
var params = this._params;
|
||||
var len = params.length;
|
||||
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);
|
||||
}
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (ret = params[i](name, fn)) {
|
||||
fn = ret;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure we end up with a
|
||||
// middleware function
|
||||
if ('function' !== typeof fn) {
|
||||
throw new Error('invalid param() call for ' + name + ', got ' + fn);
|
||||
}
|
||||
|
||||
(this.params[name] = this.params[name] || []).push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch a req, res into the router.
|
||||
* @private
|
||||
*/
|
||||
|
||||
proto.handle = function handle(req, res, out) {
|
||||
var self = this;
|
||||
|
||||
debug('dispatching %s %s', req.method, req.url);
|
||||
|
||||
var idx = 0;
|
||||
var protohost = getProtohost(req.url) || ''
|
||||
var removed = '';
|
||||
var slashAdded = false;
|
||||
var paramcalled = {};
|
||||
|
||||
// store options for OPTIONS request
|
||||
// only used if OPTIONS request
|
||||
var options = [];
|
||||
|
||||
// middleware and routes
|
||||
var stack = self.stack;
|
||||
|
||||
// manage inter-router variables
|
||||
var parentParams = req.params;
|
||||
var parentUrl = req.baseUrl || '';
|
||||
var done = restore(out, req, 'baseUrl', 'next', 'params');
|
||||
|
||||
// setup next layer
|
||||
req.next = next;
|
||||
|
||||
// for options requests, respond with a default if nothing else responds
|
||||
if (req.method === 'OPTIONS') {
|
||||
done = wrap(done, function(old, err) {
|
||||
if (err || options.length === 0) return old(err);
|
||||
sendOptionsResponse(res, options, old);
|
||||
});
|
||||
}
|
||||
|
||||
// setup basic req values
|
||||
req.baseUrl = parentUrl;
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
|
||||
next();
|
||||
|
||||
function next(err) {
|
||||
var layerError = err === 'route'
|
||||
? null
|
||||
: err;
|
||||
|
||||
// remove added slash
|
||||
if (slashAdded) {
|
||||
req.url = req.url.substr(1);
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
// restore altered req.url
|
||||
if (removed.length !== 0) {
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
removed = '';
|
||||
}
|
||||
|
||||
// signal to exit router
|
||||
if (layerError === 'router') {
|
||||
setImmediate(done, null)
|
||||
return
|
||||
}
|
||||
|
||||
// no more matching layers
|
||||
if (idx >= stack.length) {
|
||||
setImmediate(done, layerError);
|
||||
return;
|
||||
}
|
||||
|
||||
// get pathname of request
|
||||
var path = getPathname(req);
|
||||
|
||||
if (path == null) {
|
||||
return done(layerError);
|
||||
}
|
||||
|
||||
// find next matching layer
|
||||
var layer;
|
||||
var match;
|
||||
var route;
|
||||
|
||||
while (match !== true && idx < stack.length) {
|
||||
layer = stack[idx++];
|
||||
match = matchLayer(layer, path);
|
||||
route = layer.route;
|
||||
|
||||
if (typeof match !== 'boolean') {
|
||||
// hold on to layerError
|
||||
layerError = layerError || match;
|
||||
}
|
||||
|
||||
if (match !== true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!route) {
|
||||
// process non-route handlers normally
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layerError) {
|
||||
// routes do not match with a pending error
|
||||
match = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
var method = req.method;
|
||||
var has_method = route._handles_method(method);
|
||||
|
||||
// build up automatic options response
|
||||
if (!has_method && method === 'OPTIONS') {
|
||||
appendMethods(options, route._options());
|
||||
}
|
||||
|
||||
// don't even bother matching route
|
||||
if (!has_method && method !== 'HEAD') {
|
||||
match = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// no match
|
||||
if (match !== true) {
|
||||
return done(layerError);
|
||||
}
|
||||
|
||||
// store route for dispatch on change
|
||||
if (route) {
|
||||
req.route = route;
|
||||
}
|
||||
|
||||
// Capture one-time layer values
|
||||
req.params = self.mergeParams
|
||||
? mergeParams(layer.params, parentParams)
|
||||
: layer.params;
|
||||
var layerPath = layer.path;
|
||||
|
||||
// this should be done for the layer
|
||||
self.process_params(layer, paramcalled, req, res, function (err) {
|
||||
if (err) {
|
||||
return next(layerError || err);
|
||||
}
|
||||
|
||||
if (route) {
|
||||
return layer.handle_request(req, res, next);
|
||||
}
|
||||
|
||||
trim_prefix(layer, layerError, layerPath, path);
|
||||
});
|
||||
}
|
||||
|
||||
function trim_prefix(layer, layerError, layerPath, path) {
|
||||
if (layerPath.length !== 0) {
|
||||
// Validate path breaks on a path separator
|
||||
var c = path[layerPath.length]
|
||||
if (c && c !== '/' && c !== '.') return next(layerError)
|
||||
|
||||
// Trim off the part of the url that matches the route
|
||||
// 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);
|
||||
|
||||
// Ensure leading slash
|
||||
if (!protohost && req.url[0] !== '/') {
|
||||
req.url = '/' + req.url;
|
||||
slashAdded = true;
|
||||
}
|
||||
|
||||
// Setup base URL (no trailing slash)
|
||||
req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
|
||||
? removed.substring(0, removed.length - 1)
|
||||
: removed);
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
|
||||
|
||||
if (layerError) {
|
||||
layer.handle_error(layerError, req, res, next);
|
||||
} else {
|
||||
layer.handle_request(req, res, next);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Process any parameters for the layer.
|
||||
* @private
|
||||
*/
|
||||
|
||||
proto.process_params = function process_params(layer, called, req, res, done) {
|
||||
var params = this.params;
|
||||
|
||||
// captured parameters from the layer, keys and values
|
||||
var keys = layer.keys;
|
||||
|
||||
// fast track
|
||||
if (!keys || keys.length === 0) {
|
||||
return done();
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var name;
|
||||
var paramIndex = 0;
|
||||
var key;
|
||||
var paramVal;
|
||||
var paramCallbacks;
|
||||
var paramCalled;
|
||||
|
||||
// process params in order
|
||||
// param callbacks can be async
|
||||
function param(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
if (i >= keys.length ) {
|
||||
return done();
|
||||
}
|
||||
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
name = key.name;
|
||||
paramVal = req.params[name];
|
||||
paramCallbacks = params[name];
|
||||
paramCalled = called[name];
|
||||
|
||||
if (paramVal === undefined || !paramCallbacks) {
|
||||
return param();
|
||||
}
|
||||
|
||||
// param previously called with same value or error occurred
|
||||
if (paramCalled && (paramCalled.match === paramVal
|
||||
|| (paramCalled.error && paramCalled.error !== 'route'))) {
|
||||
// restore value
|
||||
req.params[name] = paramCalled.value;
|
||||
|
||||
// next param
|
||||
return param(paramCalled.error);
|
||||
}
|
||||
|
||||
called[name] = paramCalled = {
|
||||
error: null,
|
||||
match: paramVal,
|
||||
value: paramVal
|
||||
};
|
||||
|
||||
paramCallback();
|
||||
}
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
|
||||
// store updated value
|
||||
paramCalled.value = req.params[key.name];
|
||||
|
||||
if (err) {
|
||||
// store error
|
||||
paramCalled.error = err;
|
||||
param(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fn) return param();
|
||||
|
||||
try {
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
} catch (e) {
|
||||
paramCallback(e);
|
||||
}
|
||||
}
|
||||
|
||||
param();
|
||||
};
|
||||
|
||||
/**
|
||||
* Use the given middleware function, with optional path, defaulting to "/".
|
||||
*
|
||||
* Use (like `.all`) will run for any http METHOD, but it will not add
|
||||
* handlers for those methods so OPTIONS requests will not consider `.use`
|
||||
* functions even if they could respond.
|
||||
*
|
||||
* The other difference is that _route_ path is stripped and not visible
|
||||
* to the handler function. The main effect of this feature is that mounted
|
||||
* handlers can operate without any code changes regardless of the "prefix"
|
||||
* pathname.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
proto.use = function use(fn) {
|
||||
var offset = 0;
|
||||
var path = '/';
|
||||
|
||||
// default path to '/'
|
||||
// disambiguate router.use([fn])
|
||||
if (typeof fn !== 'function') {
|
||||
var arg = fn;
|
||||
|
||||
while (Array.isArray(arg) && arg.length !== 0) {
|
||||
arg = arg[0];
|
||||
}
|
||||
|
||||
// first arg is the path
|
||||
if (typeof arg !== 'function') {
|
||||
offset = 1;
|
||||
path = fn;
|
||||
}
|
||||
}
|
||||
|
||||
var callbacks = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (callbacks.length === 0) {
|
||||
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 a middleware function but got a ' + gettype(fn))
|
||||
}
|
||||
|
||||
// add the middleware
|
||||
debug('use %o %s', path, fn.name || '<anonymous>')
|
||||
|
||||
var layer = new Layer(path, {
|
||||
sensitive: this.caseSensitive,
|
||||
strict: false,
|
||||
end: false
|
||||
}, fn);
|
||||
|
||||
layer.route = undefined;
|
||||
|
||||
this.stack.push(layer);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new Route for the given path.
|
||||
*
|
||||
* Each route contains a separate middleware stack and VERB handlers.
|
||||
*
|
||||
* See the Route api documentation for details on adding handlers
|
||||
* and middleware to routes.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Route}
|
||||
* @public
|
||||
*/
|
||||
|
||||
proto.route = function route(path) {
|
||||
var route = new Route(path);
|
||||
|
||||
var layer = new Layer(path, {
|
||||
sensitive: this.caseSensitive,
|
||||
strict: this.strict,
|
||||
end: true
|
||||
}, route.dispatch.bind(route));
|
||||
|
||||
layer.route = route;
|
||||
|
||||
this.stack.push(layer);
|
||||
return route;
|
||||
};
|
||||
|
||||
// create Router#VERB functions
|
||||
methods.concat('all').forEach(function(method){
|
||||
proto[method] = function(path){
|
||||
var route = this.route(path)
|
||||
route[method].apply(route, slice.call(arguments, 1));
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
// append methods to a list of methods
|
||||
function appendMethods(list, addition) {
|
||||
for (var i = 0; i < addition.length; i++) {
|
||||
var method = addition[i];
|
||||
if (list.indexOf(method) === -1) {
|
||||
list.push(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get pathname of request
|
||||
function getPathname(req) {
|
||||
try {
|
||||
return parseUrl(req).pathname;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Get get protocol + host for a URL
|
||||
function getProtohost(url) {
|
||||
if (typeof url !== 'string' || url.length === 0 || url[0] === '/') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
var searchIndex = url.indexOf('?')
|
||||
var pathLength = searchIndex !== -1
|
||||
? searchIndex
|
||||
: url.length
|
||||
var fqdnIndex = url.substr(0, pathLength).indexOf('://')
|
||||
|
||||
return fqdnIndex !== -1
|
||||
? url.substr(0, url.indexOf('/', 3 + fqdnIndex))
|
||||
: undefined
|
||||
}
|
||||
|
||||
// get type for error message
|
||||
function gettype(obj) {
|
||||
var type = typeof obj;
|
||||
|
||||
if (type !== 'object') {
|
||||
return type;
|
||||
}
|
||||
|
||||
// inspect [[Class]] for objects
|
||||
return toString.call(obj)
|
||||
.replace(objectRegExp, '$1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Match path to a layer.
|
||||
*
|
||||
* @param {Layer} layer
|
||||
* @param {string} path
|
||||
* @private
|
||||
*/
|
||||
|
||||
function matchLayer(layer, path) {
|
||||
try {
|
||||
return layer.match(path);
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// merge params with parent params
|
||||
function mergeParams(params, parent) {
|
||||
if (typeof parent !== 'object' || !parent) {
|
||||
return params;
|
||||
}
|
||||
|
||||
// make copy of parent for base
|
||||
var obj = mixin({}, parent);
|
||||
|
||||
// simple non-numeric merging
|
||||
if (!(0 in params) || !(0 in parent)) {
|
||||
return mixin(obj, params);
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var o = 0;
|
||||
|
||||
// determine numeric gaps
|
||||
while (i in params) {
|
||||
i++;
|
||||
}
|
||||
|
||||
while (o in parent) {
|
||||
o++;
|
||||
}
|
||||
|
||||
// offset numeric indices in params before merge
|
||||
for (i--; i >= 0; i--) {
|
||||
params[i + o] = params[i];
|
||||
|
||||
// create holes for the merge when necessary
|
||||
if (i < o) {
|
||||
delete params[i];
|
||||
}
|
||||
}
|
||||
|
||||
return mixin(obj, params);
|
||||
}
|
||||
|
||||
// restore obj props after function
|
||||
function restore(fn, obj) {
|
||||
var props = new Array(arguments.length - 2);
|
||||
var vals = new Array(arguments.length - 2);
|
||||
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
props[i] = arguments[i + 2];
|
||||
vals[i] = obj[props[i]];
|
||||
}
|
||||
|
||||
return function () {
|
||||
// restore vals
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
obj[props[i]] = vals[i];
|
||||
}
|
||||
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
// send an OPTIONS response
|
||||
function sendOptionsResponse(res, options, next) {
|
||||
try {
|
||||
var body = options.join(',');
|
||||
res.set('Allow', body);
|
||||
res.send(body);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
// wrap a function
|
||||
function wrap(old, fn) {
|
||||
return function proxy() {
|
||||
var args = new Array(arguments.length + 1);
|
||||
|
||||
args[0] = old;
|
||||
for (var i = 0, len = arguments.length; i < len; i++) {
|
||||
args[i + 1] = arguments[i];
|
||||
}
|
||||
|
||||
fn.apply(this, args);
|
||||
};
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var pathRegexp = require('path-to-regexp');
|
||||
var debug = require('debug')('express:router:layer');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = Layer;
|
||||
|
||||
function Layer(path, options, fn) {
|
||||
if (!(this instanceof Layer)) {
|
||||
return new Layer(path, options, fn);
|
||||
}
|
||||
|
||||
debug('new %o', path)
|
||||
var opts = options || {};
|
||||
|
||||
this.handle = fn;
|
||||
this.name = fn.name || '<anonymous>';
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
this.regexp = pathRegexp(path, this.keys = [], opts);
|
||||
|
||||
// set fast path flags
|
||||
this.regexp.fast_star = path === '*'
|
||||
this.regexp.fast_slash = path === '/' && opts.end === false
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the error for the layer.
|
||||
*
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {function} next
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Layer.prototype.handle_error = function handle_error(error, req, res, next) {
|
||||
var fn = this.handle;
|
||||
|
||||
if (fn.length !== 4) {
|
||||
// not a standard error handler
|
||||
return next(error);
|
||||
}
|
||||
|
||||
try {
|
||||
fn(error, req, res, next);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the request for the layer.
|
||||
*
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {function} next
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Layer.prototype.handle_request = function handle(req, res, next) {
|
||||
var fn = this.handle;
|
||||
|
||||
if (fn.length > 3) {
|
||||
// not a standard request handler
|
||||
return next();
|
||||
}
|
||||
|
||||
try {
|
||||
fn(req, res, next);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this route matches `path`, if so
|
||||
* populate `.params`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Layer.prototype.match = function match(path) {
|
||||
var match
|
||||
|
||||
if (path != null) {
|
||||
// fast path non-ending match for / (any path matches)
|
||||
if (this.regexp.fast_slash) {
|
||||
this.params = {}
|
||||
this.path = ''
|
||||
return true
|
||||
}
|
||||
|
||||
// fast path for * (everything matched in a param)
|
||||
if (this.regexp.fast_star) {
|
||||
this.params = {'0': decode_param(path)}
|
||||
this.path = path
|
||||
return true
|
||||
}
|
||||
|
||||
// match the path
|
||||
match = this.regexp.exec(path)
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
return false;
|
||||
}
|
||||
|
||||
// store values
|
||||
this.params = {};
|
||||
this.path = match[0]
|
||||
|
||||
var keys = this.keys;
|
||||
var params = this.params;
|
||||
|
||||
for (var i = 1; i < match.length; i++) {
|
||||
var key = keys[i - 1];
|
||||
var prop = key.name;
|
||||
var val = decode_param(match[i])
|
||||
|
||||
if (val !== undefined || !(hasOwnProperty.call(params, prop))) {
|
||||
params[prop] = val;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode param value.
|
||||
*
|
||||
* @param {string} val
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function decode_param(val) {
|
||||
if (typeof val !== 'string' || val.length === 0) {
|
||||
return val;
|
||||
}
|
||||
|
||||
try {
|
||||
return decodeURIComponent(val);
|
||||
} catch (err) {
|
||||
if (err instanceof URIError) {
|
||||
err.message = 'Failed to decode param \'' + val + '\'';
|
||||
err.status = err.statusCode = 400;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var debug = require('debug')('express:router:route');
|
||||
var flatten = require('array-flatten');
|
||||
var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var slice = Array.prototype.slice;
|
||||
var toString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = Route;
|
||||
|
||||
/**
|
||||
* Initialize `Route` with the given `path`,
|
||||
*
|
||||
* @param {String} path
|
||||
* @public
|
||||
*/
|
||||
|
||||
function Route(path) {
|
||||
this.path = path;
|
||||
this.stack = [];
|
||||
|
||||
debug('new %o', path)
|
||||
|
||||
// route handlers for various http methods
|
||||
this.methods = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the route handles a given method.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Route.prototype._handles_method = function _handles_method(method) {
|
||||
if (this.methods._all) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var name = method.toLowerCase();
|
||||
|
||||
if (name === 'head' && !this.methods['head']) {
|
||||
name = 'get';
|
||||
}
|
||||
|
||||
return Boolean(this.methods[name]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Array} supported HTTP methods
|
||||
* @private
|
||||
*/
|
||||
|
||||
Route.prototype._options = function _options() {
|
||||
var methods = Object.keys(this.methods);
|
||||
|
||||
// append automatic head
|
||||
if (this.methods.get && !this.methods.head) {
|
||||
methods.push('head');
|
||||
}
|
||||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
// make upper case
|
||||
methods[i] = methods[i].toUpperCase();
|
||||
}
|
||||
|
||||
return methods;
|
||||
};
|
||||
|
||||
/**
|
||||
* dispatch req, res into this route
|
||||
* @private
|
||||
*/
|
||||
|
||||
Route.prototype.dispatch = function dispatch(req, res, done) {
|
||||
var idx = 0;
|
||||
var stack = this.stack;
|
||||
if (stack.length === 0) {
|
||||
return done();
|
||||
}
|
||||
|
||||
var method = req.method.toLowerCase();
|
||||
if (method === 'head' && !this.methods['head']) {
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
req.route = this;
|
||||
|
||||
next();
|
||||
|
||||
function next(err) {
|
||||
// signal to exit route
|
||||
if (err && err === 'route') {
|
||||
return done();
|
||||
}
|
||||
|
||||
// signal to exit router
|
||||
if (err && err === 'router') {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
var layer = stack[idx++];
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
if (layer.method && layer.method !== method) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
layer.handle_error(err, req, res, next);
|
||||
} else {
|
||||
layer.handle_request(req, res, next);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a handler for all HTTP verbs to this route.
|
||||
*
|
||||
* Behaves just like middleware and can respond or call `next`
|
||||
* to continue processing.
|
||||
*
|
||||
* You can use multiple `.all` call to add multiple handlers.
|
||||
*
|
||||
* function check_something(req, res, next){
|
||||
* next();
|
||||
* };
|
||||
*
|
||||
* function validate_user(req, res, next){
|
||||
* next();
|
||||
* };
|
||||
*
|
||||
* route
|
||||
* .all(validate_user)
|
||||
* .all(check_something)
|
||||
* .get(function(req, res, next){
|
||||
* res.send('hello world');
|
||||
* });
|
||||
*
|
||||
* @param {function} handler
|
||||
* @return {Route} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Route.prototype.all = function all() {
|
||||
var handles = flatten(slice.call(arguments));
|
||||
|
||||
for (var i = 0; i < handles.length; i++) {
|
||||
var handle = handles[i];
|
||||
|
||||
if (typeof handle !== 'function') {
|
||||
var type = toString.call(handle);
|
||||
var msg = 'Route.all() requires a callback function but got a ' + type
|
||||
throw new TypeError(msg);
|
||||
}
|
||||
|
||||
var layer = Layer('/', {}, handle);
|
||||
layer.method = undefined;
|
||||
|
||||
this.methods._all = true;
|
||||
this.stack.push(layer);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
methods.forEach(function(method){
|
||||
Route.prototype[method] = function(){
|
||||
var handles = flatten(slice.call(arguments));
|
||||
|
||||
for (var i = 0; i < handles.length; i++) {
|
||||
var handle = handles[i];
|
||||
|
||||
if (typeof handle !== 'function') {
|
||||
var type = toString.call(handle);
|
||||
var msg = 'Route.' + method + '() requires a callback function but got a ' + type
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
debug('%s %o', method, this.path)
|
||||
|
||||
var layer = Layer('/', {}, handle);
|
||||
layer.method = method;
|
||||
|
||||
this.methods[method] = true;
|
||||
this.stack.push(layer);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
});
|
||||
125
lib/utils.js
125
lib/utils.js
@@ -12,16 +12,21 @@
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var contentDisposition = require('content-disposition');
|
||||
var { METHODS } = require('node:http');
|
||||
var contentType = require('content-type');
|
||||
var deprecate = require('depd')('express');
|
||||
var flatten = require('array-flatten');
|
||||
var mime = require('send').mime;
|
||||
var etag = require('etag');
|
||||
var mime = require('mime-types')
|
||||
var proxyaddr = require('proxy-addr');
|
||||
var qs = require('qs');
|
||||
var querystring = require('querystring');
|
||||
var querystring = require('node:querystring');
|
||||
const { Buffer } = require('node:buffer');
|
||||
|
||||
|
||||
/**
|
||||
* A list of lowercased HTTP methods that are supported by Node.js.
|
||||
* @api private
|
||||
*/
|
||||
exports.methods = METHODS.map((method) => method.toLowerCase());
|
||||
|
||||
/**
|
||||
* Return strong ETag for `body`.
|
||||
@@ -45,31 +50,6 @@ exports.etag = createETagGenerator({ weak: false })
|
||||
|
||||
exports.wetag = createETagGenerator({ weak: true })
|
||||
|
||||
/**
|
||||
* Check if `path` looks absolute.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.isAbsolute = function(path){
|
||||
if ('/' === path[0]) return true;
|
||||
if (':' === path[1] && ('\\' === path[2] || '/' === path[2])) return true; // Windows device path
|
||||
if ('\\\\' === path.substring(0, 2)) return true; // Microsoft Azure absolute path
|
||||
};
|
||||
|
||||
/**
|
||||
* Flatten the given `arr`.
|
||||
*
|
||||
* @param {Array} arr
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.flatten = deprecate.function(flatten,
|
||||
'utils.flatten: use array-flatten npm module instead');
|
||||
|
||||
/**
|
||||
* Normalize the given `type`, for example "html" becomes "text/html".
|
||||
*
|
||||
@@ -81,7 +61,7 @@ exports.flatten = deprecate.function(flatten,
|
||||
exports.normalizeType = function(type){
|
||||
return ~type.indexOf('/')
|
||||
? acceptParams(type)
|
||||
: { value: mime.lookup(type), params: {} };
|
||||
: { value: (mime.lookup(type) || 'application/octet-stream'), params: {} }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -92,49 +72,48 @@ exports.normalizeType = function(type){
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.normalizeTypes = function(types){
|
||||
var ret = [];
|
||||
|
||||
for (var i = 0; i < types.length; ++i) {
|
||||
ret.push(exports.normalizeType(types[i]));
|
||||
}
|
||||
|
||||
return ret;
|
||||
exports.normalizeTypes = function(types) {
|
||||
return types.map(exports.normalizeType);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate Content-Disposition header appropriate for the filename.
|
||||
* non-ascii filenames are urlencoded and a filename* parameter is added
|
||||
*
|
||||
* @param {String} filename
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.contentDisposition = deprecate.function(contentDisposition,
|
||||
'utils.contentDisposition: use content-disposition npm module instead');
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var parts = str.split(/ *; */);
|
||||
var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
|
||||
function acceptParams (str) {
|
||||
var length = str.length;
|
||||
var colonIndex = str.indexOf(';');
|
||||
var index = colonIndex === -1 ? length : colonIndex;
|
||||
var ret = { value: str.slice(0, index).trim(), quality: 1, params: {} };
|
||||
|
||||
for (var i = 1; i < parts.length; ++i) {
|
||||
var pms = parts[i].split(/ *= */);
|
||||
if ('q' === pms[0]) {
|
||||
ret.quality = parseFloat(pms[1]);
|
||||
} else {
|
||||
ret.params[pms[0]] = pms[1];
|
||||
while (index < length) {
|
||||
var splitIndex = str.indexOf('=', index);
|
||||
if (splitIndex === -1) break;
|
||||
|
||||
var colonIndex = str.indexOf(';', index);
|
||||
var endIndex = colonIndex === -1 ? length : colonIndex;
|
||||
|
||||
if (splitIndex > endIndex) {
|
||||
index = str.lastIndexOf(';', splitIndex - 1) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = str.slice(index, splitIndex).trim();
|
||||
var value = str.slice(splitIndex + 1, endIndex).trim();
|
||||
|
||||
if (key === 'q') {
|
||||
ret.quality = parseFloat(value);
|
||||
} else {
|
||||
ret.params[key] = value;
|
||||
}
|
||||
|
||||
index = endIndex + 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -157,6 +136,7 @@ exports.compileETag = function(val) {
|
||||
|
||||
switch (val) {
|
||||
case true:
|
||||
case 'weak':
|
||||
fn = exports.wetag;
|
||||
break;
|
||||
case false:
|
||||
@@ -164,9 +144,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);
|
||||
}
|
||||
@@ -191,17 +168,14 @@ exports.compileQueryParser = function compileQueryParser(val) {
|
||||
|
||||
switch (val) {
|
||||
case true:
|
||||
case 'simple':
|
||||
fn = querystring.parse;
|
||||
break;
|
||||
case false:
|
||||
fn = newObject;
|
||||
break;
|
||||
case 'extended':
|
||||
fn = parseExtendedQueryString;
|
||||
break;
|
||||
case 'simple':
|
||||
fn = querystring.parse;
|
||||
break;
|
||||
default:
|
||||
throw new TypeError('unknown value for query parser function: ' + val);
|
||||
}
|
||||
@@ -232,7 +206,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 || []);
|
||||
@@ -284,6 +259,7 @@ function createETagGenerator (options) {
|
||||
/**
|
||||
* Parse an extended query string with qs.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
@@ -293,14 +269,3 @@ function parseExtendedQueryString(str) {
|
||||
allowPrototypes: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return new empty object.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function newObject() {
|
||||
return {};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user