Compare commits

...

240 Commits
4.17.2 ... 4.x

Author SHA1 Message Date
조수민
d39e8ad177 deps: body-parser@~1.20.4 (#7021) 2026-02-08 15:55:41 -05:00
Ulises Gascón
efe85d9fdc deps: qs@^6.14.1 (#6972) 2026-01-05 10:08:19 +01:00
ctcpip
f62378e1bc 📝 add note to history 2025-12-09 09:32:13 -06:00
Ulises Gascon
12fae14531 4.22.1 2025-12-01 15:27:22 -05:00
Ulises Gascon
5ddf311af3 Revert "sec: security patch for CVE-2024-51999"
This reverts commit 6e97452f60.
2025-12-01 15:27:22 -05:00
Ulises Gascón
49744abd11 4.22.0 (#6921) 2025-12-01 17:17:12 +01:00
Chris de Almeida
6e97452f60 sec: security patch for CVE-2024-51999 2025-12-01 17:14:33 +01:00
Phillip Barta
6a23d34d65 deps: use tilde notation for qs (#6919) 2025-11-25 16:17:24 -05:00
Ulises Gascón
8c12cdf93b deps: qs@6.14.0 (#6909) 2025-11-21 15:22:59 +01:00
Ulises Gascón
7fea74fcf0 deps: use tilde notation for certain dependencies (#6905) 2025-11-20 20:34:22 +01:00
Jon Church
dac7a0475a chore: wider range for query test skip (#6513) 2025-05-15 11:40:07 -05:00
Phillip Barta
997919b488 ci: add node.js 24 to test matrix (#6506) 2025-05-10 18:52:25 -05:00
Phillip Barta
36fb59c6c7 fix(ci): reorder npm i steps to fix ci for older node versions (#6336) 2025-02-17 10:53:01 -06:00
Phillip Barta
3a5edfaff0 fix(ci): updated github actions ci workflow (#6323)
Backport of #6314
2025-02-12 10:25:08 -06:00
Wes Todd
52d978119a fix(test): add test for method routes without paths #5955 2025-01-15 18:22:08 -06:00
Ulises Gascon
fe93005e04 ci: add support for Node.js@23.0
Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
Original PR: https://github.com/expressjs/express/pull/6075
2025-01-08 14:53:41 -06:00
Shahan Arshad
20415843f4 fix(examples): improve readability of user assignment (#6190)
Edit assignment inside condition check to a separate step
2024-12-17 09:59:55 -06:00
Ulises Gascón
1faf228935 4.21.2
Signed-off-by: Ulises Gascon <ulisesgascongonzalez@gmail.com>
2024-12-05 17:27:56 -05:00
Jon Church
2e0fb646d0 deps: bump path-to-regexp@0.1.12 (#6209)
fix backtracking protection
2024-12-05 17:16:48 -05:00
Blake Embrey
59fc27028e deps: path-to-regexp@0.1.11 (#5956)
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2024-10-20 20:09:32 +02:00
Sebastian Beltran
51fc39ccf8 docs: add funding (#6065) 2024-10-20 19:58:25 +02:00
Ulises Gascón
8e229f9275 4.21.1
PR-URL: https://github.com/expressjs/express/pull/6031
2024-10-08 20:36:08 +02:00
Josh Buker
a024c8a7b6 fix(deps): cookie@0.7.1
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2024-10-08 12:13:25 +02:00
Wes Todd
7e562c6d8d 4.21.0 2024-09-11 17:32:14 -05:00
agadzinski93
1bcde96bc8 fix(deps): qs@6.13.0 (#5946)
Co-authored-by: Wes Todd <wes@wesleytodd.com>
2024-09-11 17:27:37 -05:00
Wes Todd
7d36477568 fix(deps): serve-static@1.16.2 (#5951) 2024-09-11 17:26:00 -05:00
Wes Todd
40d2d8f2c8 fix(deps): finalhandler@1.3.1 2024-09-11 17:20:33 -05:00
Blake Embrey
77ada906db Deprecate "back" magic string in redirects (#5935) 2024-09-11 12:24:22 -07:00
Ulises Gascón
21df421ebc 4.20.0 2024-09-10 04:01:43 +02:00
Ulises Gascón
4c9ddc1c47 feat: upgrade to serve-static@0.16.0 2024-09-10 04:01:43 +02:00
Ulises Gascón
9ebe5d500d feat: upgrade to send@0.19.0 (#5928) 2024-09-10 04:01:43 +02:00
Ulises Gascón
ec4a01b6b8 feat: upgrade to body-parser@1.20.3 (#5926)
PR-URL: https://github.com/expressjs/express/pull/5926
2024-09-10 04:01:43 +02:00
Chris de Almeida
54271f69b5 fix: don't render redirect values in anchor href
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2024-09-10 04:00:58 +02:00
Blake Embrey
125bb742a3 path-to-regexp@0.1.10 (#5902)
* path-to-regexp@0.1.10

* Update History.md
2024-09-09 16:02:06 -05:00
Anna Bocharova
2a980ad160 merge-descriptors@1.0.3 (#5781)
* Allow patches for `merge-descriptors` dependency

* Set fixed latest of v1 (1.0.3)
2024-08-23 13:39:13 -07:00
S M Mahmudul Hasan
a3e7e05e0a docs: specify new instructions for question and discuss
PR-URL: https://github.com/expressjs/express/pull/5835
2024-08-22 18:25:14 +02:00
Blake Embrey
c5addb9a17 deps: path-to-regexp@0.1.8 (#5603) 2024-08-21 20:15:02 -07:00
Ulises Gascón
e35380a39d docs: add @IamLizu to the triage team (#5836)
PR-URL: https://github.com/expressjs/express/pull/5836
2024-08-19 22:12:24 +02:00
Sebastian Beltran
f5b6e67aed docs: update scorecard link (#5814)
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2024-08-18 20:37:51 +02:00
Ulises Gascón
2177f67f54 docs: add OSSF Scorecard badge (#5436)
PR-URL: https://github.com/expressjs/express/pull/5436
2024-07-28 12:55:10 +02:00
Jon Church
f4bd86ed36 Replace Appveyor windows testing with GHA (#5599)
This PR moves us off of Appveyor for windows testing.

We are now doing windows/linux testing on GHA. With the exception of iojs, which we are only testing on Linux and have split out to it's own workflow.

We have also added npm-shrinkwrap.json to our gitignore, in order to not have to configure npm in CI to ignore it. If it's never checked in, it shouldn't exist in CI as you need to go out of your way to create it w/ npm.
2024-07-27 14:15:55 -04:00
S M Mahmudul Hasan
2ec589c113 Fix Contributor Covenant link definition reference in attribution section (#5762) 2024-07-17 13:44:03 -07:00
Jon Church
4cf7eed927 remove minor version pinning from ci (#5722) 2024-06-26 18:23:19 -04:00
Chris de Almeida
6d084715ba 📝 update people, add ctcpip to TC (#5683) 2024-06-10 17:19:11 -04:00
Jon Church
61421a8c0c skip QUERY tests for Node 21 only, still not supported (#5695)
* skip QUERY tests for Node 21 only, still not supported

QUERY support has now landed in Node 22.2.0, but is still not supported
in 21.7.3

QUERY showed up in http.METHODS in 21.7.2. Only Node versions after that
will attempt to run tests for it, based on the way we dynamically test
members of the http.METHODS array from Node

* update CI to run on 21.7 and 22.2
2024-06-08 23:25:42 -04:00
Jon Church
f42b160bbc [v4] Deprecate res.clearCookie accepting options.maxAge and options.expires (#5672)
* add deprecation notice for res.clearCookie maxAge/expires

* update History.md for clearCookie deprecation change

* add tests to codify deprecated behavior

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>

---------

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-06-07 19:48:48 -04:00
Chris de Almeida
689073d657 bring back query tests for node 21 (#5690) 2024-06-05 17:25:58 -04:00
Ulises Gascón
2803a2b35a docs: add @UlisesGascon as captain for cookie-parser (#5666) 2024-05-22 18:29:16 -04:00
Blake Embrey
a7d6d29ed3 Add @UlisesGascon to mime repos
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2024-05-13 21:31:23 -05:00
Blake Embrey
897290b685 List and sort all projects, add captains 2024-05-13 21:31:23 -05:00
Mert Can Altin
700349ffaf doc: add table of contents, tc/triager lists to readme (#5619)
* doc: updated readme file

* doc: updated readme file for doc lint

* Update Readme.md

Co-authored-by: krzysdz <krzysdz@users.noreply.github.com>

* Update Readme.md

Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>

* Update Readme.md

Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>

* Update Readme.md

Co-authored-by: Christine Belzie <105683440+CBID2@users.noreply.github.com>

* repair readme

* added Emeritus area

* Add @carpasse to the triager team

* removed old collaborators

* add missing triagers

* lint

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* dedent to fix ToC spacing

* fixup! dedent to fix ToC spacing

* us @ for jonchurch

* format names to use github handles first, single line

* added emeritus triagers

* edited title

* added emeritus team members

* added menu head

* edited emeritus

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* edits to TC and anchors

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* Update Readme.md

Co-authored-by: Jon Church <me@jonchurch.com>

* Update Readme.md

Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>

* Update Readme.md

Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>

* Update Readme.md

Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>

* Update Readme.md

Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>

* Update Readme.md

Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>

---------

Co-authored-by: Mert Can Altin <mert.altin@trendyol.com>
Co-authored-by: krzysdz <krzysdz@users.noreply.github.com>
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
Co-authored-by: Christine Belzie <105683440+CBID2@users.noreply.github.com>
Co-authored-by: Jon Church <me@jonchurch.com>
2024-05-08 17:02:11 -04:00
Mert Can Altin
4b9cd2fd0e add support Node.js@22 in the CI (#5627)
Co-authored-by: Mert Can Altin <mert.altin@trendyol.com>
2024-05-04 18:15:53 -04:00
Jon Church
b44191eb3d ignore ETAG query test as well, reuse skip util (#5639) 2024-05-04 18:01:42 -04:00
Jon Church
8417c60fcf skip QUERY method test (#5628) 2024-05-04 17:09:52 -04:00
Blake Embrey
bf91946bd4 deps: encodeurl@~2.0.0 (#5569) 2024-05-04 16:53:09 -04:00
Ulises Gascón
d97d79ed9a docs: add UlisesGascon as triage initiative captain 2024-04-21 11:48:10 +02:00
Ulises Gascón
26e53f0fbc ci: add CodeQL (SAST) (#5433)
PR-URL: https://github.com/expressjs/express/pull/5433

---------

Co-authored-by: Íñigo Marquínez Prado <25435858+inigomarquinez@users.noreply.github.com>
2024-04-17 15:13:07 +02:00
Ulises Gascón
6abec204c0 docs: update triage nomination policy (#5600)
PR-URL: https://github.com/expressjs/express/pull/5600
2024-04-17 13:53:16 +02:00
Íñigo Marquínez Prado
815f799310 docs: update reference to the threat model (#5590)
PR: https://github.com/expressjs/express/pull/5590
2024-04-10 18:53:52 +02:00
Jon Church
7f9e5843b9 add jonchurch as repo captain on several packages 2024-04-09 08:51:51 -05:00
Blake Embrey
93cf646d5c docs: add blakeembrey as captain for encodeurl (#5579) 2024-04-07 18:40:16 +02:00
Ulises Gascón
2676a1f281 docs: add reference to the Threat Model
* docs: add Threat Model

* docs: update reference

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>

* docs: improve readability

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>

* docs: add reference to the Threat Model

---------

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-04-04 13:01:35 +02:00
Jon Church
6da57c7819 remove examples from the ignore 2024-03-27 09:57:44 -05:00
Jon Church
d546f93f2f add develop to branches 2024-03-27 09:57:44 -05:00
Jon Church
4771ba2bc3 crib fastify's ci concurrency logic
76674fdf46/.github/workflows/ci.yml (L18)
2024-03-27 09:57:44 -05:00
Jon Church
3ae704f67f update ci push trigger only to some branches, ignore examples, .md 2024-03-27 09:57:44 -05:00
Marco Ippolito
8b6d34963d Update Security.md
Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-03-27 09:57:09 -05:00
Marco Ippolito
36b8148110 Update Security.md
Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-03-27 09:57:09 -05:00
Marco Ippolito
6d98d2e110 Update Release-Process.md
Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-03-27 09:57:09 -05:00
marco-ippolito
51a76366e3 refactor: reword to pre-releases 2024-03-27 09:57:09 -05:00
Marco Ippolito
4e3f95c0ea Update Security.md
Co-authored-by: Wes Todd <wes@wesleytodd.com>
2024-03-27 09:57:09 -05:00
Marco Ippolito
88bd6d8e3a Update Release-Process.md
Co-authored-by: Wes Todd <wes@wesleytodd.com>
2024-03-27 09:57:09 -05:00
marco-ippolito
51595d402b feat: document beta releases expectations 2024-03-27 09:57:09 -05:00
Wes Todd
94669f9289 remove duplicate location test for data uri 2024-03-26 09:41:06 -05:00
Wes Todd
b28db2c12c 4.19.2 2024-03-25 09:29:41 -05:00
Wes Todd
0b746953c4 Improved fix for open redirect allow list bypass
Co-authored-by: Jon Church <me@jonchurch.com>
Co-authored-by: Blake Embrey <hello@blakeembrey.com>
2024-03-25 09:29:41 -05:00
Wes Todd
4f0f6cc67d 4.19.1 2024-03-20 17:17:59 -05:00
Wes Todd
a003cfab03 Allow passing non-strings to res.location with new encoding handling checks fixes #5554 #5555 2024-03-20 17:12:46 -05:00
Wes Todd
a1fa90fcea fixed un-edited version in history.md for 4.19.0 2024-03-20 16:52:48 -05:00
Wes Todd
11f2b1db22 build: fix build due to inconsistent supertest behavior in older versions 2024-03-20 16:33:03 -05:00
Wes Todd
084e36506a 4.19.0 2024-03-20 10:18:54 -05:00
FDrag0n
0867302ddb Prevent open redirect allow list bypass due to encodeurl
Co-authored-by: Jon Church <me@jonchurch.com>
2024-03-20 10:18:54 -05:00
Rand McKinney
567c9c665d Add note on how to update docs for new release (#5541)
* Update Release-Process.md

Add note about updating docs.

* Update Release-Process.md

* Update Release-Process.md
2024-03-16 11:57:42 -06:00
Rich Hodgkins
69a4cf2819 deps: cookie@0.6.0
closes #5404
2024-03-15 10:27:45 -05:00
Wes Todd
4ee853e837 docs: loosen TC activity rules 2024-03-14 12:43:12 +01:00
Wes Todd
414854b82e docs: nominating @wesleytodd to be project captian 2024-03-14 12:40:52 +01:00
Ulises Gascón
06c6b88808 docs: update release date 2024-03-11 16:19:21 +01:00
Ulises Gascón
1b51edac7c 4.18.3 2024-02-26 20:20:53 +01:00
Ulises Gascón
b625132864 build: pin Node 21.x to minor
Co-authored-by: Aravind Nair <22199259+aravindvnair99@users.noreply.github.com>
closes #5430
2024-02-26 20:08:10 +01:00
Ulises Gascón
e3eca80584 build: pin Node 21.x to minor
Co-authored-by: Aravind Nair <22199259+aravindvnair99@users.noreply.github.com>
2024-02-26 20:07:59 +01:00
Ulises Gascon
23b44b3ddd build: support Node.js 21.6.2 2024-02-26 20:07:27 +01:00
Ulises Gascon
b9fea12245 build: support Node.js 21.x in appveyor 2024-02-26 20:06:49 +01:00
Ulises Gascon
c259c3407f build: support Node.js 21.x 2024-02-26 20:05:58 +01:00
Ulises Gascon
fdeb1d3176 build: support Node.js 20.x in appveyor
closes #5429
2024-02-26 19:57:01 +01:00
Ulises Gascón
734b281900 build: support Node.js 20.x 2024-02-26 19:41:28 +01:00
Dmitry Kondar
0e3ab6ec21 examples: improve view count in cookie-sessions
closes #5414
2024-02-19 16:47:26 -05:00
Ulises Gascon
59af63ac2e build: Node.js@18.19
closes #5490
2024-02-19 14:18:24 -05:00
Ulises Gascon
e720c5a21b docs: add documentation for benchmarks
closes #5312
2024-02-19 12:04:49 -05:00
riddlew
3abea7f818 examples: remove multipart example
Closes #5193
closes #5195
2024-02-19 10:04:05 -05:00
christof louw
2a89eb5c74 tests: fix handling multiple callbacks
closes #5233
2024-02-19 08:20:39 -05:00
Gireesh Punathil
59aae7686b docs: add project captains to contribution
closes #4210
closes #5484
2024-02-18 12:21:22 -06:00
Wes Todd
c4fe7de7bc docs: update TC governance rules
closes #5483
2024-02-18 22:04:47 -05:00
Douglas Christopher Wilson
a22920707b build: actions/checkout@v4 2023-11-01 22:08:37 -04:00
Douglas Christopher Wilson
02d1c3916e build: Node.js@19.9 2023-08-23 20:07:50 -04:00
Douglas Christopher Wilson
8d8bfaac7b build: Node.js@18.17 2023-08-23 19:58:41 -04:00
Douglas Christopher Wilson
13df1de857 build: eslint@8.47.0 2023-08-23 19:49:36 -04:00
Raz Luvaton
2a00da2067 tests: use random port in listen test
closes #5162
2023-06-04 11:47:20 -04:00
Douglas Christopher Wilson
24e4a2570d build: Node.js@16.20 2023-04-06 20:43:53 -04:00
Douglas Christopher Wilson
91b6fb83b4 build: use nyc@14.1.1 for Node.js < 10 2023-04-06 20:40:27 -04:00
Rakesh Bisht
3531987844 lint: remove unused function arguments in Route tests
closes #5137
2023-05-15 21:53:48 -04:00
Douglas Christopher Wilson
f540c3b019 build: Node.js@18.15 2023-03-13 22:59:15 -04:00
Douglas Christopher Wilson
b8b2eff3c3 build: eslint@8.36.0 2023-03-13 22:52:34 -04:00
Douglas Christopher Wilson
f4e48bc43e build: ejs@3.1.9 2023-03-13 22:49:54 -04:00
Douglas Christopher Wilson
8c24fa8f7b tests: wait for server close in app.listen() 2023-03-13 22:43:19 -04:00
Douglas Christopher Wilson
0debedf4f3 build: fix code coverage aggregate upload 2023-02-26 13:34:32 -05:00
Douglas Christopher Wilson
74beeac071 Fix routing requests without method 2023-02-23 17:23:22 -05:00
Arnaud Benhamdine
9bc1742937 build: support Node.js 19.x 2023-02-23 15:28:18 -05:00
Rakesh Bisht
5ad95419ba docs: fix typos in history
closes #5131
2023-02-23 12:14:47 -05:00
Douglas Christopher Wilson
8a76f39d98 build: eslint@8.34.0 2023-02-23 00:28:41 -05:00
Douglas Christopher Wilson
60b7c672c1 build: mocha@10.2.0 2023-02-23 00:27:06 -05:00
Douglas Christopher Wilson
1e42a98db6 deps: body-parser@1.20.2 2023-02-23 00:24:20 -05:00
Rakesh Bisht
506fbd63be docs: add missing JSDoc param for parseExtendedQueryString
closes #5130
2023-02-22 21:10:07 -05:00
Douglas Christopher Wilson
b9f7a97fe1 build: use $GITHUB_OUTPUT for environment list 2023-02-22 18:10:59 -05:00
Douglas Christopher Wilson
546969d198 build: Node.js@18.14 2023-02-22 12:07:48 -05:00
Douglas Christopher Wilson
f05b5d0e9c build: Node.js@16.19 2023-02-22 11:59:40 -05:00
Rakesh Bisht
3c1d605da7 lint: remove unused parameters in examples
closes #5113
2023-02-22 10:25:07 -05:00
Rakesh Bisht
6b4c4f5426 docs: fix typos in JSDoc comments
closes #5117
2023-02-22 00:16:40 -05:00
Rakesh Bisht
a1efd9d6cf lint: remove unused parameter from internal function
coses #5119
2023-02-21 22:55:22 -05:00
Rakesh Bisht
c6ee8d6e7f lint: remove unused function arguments in tests
closes #5124
2023-02-20 22:57:13 -05:00
Abdul Rauf
442fd46799 build: actions/checkout@v3
closes #5027
2022-11-01 21:13:10 -04:00
Arnaud Benhamdine
723b67766f build: Node.js@18.12 2022-11-01 21:10:12 -04:00
Arnaud Benhamdine
29e117e676 build: Node.js@16.18 2022-11-01 21:08:43 -04:00
Kevin Jones
06b2b1416d docs: update git clone to https protocol
closes #5032
2022-11-01 21:05:31 -04:00
Douglas Christopher Wilson
8368dc178a 4.18.2 2022-10-08 16:11:42 -04:00
Abhinav Das
61f4049122 docs: replace Freenode with Libera Chat
closes #5013
2022-10-08 14:52:31 -04:00
Douglas Christopher Wilson
bb7907b932 build: Node.js@18.10
closes #5014
2022-10-07 17:48:59 -04:00
Douglas Christopher Wilson
f56ce73186 build: supertest@6.3.0 2022-10-06 10:28:13 -04:00
Douglas Christopher Wilson
24b3dc5516 deps: qs@6.11.0 2022-10-06 10:27:01 -04:00
Douglas Christopher Wilson
689d175b8b deps: body-parser@1.20.1 2022-10-06 10:26:18 -04:00
Douglas Christopher Wilson
340be0f79a build: eslint@8.24.0 2022-10-05 22:40:51 -04:00
REALSTEVEIG
33e8dc303a docs: use Node.js name style
closes #4926
2022-08-19 21:12:14 -04:00
Douglas Christopher Wilson
644f6464b9 build: supertest@6.2.4 2022-08-18 23:42:39 -04:00
Douglas Christopher Wilson
ecd7572f1e build: Node.js@14.20 2022-08-18 23:41:10 -04:00
Douglas Christopher Wilson
97131bcda8 build: Node.js@18.7 2022-08-18 23:01:25 -04:00
Douglas Christopher Wilson
8d98e86d7f build: Node.js@16.17 2022-08-18 23:00:36 -04:00
Alexandru Dragomir
2c47827053 examples: remove unused function arguments in params
closes #4914
2022-05-20 11:57:37 -04:00
Douglas Christopher Wilson
97f0a518d8 tests: verify all handlers called in stack tests 2022-05-20 11:54:35 -04:00
Douglas Christopher Wilson
7ec5dd2b3c Fix regression routing a large stack in a single route
fixes #4913
2022-05-20 09:37:20 -04:00
Douglas Christopher Wilson
ab2c70b954 build: Node.js@18.1 2022-05-20 09:35:20 -04:00
Douglas Christopher Wilson
745a63f825 build: ejs@3.1.8 2022-05-20 09:34:47 -04:00
Douglas Christopher Wilson
a2dfc56a49 build: mocha@10.0.0 2022-05-20 09:33:19 -04:00
Douglas Christopher Wilson
d854c43ea1 4.18.1 2022-04-29 15:32:26 -04:00
Douglas Christopher Wilson
b02a95c693 build: Node.js@16.15 2022-04-29 14:52:20 -04:00
Douglas Christopher Wilson
631ada0c64 Fix hanging on large stack of sync routes
fixes #4899
2022-04-29 13:34:47 -04:00
Hashen
75e0c7a2c9 bench: remove unused parameter
closes #4898
2022-04-28 22:00:49 -04:00
Douglas Christopher Wilson
e2482b7e36 build: ejs@3.1.7 2022-04-28 21:59:32 -04:00
Douglas Christopher Wilson
2df96e349f build: supertest@6.2.3 2022-04-28 13:04:38 -04:00
Douglas Christopher Wilson
a38fae126a build: mocha@9.2.2 2022-04-28 13:04:11 -04:00
Douglas Christopher Wilson
547fdd41dc 4.18.0 2022-04-25 14:53:28 -04:00
Deniz
0b330ef57c bench: print latency and vary connections
closes #4880
2022-04-24 19:31:42 -04:00
Douglas Christopher Wilson
158a17031a build: support Node.js 18.x 2022-04-21 02:09:08 -04:00
Douglas Christopher Wilson
29ea1b2f74 build: use 64-bit Node.js in AppVeyor 2022-04-21 01:38:59 -04:00
Douglas Christopher Wilson
11a209e4b7 build: support Node.js 17.x 2022-04-20 22:02:37 -04:00
phoenix
fd8e45c344 tests: mark stack overflow as long running
closes #4887
2022-04-20 19:52:20 -04:00
Douglas Christopher Wilson
708ac4cdf5 Fix handling very large stacks of sync middleware
closes #4891
2022-04-13 23:29:25 -04:00
Douglas Christopher Wilson
92c5ce59f5 deps: cookie@0.5.0 2022-04-11 22:51:13 -04:00
Hashen
8880ddad1c examples: add missing html label associations
closes #4884
2022-04-11 19:29:14 -04:00
Douglas Christopher Wilson
b91c7ffb28 examples: use http-errors to create errors 2022-04-11 19:28:50 -04:00
Eslam Salem
ecaf67c930 docs: remove Node Security Project from security policy
closes #4890
2022-04-11 18:54:33 -04:00
Ghouse Mohamed
99175c3ef6 docs: fix typo in casing of HTTP
closes #4872
2022-04-08 03:42:09 -04:00
Hashen
1b2e097be2 tests: fix typo in description
closes #4882
2022-04-07 19:20:41 -04:00
Douglas Christopher Wilson
04da4aaf1a build: use supertest@3.4.2 for Node.js 6.x 2022-04-07 19:17:10 -04:00
Douglas Christopher Wilson
2e2d78c4d9 deps: on-finished@2.4.1 2022-04-03 01:15:37 -04:00
3imed-jaberi
980d881e3b deps: statuses@2.0.1
closes #4336
2022-04-02 22:39:35 -04:00
Douglas Christopher Wilson
1df75763e3 deps: qs@6.10.3 2022-04-02 21:56:41 -04:00
Douglas Christopher Wilson
32c558d414 deps: body-parser@1.20.0 2022-04-02 21:51:31 -04:00
Douglas Christopher Wilson
a10770286e Use http-errors for res.format error 2022-03-27 23:41:31 -04:00
Chris Barth
5855339455 Fix behavior of null/undefined as "maxAge" in res.cookie
fixes #3935
closes #3936
2022-03-26 23:11:48 -04:00
Ulises Gascon
1cc8169938 deps: depd@2.0.0
closes #4174
2022-03-26 14:05:25 -04:00
Nadav Ivgi
9482b82d0b Invoke default with same arguments as types in res.format
closes #3587
2022-03-26 01:29:40 -04:00
Douglas Christopher Wilson
10b9b507b7 examples: use updated res.download in example 2022-03-25 18:14:27 -04:00
Douglas Christopher Wilson
03dc367187 Allow options without filename in res.download 2022-03-25 18:13:42 -04:00
Douglas Christopher Wilson
f739b162d9 deps: finalhandler@1.2.0 2022-03-25 01:46:32 -04:00
Douglas Christopher Wilson
c92420648e deps: serve-static@1.15.0 2022-03-25 01:44:51 -04:00
Douglas Christopher Wilson
dd69eedd18 deps: send@0.18.0 2022-03-25 01:43:45 -04:00
Tommaso Tofacchi
0def9bb659 Add "root" option to res.download
fixes #4834
closes #4855
2022-03-25 00:08:38 -04:00
Jon Church
4847d0efa1 Deprecate string and non-integer arguments to res.status
closes #4223
2022-03-23 23:14:08 -04:00
Evan Hahn
c17fe05861 Ignore Object.prototype values in settings through app.set/app.get
closes #4802
closes #4803
2022-03-23 20:05:05 -04:00
Tito D. Kesumo Siregar
87279c08aa Support proper 205 responses using res.send
closes #4592
closes #4596
2022-03-23 20:05:05 -04:00
Kris Kalavantavanich
8bf0720391 build: support Node.js 16.x 2022-03-23 20:05:05 -04:00
Kris Kalavantavanich
eb4c930d5f build: support Node.js 15.x 2022-03-23 20:05:05 -04:00
Hashen
947b6b7d57 lint: remove unnecessary continue statement in loop
closes #4868
2022-03-20 22:59:20 -04:00
Hashen
bf4c3ee00f docs: fix incomplete JSDoc comment
closes #4867
2022-03-20 20:51:39 -04:00
Hashen
2a7417dd84 examples: fixup html
closes #4866
2022-03-20 15:14:33 -04:00
Tobias Speicher
490f1a1738 lint: remove deprecated String.prototype.substr
closes #4860
2022-03-20 12:34:37 -04:00
Douglas Christopher Wilson
446046f886 build: mocha@9.2.1 2022-03-01 00:27:48 -05:00
Douglas Christopher Wilson
291993d73c tests: expand res.sendFile options tests 2022-03-01 00:22:09 -05:00
Steven
e8594c3571 docs: add install size badge
closes #3710
2022-02-28 23:41:41 -05:00
Douglas Christopher Wilson
07aa91f7cb docs: consolidate contributing in readme 2022-02-28 21:52:57 -05:00
Douglas Christopher Wilson
4ed35b4202 docs: switch badges to badgen 2022-02-28 21:23:49 -05:00
Douglas Christopher Wilson
ea66a9b81b docs: update link to github actions ci 2022-02-28 21:15:08 -05:00
apeltop
d0e166c3c6 docs: fix typo in private api jsdoc
closes #4843
2022-02-28 18:37:41 -05:00
Douglas Christopher Wilson
cf9f662655 tests: fix position of res.sendfile(path, options) test 2022-02-24 00:17:01 -05:00
Douglas Christopher Wilson
8da8f79c44 tests: fix callback in res.download test 2022-02-23 00:20:34 -05:00
Douglas Christopher Wilson
18f782bba9 tests: remove duplicate utils 2022-02-23 00:18:36 -05:00
Douglas Christopher Wilson
bc5ca05509 tests: remove usage of should 2022-02-21 19:54:52 -05:00
Douglas Christopher Wilson
9967ffbdc2 tests: update res.append to verify separate header lines 2022-02-21 19:23:25 -05:00
Douglas Christopher Wilson
7df0c840e0 tests: fix up app.locals tests 2022-02-21 19:07:26 -05:00
Douglas Christopher Wilson
d8ed591117 tests: fix req.acceptsLanguage tests 2022-02-20 23:49:42 -05:00
Douglas Christopher Wilson
8ee3420f0f tests: fix req.acceptsEncodings tests 2022-02-20 23:43:02 -05:00
Douglas Christopher Wilson
3d7fce56a3 4.17.3 2022-02-16 21:03:42 -05:00
Douglas Christopher Wilson
f9063712e0 build: update example dependencies 2022-02-16 00:11:24 -05:00
Douglas Christopher Wilson
6381bc6317 deps: qs@6.9.7 2022-02-16 00:03:16 -05:00
Douglas Christopher Wilson
a007863096 deps: body-parser@1.19.2 2022-02-15 23:43:41 -05:00
Douglas Christopher Wilson
e98f5848a0 Revert "build: use minimatch@3.0.4 for Node.js < 4" 2022-02-14 18:42:47 -05:00
Douglas Christopher Wilson
a65913776d tests: use strict mode 2022-02-09 01:07:08 -05:00
Douglas Christopher Wilson
a39e409cf3 tests: prevent leaking changes to NODE_ENV 2022-02-08 18:40:07 -05:00
KoyamaSohei
82de4de5ab examples: fix path traversal in downloads example
closes #4120
2022-02-08 17:32:10 -05:00
Douglas Christopher Wilson
12310c5294 build: use nyc for test coverage 2022-02-07 23:08:12 -05:00
Thomas Strauß
884657d546 examples: remove bitwise syntax for includes check
closes #4814
2022-02-07 22:58:44 -05:00
Douglas Christopher Wilson
7511d08328 build: use minimatch@3.0.4 for Node.js < 4 2022-02-07 17:46:47 -05:00
Douglas Christopher Wilson
2585f209f9 tests: fix test missing assertion 2022-02-04 21:18:47 -05:00
Douglas Christopher Wilson
9d0976229d build: supertest@6.2.2 2022-02-04 21:14:41 -05:00
Douglas Christopher Wilson
43cc56eb9e build: clean up gitignore 2022-02-04 16:48:33 -05:00
Douglas Christopher Wilson
1c7bbcc143 build: Node.js@14.19 2022-02-04 16:36:47 -05:00
Douglas Christopher Wilson
9cbbc8ae74 deps: cookie@0.4.2 2022-02-04 16:35:50 -05:00
Douglas Christopher Wilson
6fbc269563 pref: remove unnecessary regexp for trust proxy 2022-02-04 16:34:56 -05:00
Douglas Christopher Wilson
2bc734aa3f deps: accepts@~1.3.8 2022-02-04 16:21:11 -05:00
caioagiani
89bb531b31 docs: fix typo in res.download jsdoc
closes #4805
2022-02-03 23:26:17 -05:00
Douglas Christopher Wilson
744564fcf8 tests: add test for multiple ips in "trust proxy" 2022-02-02 16:19:01 -05:00
Douglas Christopher Wilson
da6cb0ed8a tests: add range tests to res.download 2022-02-02 15:15:41 -05:00
Douglas Christopher Wilson
00ad5bee96 tests: add more tests for app.request & app.response 2022-02-02 15:02:46 -05:00
Douglas Christopher Wilson
141914e817 tests: fix tests that did not bubble errors 2022-02-02 14:16:20 -05:00
Douglas Christopher Wilson
bd4fdfe5f7 tests: remove global dependency on should
fixes #4797
2022-02-02 01:55:16 -05:00
Douglas Christopher Wilson
215f484fb4 tests: fix wording of req.accepts* test cases 2022-02-02 01:42:35 -05:00
Douglas Christopher Wilson
20047bb6e4 examples: use strict mode 2022-02-02 01:30:51 -05:00
Douglas Christopher Wilson
8b9757e8b8 build: fix running linter in CI 2022-02-02 01:25:55 -05:00
Douglas Christopher Wilson
a84e73b958 tests: add test for hello-world example 2022-02-02 01:23:40 -05:00
Douglas Christopher Wilson
69997cbdbe examples: fix error handling in auth example 2022-02-02 00:44:28 -05:00
Douglas Christopher Wilson
c221b8596e build: mocha@9.2.0 2022-01-27 19:02:58 -05:00
152 changed files with 4719 additions and 1330 deletions

View File

@@ -1,160 +1,192 @@
name: ci
on:
- pull_request
- push
push:
branches:
- master
- develop
- '4.x'
- '5.x'
paths-ignore:
- '*.md'
pull_request:
paths-ignore:
- '*.md'
permissions:
contents: read
# Cancel in progress workflows
# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run
concurrency:
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
cancel-in-progress: true
jobs:
test:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- name: Install dependencies
run: npm install --ignore-scripts --include=dev
- name: Run lint
run: npm run lint
test:
name: Run tests
strategy:
fail-fast: false
matrix:
name:
- Node.js 0.10
- Node.js 0.12
- io.js 1.x
- io.js 2.x
- io.js 3.x
- Node.js 4.x
- Node.js 5.x
- Node.js 6.x
- Node.js 7.x
- Node.js 8.x
- Node.js 9.x
- Node.js 10.x
- Node.js 11.x
- Node.js 12.x
- Node.js 13.x
- Node.js 14.x
os: [ubuntu-latest, windows-latest]
node-version:
- "0.10"
- "0.12"
- "4"
- "5"
- "6"
- "7"
- "8"
- "9"
- "10"
- "11"
- "12"
- "13"
- "14"
- "15"
- "16"
- "17"
- "18"
- "19"
- "20"
- "21"
- "22"
- "23"
- "24"
# Use supported versions of our testing tools under older versions of Node
# Install npm in some specific cases where we need to
include:
- name: Node.js 0.10
node-version: "0.10"
npm-i: mocha@3.5.3 supertest@2.0.0
- node-version: "0.10"
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
# Npm isn't being installed on windows w/ setup-node for
# 0.10 and 0.12, which will end up choking when npm uses es6
npm-version: "npm@2.15.1"
- name: Node.js 0.12
node-version: "0.12"
npm-i: mocha@3.5.3 supertest@2.0.0
- node-version: "0.12"
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
npm-version: "npm@2.15.11"
- name: io.js 1.x
node-version: "1.8"
npm-i: mocha@3.5.3 supertest@2.0.0
- node-version: "4"
npm-i: "mocha@5.2.0 nyc@11.9.0 supertest@3.4.2"
- name: io.js 2.x
node-version: "2.5"
npm-i: mocha@3.5.3 supertest@2.0.0
- node-version: "5"
npm-i: "mocha@5.2.0 nyc@11.9.0 supertest@3.4.2"
# fixes https://github.com/npm/cli/issues/681
npm-version: "npm@3.10.10"
- name: io.js 3.x
node-version: "3.3"
npm-i: mocha@3.5.3 supertest@2.0.0
- node-version: "6"
npm-i: "mocha@6.2.2 nyc@14.1.1 supertest@3.4.2"
- name: Node.js 4.x
node-version: "4.9"
npm-i: mocha@5.2.0 supertest@3.4.2
- node-version: "7"
npm-i: "mocha@6.2.2 nyc@14.1.1 supertest@6.1.6"
- name: Node.js 5.x
node-version: "5.12"
npm-i: mocha@5.2.0 supertest@3.4.2
- node-version: "8"
npm-i: "mocha@7.2.0 nyc@14.1.1 supertest@6.1.6"
- name: Node.js 6.x
node-version: "6.17"
npm-i: mocha@6.2.2
- node-version: "9"
npm-i: "mocha@7.2.0 nyc@14.1.1 supertest@6.1.6"
- name: Node.js 7.x
node-version: "7.10"
npm-i: mocha@6.2.2
- node-version: "10"
npm-i: "mocha@8.4.0 supertest@6.1.6"
- name: Node.js 8.x
node-version: "8.17"
npm-i: mocha@7.2.0
- node-version: "11"
npm-i: "mocha@8.4.0 supertest@6.1.6"
- name: Node.js 9.x
node-version: "9.11"
npm-i: mocha@7.2.0
- node-version: "12"
npm-i: "mocha@9.2.2 supertest@6.1.6"
- name: Node.js 10.x
node-version: "10.24"
npm-i: mocha@8.4.0
- node-version: "13"
npm-i: "mocha@9.2.2 supertest@6.1.6"
- name: Node.js 11.x
node-version: "11.15"
npm-i: mocha@8.4.0
- name: Node.js 12.x
node-version: "12.22"
- name: Node.js 13.x
node-version: "13.14"
- name: Node.js 14.x
node-version: "14.18"
- node-version: "15"
npm-i: "supertest@6.1.6"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install Node.js ${{ matrix.node-version }}
shell: bash -eo pipefail -l {0}
run: |
nvm install --default ${{ matrix.node-version }}
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Configure npm
run: |
npm config set loglevel error
npm config set shrinkwrap false
- name: Npm version fixes
if: ${{matrix.npm-version != ''}}
run: npm install -g ${{ matrix.npm-version }}
- name: Install npm module(s) ${{ matrix.npm-i }}
run: npm install --save-dev ${{ matrix.npm-i }}
if: matrix.npm-i != ''
- name: Configure npm loglevel
run: |
npm config set loglevel error
shell: bash
- name: Remove non-test dependencies
run: npm rm --silent --save-dev connect-redis
- name: Install Node version specific dev deps
if: ${{ matrix.npm-i != '' }}
run: npm install --save-dev ${{ matrix.npm-i }}
- name: Setup Node.js version-specific dependencies
shell: bash
run: |
# eslint for linting
# - remove on Node.js < 10
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
grep -E '^eslint(-|$)' | \
sort -r | \
xargs -n1 npm rm --silent --save-dev
fi
- name: Install dependencies
run: npm install
- name: Install Node.js dependencies
run: npm install
- name: Remove non-test dependencies
run: npm rm --silent --save-dev connect-redis
- name: List environment
id: list_env
shell: bash
run: |
echo "node@$(node -v)"
echo "npm@$(npm -v)"
npm -s ls ||:
- name: Output Node and NPM versions
run: |
echo "Node.js version: $(node -v)"
echo "NPM version: $(npm -v)"
- name: Run tests
shell: bash
run: npm run test-ci
- name: Run tests
shell: bash
run: npm run test-ci
- name: Lint code
if: steps.list_env.outputs.eslint != ''
run: npm run lint
- name: Collect code coverage
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
flag-name: run-${{ matrix.test_number }}
parallel: true
- name: Upload code coverage
uses: actions/upload-artifact@v4
with:
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
path: ./coverage/lcov.info
retention-days: 1
coverage:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
steps:
- name: Upload code coverage
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true
- uses: actions/checkout@v4
- name: Install lcov
shell: bash
run: sudo apt-get -y install lcov
- name: Collect coverage reports
uses: actions/download-artifact@v4
with:
path: ./coverage
pattern: coverage-node-*
- name: Merge coverage reports
shell: bash
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
- name: Upload coverage report
uses: coverallsapp/github-action@v2
with:
file: ./lcov.info

66
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: ["master"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["master"]
schedule:
- cron: "0 0 * * 1"
permissions:
contents: read
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7
with:
languages: javascript
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
# - name: Autobuild
# uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7
with:
category: "/language:javascript"

69
.github/workflows/iojs.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: iojs-ci
on:
push:
branches:
- master
- '4.x'
paths-ignore:
- '*.md'
pull_request:
paths-ignore:
- '*.md'
concurrency:
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: ["1.8", "2.5", "3.3"]
include:
- node-version: "1.8"
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
- node-version: "2.5"
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
- node-version: "3.3"
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
steps:
- uses: actions/checkout@v4
- name: Install iojs ${{ matrix.node-version }}
shell: bash -eo pipefail -l {0}
run: |
nvm install --default ${{ matrix.node-version }}
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
- name: Configure npm
run: |
npm config set loglevel error
npm config set shrinkwrap false
- name: Install npm module(s) ${{ matrix.npm-i }}
run: npm install --save-dev ${{ matrix.npm-i }}
if: matrix.npm-i != ''
- name: Remove non-test dependencies
run: npm rm --silent --save-dev connect-redis
- name: Install Node.js dependencies
run: npm install
- name: List environment
id: list_env
shell: bash
run: |
echo "node@$(node -v)"
echo "npm@$(npm -v)"
npm -s ls ||:
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT"
- name: Run tests
shell: bash
run: npm run test

21
.gitignore vendored
View File

@@ -1,27 +1,16 @@
# OS X
.DS_Store*
Icon?
._*
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
# Linux
.directory
*~
# npm
node_modules
package-lock.json
npm-shrinkwrap.json
*.log
*.gz
# Coveralls
.nyc_output
coverage
# Benchmarking
benchmarks/graphs
# ignore additional files using core.excludesFile
# https://git-scm.com/docs/gitignore

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
package-lock=false

View File

@@ -9,7 +9,7 @@ also easily visible to outsiders.
## Section 1: Scope
Express is a http web server framework with a simple and expressive API
Express is a HTTP web server framework with a simple and expressive API
which is highly aligned with Node.js core. We aim to be the best in
class for writing performant, spec compliant, and powerful web servers
in Node.js. As one of the oldest and most popular web frameworks in
@@ -24,7 +24,7 @@ Express is made of many modules spread between three GitHub Orgs:
libraries
- [pillarjs](http://github.com/pillarjs/): Components which make up
Express but can also be used for other web frameworks
- [jshttp](http://github.com/jshttp/): Low level http libraries
- [jshttp](http://github.com/jshttp/): Low level HTTP libraries
### 1.2: Out-of-Scope

View File

@@ -127,7 +127,7 @@ project community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant, version 2.0](cc-20-doc).
This Code of Conduct is adapted from the [Contributor Covenant, version 2.0][cc-20-doc].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).

View File

@@ -12,6 +12,7 @@ contributors can be involved in decision making.
* A **Contributor** is any individual creating or commenting on an issue or pull request.
* A **Committer** is a subset of contributors who have been given write access to the repository.
* A **Project Captain** is the lead maintainer of a repository.
* A **TC (Technical Committee)** is a group of committers representing the required technical
expertise to resolve rare disputes.
* A **Triager** is a subset of contributors who have been given triage access to the repository.
@@ -62,29 +63,14 @@ compromise among committers be the default resolution mechanism.
Anyone can become a triager! Read more about the process of being a triager in
[the triage process document](Triager-Guide.md).
[Open an issue in `expressjs/express` repo](https://github.com/expressjs/express/issues/new)
to request the triage role. State that you have read and agree to the
[Code of Conduct](Code-Of-Conduct.md) and details of the role.
Currently, any existing [organization member](https://github.com/orgs/expressjs/people) can nominate
a new triager. If you are interested in becoming a triager, our best advice is to actively participate
in the community by helping triaging issues and pull requests. As well we recommend
to engage in other community activities like attending the TC meetings, and participating in the Slack
discussions.
Here is an example issue content you can copy and paste:
```
Title: Request triager role for <your GitHub username>
I have read and understood the project's Code of Conduct.
I also have read and understood the process and best practices around Express triaging.
I request for a triager role for the following GitHub organizations:
jshttp
pillarjs
express
```
Once you have opened your issue, a member of the TC will add you to the `triage` team in
the organizations requested. They will then close the issue.
Happy triaging!
You can also reach out to any of the [organization members](https://github.com/orgs/expressjs/people)
if you have questions or need guidance.
## Becoming a Committer
@@ -102,12 +88,122 @@ If a consensus cannot be reached that has no objections then a majority wins vot
is called. It is also expected that the majority of decisions made by the TC are via
a consensus seeking process and that voting is only used as a last-resort.
Resolution may involve returning the issue to committers with suggestions on how to
move forward towards a consensus. It is not expected that a meeting of the TC
Resolution may involve returning the issue to project captains with suggestions on
how to move forward towards a consensus. It is not expected that a meeting of the TC
will resolve all issues on its agenda during that meeting and may prefer to continue
the discussion happening among the committers.
the discussion happening among the project captains.
Members can be added to the TC at any time. Any committer can nominate another committer
Members can be added to the TC at any time. Any TC member can nominate another committer
to the TC and the TC uses its standard consensus seeking process to evaluate whether or
not to add this new member. Members who do not participate consistently at the level of
a majority of the other members are expected to resign.
not to add this new member. The TC will consist of a minimum of 3 active members and a
maximum of 10. If the TC should drop below 5 members the active TC members should nominate
someone new. If a TC member is stepping down, they are encouraged (but not required) to
nominate someone to take their place.
TC members will be added as admin's on the Github orgs, npm orgs, and other resources as
necessary to be effective in the role.
To remain "active" a TC member should have participation within the last 12 months and miss
no more than six consecutive TC meetings. Our goal is to increase participation, not punish
people for any lack of participation, this guideline should be only be used as such
(replace an inactive member with a new active one, for example). Members who do not meet this
are expected to step down. If A TC member does not step down, an issue can be opened in the
discussions repo to move them to inactive status. TC members who step down or are removed due
to inactivity will be moved into inactive status.
Inactive status members can become active members by self nomination if the TC is not already
larger than the maximum of 10. They will also be given preference if, while at max size, an
active member steps down.
## Project Captains
The Express TC can designate captains for individual projects/repos in the
organizations. These captains are responsible for being the primary
day-to-day maintainers of the repo on a technical and community front.
Repo captains are empowered with repo ownership and package publication rights.
When there are conflicts, especially on topics that effect the Express project
at large, captains are responsible to raise it up to the TC and drive
those conflicts to resolution. Captains are also responsible for making sure
community members follow the community guidelines, maintaining the repo
and the published package, as well as in providing user support.
Like TC members, Repo captains are a subset of committers.
To become a captain for a project the candidate is expected to participate in that
project for at least 6 months as a committer prior to the request. They should have
helped with code contributions as well as triaging issues. They are also required to
have 2FA enabled on both their GitHub and npm accounts. Any TC member or existing
captain on the repo can nominate another committer to the captain role, submit a PR to
this doc, under `Current Project Captains` section (maintaining the sort order) with
the project, their GitHub handle and npm username (if different). The PR will require
at least 2 approvals from TC members and 2 weeks hold time to allow for comment and/or
dissent. When the PR is merged, a TC member will add them to the proper GitHub/npm groups.
### Active Projects and Captains
- `expressjs/badgeboard`: @wesleytodd
- `expressjs/basic-auth-connect`: N/A
- `expressjs/body-parser`: @wesleytodd, @jonchurch
- `expressjs/compression`: N/A
- `expressjs/connect-multiparty`: N/A
- `expressjs/cookie-parser`: @wesleytodd, @UlisesGascon
- `expressjs/cookie-session`: N/A
- `expressjs/cors`: @jonchurch
- `expressjs/discussions`: @wesleytodd
- `expressjs/errorhandler`: N/A
- `expressjs/express-paginate`: N/A
- `expressjs/express`: @wesleytodd
- `expressjs/expressjs.com`: @crandmck, @jonchurch
- `expressjs/flash`: N/A
- `expressjs/generator`: @wesleytodd
- `expressjs/method-override`: N/A
- `expressjs/morgan`: @jonchurch
- `expressjs/multer`: @LinusU
- `expressjs/response-time`: @blakeembrey
- `expressjs/serve-favicon`: N/A
- `expressjs/serve-index`: N/A
- `expressjs/serve-static`: N/A
- `expressjs/session`: N/A
- `expressjs/statusboard`: @wesleytodd
- `expressjs/timeout`: N/A
- `expressjs/vhost`: N/A
- `jshttp/accepts`: @blakeembrey
- `jshttp/basic-auth`: @blakeembrey
- `jshttp/compressible`: @blakeembrey
- `jshttp/content-disposition`: @blakeembrey
- `jshttp/content-type`: @blakeembrey
- `jshttp/cookie`: @wesleytodd
- `jshttp/etag`: @blakeembrey
- `jshttp/forwarded`: @blakeembrey
- `jshttp/fresh`: @blakeembrey
- `jshttp/http-assert`: @wesleytodd, @jonchurch
- `jshttp/http-errors`: @wesleytodd, @jonchurch
- `jshttp/media-typer`: @blakeembrey
- `jshttp/methods`: @blakeembrey
- `jshttp/mime-db`: @blakeembrey, @UlisesGascon
- `jshttp/mime-types`: @blakeembrey, @UlisesGascon
- `jshttp/negotiator`: @blakeembrey
- `jshttp/on-finished`: @wesleytodd
- `jshttp/on-headers`: @blakeembrey
- `jshttp/proxy-addr`: @wesleytodd
- `jshttp/range-parser`: @blakeembrey
- `jshttp/statuses`: @blakeembrey
- `jshttp/type-is`: @blakeembrey
- `jshttp/vary`: @blakeembrey
- `pillarjs/cookies`: @blakeembrey
- `pillarjs/csrf`: N/A
- `pillarjs/encodeurl`: @blakeembrey
- `pillarjs/finalhandler`: @wesleytodd
- `pillarjs/hbs`: N/A
- `pillarjs/multiparty`: @blakeembrey
- `pillarjs/parseurl`: @blakeembrey
- `pillarjs/path-to-regexp`: @blakeembrey
- `pillarjs/request`: @wesleytodd
- `pillarjs/resolve-path`: @blakeembrey
- `pillarjs/router`: @blakeembrey
- `pillarjs/send`: @blakeembrey
- `pillarjs/understanding-csrf`: N/A
### Current Initiative Captains
- Triage team [ref](https://github.com/expressjs/discussions/issues/227): @UlisesGascon

View File

@@ -1,3 +1,161 @@
4.22.1 / 2025-12-01
==========
* Revert security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))
* The prior release (4.22.0) included an erroneous breaking change related to the extended query parser. There is no actual security vulnerability associated with this behavior (CVE-2024-51999 has been rejected). The change has been fully reverted in this release.
4.22.0 / 2025-12-01
==========
* Security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))
* deps: use tilde notation for dependencies
* deps: qs@6.14.0
4.21.2 / 2024-11-06
==========
* deps: path-to-regexp@0.1.12
- Fix backtracking protection
* deps: path-to-regexp@0.1.11
- Throws an error on invalid path values
4.21.1 / 2024-10-08
==========
* Backported a fix for [CVE-2024-47764](https://nvd.nist.gov/vuln/detail/CVE-2024-47764)
4.21.0 / 2024-09-11
==========
* Deprecate `res.location("back")` and `res.redirect("back")` magic string
* deps: serve-static@1.16.2
* includes send@0.19.0
* deps: finalhandler@1.3.1
* deps: qs@6.13.0
4.20.0 / 2024-09-10
==========
* deps: serve-static@0.16.0
* Remove link renderization in html while redirecting
* deps: send@0.19.0
* Remove link renderization in html while redirecting
* deps: body-parser@0.6.0
* add `depth` option to customize the depth level in the parser
* IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`)
* Remove link renderization in html while using `res.redirect`
* deps: path-to-regexp@0.1.10
- Adds support for named matching groups in the routes using a regex
- Adds backtracking protection to parameters without regexes defined
* deps: encodeurl@~2.0.0
- Removes encoding of `\`, `|`, and `^` to align better with URL spec
* Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie`
- Will be ignored in v5, clearCookie will set a cookie with an expires in the past to instruct clients to delete the cookie
4.19.2 / 2024-03-25
==========
* Improved fix for open redirect allow list bypass
4.19.1 / 2024-03-20
==========
* Allow passing non-strings to res.location with new encoding handling checks
4.19.0 / 2024-03-20
==========
* Prevent open redirect allow list bypass due to encodeurl
* deps: cookie@0.6.0
4.18.3 / 2024-02-29
==========
* Fix routing requests without method
* deps: body-parser@1.20.2
- Fix strict json error message on Node.js 19+
- deps: content-type@~1.0.5
- deps: raw-body@2.5.2
* deps: cookie@0.6.0
- Add `partitioned` option
4.18.2 / 2022-10-08
===================
* Fix regression routing a large stack in a single route
* deps: body-parser@1.20.1
- deps: qs@6.11.0
- perf: remove unnecessary object clone
* deps: qs@6.11.0
4.18.1 / 2022-04-29
===================
* Fix hanging on large stack of sync routes
4.18.0 / 2022-04-25
===================
* Add "root" option to `res.download`
* Allow `options` without `filename` in `res.download`
* Deprecate string and non-integer arguments to `res.status`
* Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie`
* Fix handling very large stacks of sync middleware
* Ignore `Object.prototype` values in settings through `app.set`/`app.get`
* Invoke `default` with same arguments as types in `res.format`
* Support proper 205 responses using `res.send`
* Use `http-errors` for `res.format` error
* deps: body-parser@1.20.0
- Fix error message for json parse whitespace in `strict`
- Fix internal error when inflated body exceeds limit
- Prevent loss of async hooks context
- Prevent hanging when request already read
- deps: depd@2.0.0
- deps: http-errors@2.0.0
- deps: on-finished@2.4.1
- deps: qs@6.10.3
- deps: raw-body@2.5.1
* deps: cookie@0.5.0
- Add `priority` option
- Fix `expires` option to reject invalid dates
* deps: depd@2.0.0
- Replace internal `eval` usage with `Function` constructor
- Use instance methods on `process` to check for listeners
* deps: finalhandler@1.2.0
- Remove set content headers that break response
- deps: on-finished@2.4.1
- deps: statuses@2.0.1
* deps: on-finished@2.4.1
- Prevent loss of async hooks context
* deps: qs@6.10.3
* deps: send@0.18.0
- Fix emitted 416 error missing headers property
- Limit the headers removed for 304 response
- deps: depd@2.0.0
- deps: destroy@1.2.0
- deps: http-errors@2.0.0
- deps: on-finished@2.4.1
- deps: statuses@2.0.1
* deps: serve-static@1.15.0
- deps: send@0.18.0
* deps: statuses@2.0.1
- Remove code 306
- Rename `425 Unordered Collection` to standard `425 Too Early`
4.17.3 / 2022-02-16
===================
* deps: accepts@~1.3.8
- deps: mime-types@~2.1.34
- deps: negotiator@0.6.3
* deps: body-parser@1.19.2
- deps: bytes@3.1.2
- deps: qs@6.9.7
- deps: raw-body@2.4.3
* deps: cookie@0.4.2
* deps: qs@6.9.7
* Fix handling of `__proto__` keys
* pref: remove unnecessary regexp for trust proxy
4.17.2 / 2021-12-16
===================
@@ -2033,7 +2191,7 @@
* deps: connect@2.21.0
- deprecate `connect(middleware)` -- use `app.use(middleware)` instead
- deprecate `connect.createServer()` -- use `connect()` instead
- fix `res.setHeader()` patch to work with with get -> append -> set pattern
- fix `res.setHeader()` patch to work with get -> append -> set pattern
- deps: compression@~1.0.8
- deps: errorhandler@~1.1.1
- deps: express-session@~1.5.0
@@ -3244,8 +3402,8 @@ Shaw]
* Added node v0.1.97 compatibility
* Added support for deleting cookies via Request#cookie('key', null)
* Updated haml submodule
* Fixed not-found page, now using using charset utf-8
* Fixed show-exceptions page, now using using charset utf-8
* Fixed not-found page, now using charset utf-8
* Fixed show-exceptions page, now using charset utf-8
* Fixed view support due to fs.readFile Buffers
* Changed; mime.type() no longer accepts ".type" due to node extname() changes
@@ -3280,7 +3438,7 @@ Shaw]
==================
* Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s
encoding is set to 'utf8' or 'utf-8'.
encoding is set to 'utf8' or 'utf-8').
* Added "encoding" option to Request#render(). Closes #299
* Added "dump exceptions" setting, which is enabled by default.
* Added simple ejs template engine support
@@ -3319,7 +3477,7 @@ Shaw]
* Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js
* Added callback function support to Request#halt() as 3rd/4th arg
* Added preprocessing of route param wildcards using param(). Closes #251
* Added view partial support (with collections etc)
* Added view partial support (with collections etc.)
* Fixed bug preventing falsey params (such as ?page=0). Closes #286
* Fixed setting of multiple cookies. Closes #199
* Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)

180
Readme.md
View File

@@ -1,12 +1,29 @@
[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
**Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org).**
**This project has a [Code of Conduct][].**
## Table of contents
* [Installation](#Installation)
* [Features](#Features)
* [Docs & Community](#docs--community)
* [Quick Start](#Quick-Start)
* [Running Tests](#Running-Tests)
* [Philosophy](#Philosophy)
* [Examples](#Examples)
* [Contributing to Express](#Contributing)
* [TC (Technical Committee)](#tc-technical-committee)
* [Triagers](#triagers)
* [License](#license)
[![NPM Version][npm-version-image]][npm-url]
[![NPM Install Size][npm-install-size-image]][npm-install-size-url]
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Linux Build][ci-image]][ci-url]
[![Windows Build][appveyor-image]][appveyor-url]
[![Test Coverage][coveralls-image]][coveralls-url]
```js
const express = require('express')
@@ -33,7 +50,7 @@ the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
```bash
```console
$ npm install express
```
@@ -53,7 +70,7 @@ for more information.
## Docs & Community
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
* [#express](https://web.libera.chat/#express) on [Libera Chat](https://libera.chat) IRC
* [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules
* Visit the [Wiki](https://github.com/expressjs/express/wiki)
* [Google Group](https://groups.google.com/group/express-js) for discussion
@@ -61,35 +78,31 @@ for more information.
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/expressjs/express/wiki/New-features-in-4.x).
### Security Issues
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).
## Quick Start
The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:
Install the executable. The executable's major version will match Express's:
```bash
```console
$ npm install -g express-generator@4
```
Create the app:
```bash
```console
$ express /tmp/foo && cd /tmp/foo
```
Install dependencies:
```bash
```console
$ npm install
```
Start the server:
```bash
```console
$ npm start
```
@@ -109,50 +122,139 @@ $ npm start
To view the examples, clone the Express repo and install the dependencies:
```bash
$ git clone git://github.com/expressjs/express.git --depth 1
```console
$ git clone https://github.com/expressjs/express.git --depth 1
$ cd express
$ npm install
```
Then run whichever example you want:
```bash
```console
$ node examples/content-negotiation
```
## Tests
To run the test suite, first install the dependencies, then run `npm test`:
```bash
$ npm install
$ npm test
```
## Contributing
[Contributing Guide](Contributing.md)
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
[![Windows Build][appveyor-image]][appveyor-url]
[![Test Coverage][coveralls-image]][coveralls-url]
The Express.js project welcomes all constructive contributions. Contributions take many forms,
from code for bug fixes and enhancements, to additions and fixes to documentation, additional
tests, triaging incoming pull requests and issues, and more!
See the [Contributing Guide](Contributing.md) for more technical details on contributing.
### Security Issues
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).
### Running Tests
To run the test suite, first install the dependencies, then run `npm test`:
```console
$ npm install
$ npm test
```
## People
The original author of Express is [TJ Holowaychuk](https://github.com/tj)
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson)
[List of all contributors](https://github.com/expressjs/express/graphs/contributors)
### TC (Technical Committee)
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
* [wesleytodd](https://github.com/wesleytodd) - **Wes Todd**
* [LinusU](https://github.com/LinusU) - **Linus Unnebäck**
* [blakeembrey](https://github.com/blakeembrey) - **Blake Embrey**
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
* [crandmck](https://github.com/crandmck) - **Rand McKinney**
* [ctcpip](https://github.com/ctcpip) - **Chris de Almeida**
<details>
<summary>TC emeriti members</summary>
#### TC emeriti members
* [dougwilson](https://github.com/dougwilson) - **Douglas Wilson**
* [hacksparrow](https://github.com/hacksparrow) - **Hage Yaapa**
* [jonathanong](https://github.com/jonathanong) - **jongleberry**
* [niftylettuce](https://github.com/niftylettuce) - **niftylettuce**
* [troygoode](https://github.com/troygoode) - **Troy Goode**
</details>
### Triagers
* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair**
* [carpasse](https://github.com/carpasse) - **Carlos Serrano**
* [CBID2](https://github.com/CBID2) - **Christine Belzie**
* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
* [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin**
* [0ss](https://github.com/0ss) - **Salah**
* [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him)
* [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi**
* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)
* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**
* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him)
* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
<details>
<summary>Triagers emeriti members</summary>
#### Emeritus Triagers
* [AuggieH](https://github.com/AuggieH) - **Auggie Hudak**
* [G-Rath](https://github.com/G-Rath) - **Gareth Jones**
* [MohammadXroid](https://github.com/MohammadXroid) - **Mohammad Ayashi**
* [NawafSwe](https://github.com/NawafSwe) - **Nawaf Alsharqi**
* [NotMoni](https://github.com/NotMoni) - **Moni**
* [VigneshMurugan](https://github.com/VigneshMurugan) - **Vignesh Murugan**
* [davidmashe](https://github.com/davidmashe) - **David Ashe**
* [digitaIfabric](https://github.com/digitaIfabric) - **David**
* [e-l-i-s-e](https://github.com/e-l-i-s-e) - **Elise Bonner**
* [fed135](https://github.com/fed135) - **Frederic Charette**
* [firmanJS](https://github.com/firmanJS) - **Firman Abdul Hakim**
* [getspooky](https://github.com/getspooky) - **Yasser Ameur**
* [ghinks](https://github.com/ghinks) - **Glenn**
* [ghousemohamed](https://github.com/ghousemohamed) - **Ghouse Mohamed**
* [gireeshpunathil](https://github.com/gireeshpunathil) - **Gireesh Punathil**
* [jake32321](https://github.com/jake32321) - **Jake Reed**
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
* [lekanikotun](https://github.com/lekanikotun) - **Troy Goode**
* [marsonya](https://github.com/marsonya) - **Lekan Ikotun**
* [mastermatt](https://github.com/mastermatt) - **Matt R. Wilson**
* [maxakuru](https://github.com/maxakuru) - **Max Edell**
* [mlrawlings](https://github.com/mlrawlings) - **Michael Rawlings**
* [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov**
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
* [tarunyadav1](https://github.com/tarunyadav1) - **Tarun yadav**
* [tunniclm](https://github.com/tunniclm) - **Mike Tunnicliffe**
</details>
## License
[MIT](LICENSE)
[ci-image]: https://img.shields.io/github/workflow/status/expressjs/express/ci/master.svg?label=linux
[ci-url]: https://github.com/expressjs/express/actions?query=workflow%3Aci
[npm-image]: https://img.shields.io/npm/v/express.svg
[npm-url]: https://npmjs.org/package/express
[downloads-image]: https://img.shields.io/npm/dm/express.svg
[downloads-url]: https://npmcharts.com/compare/express?minimal=true
[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/express/master.svg?label=windows
[appveyor-image]: https://badgen.net/appveyor/ci/dougwilson/express/master?label=windows
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/express
[coveralls-image]: https://img.shields.io/coveralls/expressjs/express/master.svg
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/express/master
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/express/master?label=linux
[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml
[npm-downloads-image]: https://badgen.net/npm/dm/express
[npm-downloads-url]: https://npmcharts.com/compare/express?minimal=true
[npm-install-size-image]: https://badgen.net/packagephobia/install/express
[npm-install-size-url]: https://packagephobia.com/result?p=express
[npm-url]: https://npmjs.org/package/express
[npm-version-image]: https://badgen.net/npm/v/express
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/express
[Code of Conduct]: https://github.com/expressjs/express/blob/master/Code-Of-Conduct.md

View File

@@ -77,6 +77,13 @@ non-patch flow.
- This branch contains the commits accepted so far that implement the proposal
in the tracking pull request.
### Pre-release Versions
Alpha and Beta releases are made from a proposal branch. The version number should be
incremented to the next minor version with a `-beta` or `-alpha` suffix.
For example, if the next beta release is `5.0.1`, the beta release would be `5.0.1-beta.0`.
The pre-releases are unstable and not suitable for production use.
### Patch flow
In the patch flow, simple changes are committed to the release branch which
@@ -184,3 +191,9 @@ $ npm publish
**NOTE:** The version number to publish will be picked up automatically from
package.json.
### Step 7. Update documentation website
The documentation website https://expressjs.com/ documents the current release version in various places. For a new release:
1. Change the value of `current_version` in https://github.com/expressjs/expressjs.com/blob/gh-pages/_data/express.yml to match the latest version number.
2. Add a new section to the change log. For example, for a 4.x release, https://github.com/expressjs/expressjs.com/blob/gh-pages/en/changelog/4x.md,

View File

@@ -27,8 +27,13 @@ endeavor to keep you informed of the progress towards a fix and full
announcement, and may ask for additional information or guidance.
Report security bugs in third-party modules to the person or team maintaining
the module. You can also report a vulnerability through the
[Node Security Project](https://nodesecurity.io/report).
the module.
## Pre-release Versions
Alpha and Beta releases are unstable and **not suitable for production use**.
Vulnerabilities found in pre-releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section.
Due to the unstable nature of the branch it is not guaranteed that any fixes will be released in the next pre-release.
## Disclosure Policy
@@ -41,6 +46,10 @@ involving the following steps:
* Prepare fixes for all releases still under maintenance. These fixes will be
released as fast as possible to npm.
## The Express Threat Model
We are currently working on a new version of the security model, the most updated version can be found [here](https://github.com/expressjs/security-wg/blob/main/docs/ThreatModel.md)
## Comments on this Policy
If you have suggestions on how this process could be improved please submit a

View File

@@ -9,11 +9,18 @@ classification:
* `needs triage`: This can be kept if the triager is unsure which next steps to take
* `awaiting more info`: If more info has been requested from the author, apply this label.
* `question`: User questions that do not appear to be bugs or enhancements.
* `discuss`: Topics for discussion. Might end in an `enhancement` or `question` label.
* `bug`: Issues that present a reasonable conviction there is a reproducible bug.
* `enhancement`: Issues that are found to be a reasonable candidate feature additions.
If the issue is a question or discussion, it should be moved to GitHub Discussions.
### Moving Discussions and Questions to GitHub Discussions
For issues labeled with `question` or `discuss`, it is recommended to move them to GitHub Discussions instead:
* **Questions**: User questions that do not appear to be bugs or enhancements should be moved to GitHub Discussions.
* **Discussions**: Topics for discussion should be moved to GitHub Discussions. If the discussion leads to a new feature or bug identification, it can be moved back to Issues.
In all cases, issues may be closed by maintainers if they don't receive a timely response when
further information is sought, or when additional questions are asked.

View File

@@ -15,18 +15,30 @@ environment:
- nodejs_version: "11.15"
- nodejs_version: "12.22"
- nodejs_version: "13.14"
- nodejs_version: "14.18"
- nodejs_version: "14.20"
- nodejs_version: "15.14"
- nodejs_version: "16.20"
- nodejs_version: "17.9"
- nodejs_version: "18.19"
- nodejs_version: "19.9"
- nodejs_version: "20.11"
- nodejs_version: "21.6"
- nodejs_version: "22.0"
cache:
- node_modules
install:
# Install Node.js
- ps: >-
try { Install-Product node $env:nodejs_version -ErrorAction Stop }
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) }
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64 }
# Configure npm
- ps: |
npm config set loglevel error
npm config set shrinkwrap false
if ((npm config get package-lock) -eq "true") {
npm config set package-lock false
} else {
npm config set shrinkwrap false
}
# Remove all non-test dependencies
- ps: |
# Remove example dependencies
@@ -43,6 +55,7 @@ install:
# - use 6.x for Node.js < 8
# - use 7.x for Node.js < 10
# - use 8.x for Node.js < 12
# - use 9.x for Node.js < 14
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev mocha@3.5.3
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
@@ -53,15 +66,32 @@ install:
npm install --silent --save-dev mocha@7.2.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 12) {
npm install --silent --save-dev mocha@8.4.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 14) {
npm install --silent --save-dev mocha@9.2.2
}
- ps: |
# nyc for test coverage
# - use 10.3.2 for Node.js < 4
# - use 11.9.0 for Node.js < 6
# - use 14.1.1 for Node.js < 10
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev nyc@10.3.2
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev nyc@11.9.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
npm install --silent --save-dev nyc@14.1.1
}
- ps: |
# supertest for http calls
# - use 2.0.0 for Node.js < 4
# - use 3.4.2 for Node.js < 6
# - use 3.4.2 for Node.js < 7
# - use 6.1.6 for Node.js < 8
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev supertest@2.0.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 7) {
npm install --silent --save-dev supertest@3.4.2
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
npm install --silent --save-dev supertest@6.1.6
}
# Update Node.js modules
- ps: |

View File

@@ -1,13 +1,17 @@
all:
@./run 1 middleware
@./run 5 middleware
@./run 10 middleware
@./run 15 middleware
@./run 20 middleware
@./run 30 middleware
@./run 50 middleware
@./run 100 middleware
@./run 1 middleware 50
@./run 5 middleware 50
@./run 10 middleware 50
@./run 15 middleware 50
@./run 20 middleware 50
@./run 30 middleware 50
@./run 50 middleware 50
@./run 100 middleware 50
@./run 10 middleware 100
@./run 10 middleware 250
@./run 10 middleware 500
@./run 10 middleware 1000
@echo
.PHONY: all

34
benchmarks/README.md Normal file
View File

@@ -0,0 +1,34 @@
# Express Benchmarks
## Installation
You will need to install [wrk](https://github.com/wg/wrk/blob/master/INSTALL) in order to run the benchmarks.
## Running
To run the benchmarks, first install the dependencies `npm i`, then run `make`
The output will look something like this:
```
50 connections
1 middleware
7.15ms
6784.01
[...redacted...]
1000 connections
10 middleware
139.21ms
6155.19
```
### Tip: Include Node.js version in output
You can use `make && node -v` to include the node.js version in the output.
### Tip: Save the results to a file
You can use `make > results.log` to save the results to a file `results.log`.

View File

@@ -13,7 +13,7 @@ while (n--) {
});
}
app.use(function(req, res, next){
app.use(function(req, res){
res.send('Hello World')
});

View File

@@ -4,13 +4,15 @@ echo
MW=$1 node $2 &
pid=$!
echo " $3 connections"
sleep 2
wrk 'http://localhost:3333/?foo[bar]=baz' \
-d 3 \
-c 50 \
-c $3 \
-t 8 \
| grep 'Requests/sec' \
| awk '{ print " " $2 }'
| grep 'Requests/sec\|Latency' \
| awk '{ print " " $2 }'
kill $pid

View File

@@ -13,7 +13,6 @@ This page contains list of examples using Express.
- [hello-world](./hello-world) - Simple request handler
- [markdown](./markdown) - Markdown as template engine
- [multi-router](./multi-router) - Working with multiple Express routers
- [multipart](./multipart) - Accepting multipart-encoded forms
- [mvc](./mvc) - MVC-style controllers
- [online](./online) - Tracking online user activity with `online` and `redis` packages
- [params](./params) - Working with route parameters

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/
@@ -59,14 +61,14 @@ function authenticate(name, pass, fn) {
if (!module.parent) console.log('authenticating %s:%s', name, pass);
var user = users[name];
// query the db for the given username
if (!user) return fn(new Error('cannot find user'));
if (!user) return fn(null, null)
// apply the same algorithm to the POSTed password, applying
// the hash against the pass / salt, if there is a match we
// found the user
hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {
if (err) return fn(err);
if (hash === user.hash) return fn(null, user)
fn(new Error('invalid password'));
fn(null, null)
});
}
@@ -99,8 +101,9 @@ app.get('/login', function(req, res){
res.render('login');
});
app.post('/login', function(req, res){
app.post('/login', function (req, res, next) {
authenticate(req.body.username, req.body.password, function(err, user){
if (err) return next(err)
if (user) {
// Regenerate session when signing in
// to prevent fixation

View File

@@ -6,12 +6,12 @@
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
<form method="post" action="/login">
<p>
<label>Username:</label>
<input type="text" name="username">
<label for="username">Username:</label>
<input type="text" name="username" id="username">
</p>
<p>
<label>Password:</label>
<input type="text" name="password">
<label for="password">Password:</label>
<input type="text" name="password" id="password">
</p>
<p>
<input type="submit" value="Login">

View File

@@ -1,3 +1,5 @@
'use strict'
var users = [];
users.push({ name: 'Tobi' });

View File

@@ -1,3 +1,5 @@
'use strict'
var express = require('../../');
var app = module.exports = express();
var users = require('./db');

View File

@@ -1,3 +1,4 @@
'use strict'
var users = require('./db');

View File

@@ -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) {

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,11 +1,17 @@
'use strict'
/**
* Module dependencies.
*/
var express = require('../../');
var path = require('path');
var app = module.exports = express();
// path to where the files are stored on disk
var FILES_DIR = path.join(__dirname, 'files')
app.get('/', function(req, res){
res.send('<ul>' +
'<li>Download <a href="/files/notes/groceries.txt">notes/groceries.txt</a>.</li>' +
@@ -18,9 +24,7 @@ app.get('/', function(req, res){
// /files/* is accessed via req.params[0]
// but here we name it :file
app.get('/files/:file(*)', function(req, res, next){
var filePath = path.join(__dirname, 'files', req.params.file);
res.download(filePath, function (err) {
res.download(req.params.file, { root: FILES_DIR }, function (err) {
if (!err) return; // file sent
if (err.status !== 404) return next(err); // non-404 error
// file for download not found

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -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!');
});

View File

@@ -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');

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/
@@ -24,7 +26,7 @@ app.engine('md', function(path, options, fn){
app.set('views', path.join(__dirname, 'views'));
// make it the default so we dont need .md
// make it the default, so we don't need .md
app.set('view engine', 'md');
app.get('/', function(req, res){

View File

@@ -1,3 +1,5 @@
'use strict'
var express = require('../../..');
var apiv1 = express.Router();

View File

@@ -1,3 +1,5 @@
'use strict'
var express = require('../../..');
var apiv2 = express.Router();

View File

@@ -1,3 +1,5 @@
'use strict'
var express = require('../..');
var app = module.exports = express();

View File

@@ -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');
}

View File

@@ -1,3 +1,5 @@
'use strict'
exports.index = function(req, res){
res.redirect('/users');
};

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
// faux database
var pets = exports.pets = [];

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,4 @@
'use strict'
// install redis first:
// https://redis.io/

View File

@@ -1,7 +1,10 @@
'use strict'
/**
* Module dependencies.
*/
var createError = require('http-errors')
var express = require('../../');
var app = module.exports = express();
@@ -15,14 +18,6 @@ var users = [
, { name: 'bandit' }
];
// Create HTTP error
function createError(status, message) {
var err = new Error(message);
err.status = status;
return err;
}
// Convert :to and :from to integers
app.param(['to', 'from'], function(req, res, next, num, name){
@@ -37,7 +32,8 @@ app.param(['to', 'from'], function(req, res, next, num, name){
// Load user by id
app.param('user', function(req, res, next, id){
if (req.user = users[id]) {
req.user = users[id];
if (req.user) {
next();
} else {
next(createError(404, 'failed to find user'));
@@ -56,7 +52,7 @@ app.get('/', function(req, res){
* GET :user.
*/
app.get('/user/:user', function(req, res, next){
app.get('/user/:user', function (req, res) {
res.send('user ' + req.user.name);
});
@@ -64,7 +60,7 @@ app.get('/user/:user', function(req, res, next){
* GET users :from - :to.
*/
app.get('/users/:from-:to', function(req, res, next){
app.get('/users/:from-:to', function (req, res) {
var from = req.params.from;
var to = req.params.to;
var names = users.map(function(user){ return user.name; });

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
// Fake posts database
var posts = [

View File

@@ -1,3 +1,5 @@
'use strict'
exports.index = function(req, res){
res.render('index', { title: 'Route Separation Example' });
};

View File

@@ -1,3 +1,5 @@
'use strict'
// Fake user database
var users = [

View File

@@ -3,7 +3,7 @@
<h1>Editing <%= user.name %></h1>
<div id="user">
<form action="?_method=put", method="post">
<form action="?_method=put" method="post">
<p>
Name:
<input type="text" value="<%= user.name %>" name="user[name]" />

View File

@@ -1,3 +1,4 @@
'use strict'
// install redis first:
// https://redis.io/

View File

@@ -1,3 +1,5 @@
'use strict'
var search = document.querySelector('[type=search]');
var code = document.querySelector('pre');

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Search example</title>
<style type="text/css">
<style>
body {
font: 14px "Helvetica Neue", Helvetica;
padding: 50px;
@@ -15,7 +15,7 @@
<h2>Search</h2>
<p>Try searching for "ferret" or "cat".</p>
<input type="search" name="search" value="" />
<pre />
<pre></pre>
<script src="/client.js" charset="utf-8"></script>
</body>
</html>

View File

@@ -1,3 +1,4 @@
'use strict'
// install redis first:
// https://redis.io/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/
@@ -59,7 +61,7 @@ function users(req, res, next) {
})
}
app.get('/middleware', count, users, function(req, res, next){
app.get('/middleware', count, users, function (req, res) {
res.render('index', {
title: 'Users',
count: req.count,
@@ -97,7 +99,7 @@ function users2(req, res, next) {
})
}
app.get('/middleware-locals', count2, users2, function(req, res, next){
app.get('/middleware-locals', count2, users2, function (req, res) {
// you can see now how we have much less
// to pass to res.render(). If we have
// several routes related to users this

View File

@@ -1,3 +1,5 @@
'use strict'
module.exports = User;
// faux model

View File

@@ -1,3 +1,5 @@
'use strict'
/**
* Module dependencies.
*/
@@ -32,7 +34,7 @@ app.use('/api', function(req, res, next){
if (!key) return next(error(400, 'api key required'));
// key is invalid
if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key'));
if (apiKeys.indexOf(key) === -1) return next(error(401, 'invalid api key'))
// all good, store req.key for route access
req.key = key;
@@ -70,12 +72,12 @@ var userRepos = {
// and simply expose the data
// example: http://localhost:3000/api/users/?api-key=foo
app.get('/api/users', function(req, res, next){
app.get('/api/users', function (req, res) {
res.send(users);
});
// example: http://localhost:3000/api/repos/?api-key=foo
app.get('/api/repos', function(req, res, next){
app.get('/api/repos', function (req, res) {
res.send(repos);
});

View File

@@ -29,6 +29,13 @@ var flatten = require('array-flatten');
var merge = require('utils-merge');
var resolve = require('path').resolve;
var setPrototypeOf = require('setprototypeof')
/**
* Module variables.
* @private
*/
var hasOwnProperty = Object.prototype.hasOwnProperty
var slice = Array.prototype.slice;
/**
@@ -352,7 +359,17 @@ app.param = function param(name, fn) {
app.set = function set(setting, val) {
if (arguments.length === 1) {
// app.get(setting)
return this.settings[setting];
var settings = this.settings
while (settings && settings !== Object.prototype) {
if (hasOwnProperty.call(settings, setting)) {
return settings[setting]
}
settings = Object.getPrototypeOf(settings)
}
return undefined
}
debug('set "%s" to %o', setting, val);

View File

@@ -14,6 +14,7 @@
var Buffer = require('safe-buffer').Buffer
var contentDisposition = require('content-disposition');
var createError = require('http-errors')
var deprecate = require('depd')('express');
var encodeUrl = require('encodeurl');
var escapeHtml = require('escape-html');
@@ -64,6 +65,9 @@ var charsetRegExp = /;\s*charset\s*=/;
*/
res.status = function status(code) {
if ((typeof code === 'string' || Math.floor(code) !== code) && code > 99 && code < 1000) {
deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead')
}
this.statusCode = code;
return this;
};
@@ -135,7 +139,7 @@ res.send = function send(body) {
deprecate('res.send(status): Use res.sendStatus(status) instead');
this.statusCode = chunk;
chunk = statuses[chunk]
chunk = statuses.message[chunk]
}
switch (typeof chunk) {
@@ -213,6 +217,13 @@ res.send = function send(body) {
chunk = '';
}
// alter headers for 205
if (this.statusCode === 205) {
this.set('Content-Length', '0')
this.removeHeader('Transfer-Encoding')
chunk = ''
}
if (req.method === 'HEAD') {
// skip body for HEAD
this.end();
@@ -356,7 +367,7 @@ res.jsonp = function jsonp(obj) {
*/
res.sendStatus = function sendStatus(statusCode) {
var body = statuses[statusCode] || String(statusCode)
var body = statuses.message[statusCode] || String(statusCode)
this.statusCode = statusCode;
this.type('txt');
@@ -524,7 +535,7 @@ res.sendfile = deprecate.function(res.sendfile,
* Optionally providing an alternate attachment `filename`,
* and optional callback `callback(err)`. The callback is invoked
* when the data transfer is complete, or when an error has
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
* occurred. Be sure to check `res.headersSent` if you plan to respond.
*
* Optionally providing an `options` object to use with `res.sendFile()`.
* This function will set the `Content-Disposition` header, overriding
@@ -551,6 +562,13 @@ res.download = function download (path, filename, options, callback) {
opts = null
}
// support optional filename, where options may be in it's place
if (typeof filename === 'object' &&
(typeof options === 'function' || options === undefined)) {
name = null
opts = filename
}
// set Content-Disposition when file is sent
var headers = {
'Content-Disposition': contentDisposition(name || path)
@@ -572,7 +590,9 @@ res.download = function download (path, filename, options, callback) {
opts.headers = headers
// Resolve the full path for sendFile
var fullPath = resolve(path);
var fullPath = !opts.root
? resolve(path)
: path
// send file
return this.sendFile(fullPath, opts, done)
@@ -665,9 +685,8 @@ res.format = function(obj){
var req = this.req;
var next = req.next;
var fn = obj.default;
if (fn) delete obj.default;
var keys = Object.keys(obj);
var keys = Object.keys(obj)
.filter(function (v) { return v !== 'default' })
var key = keys.length > 0
? req.accepts(keys)
@@ -678,13 +697,12 @@ res.format = function(obj){
if (key) {
this.set('Content-Type', normalizeType(key).value);
obj[key](req, this, next);
} else if (fn) {
fn();
} else if (obj.default) {
obj.default(req, this, next)
} else {
var err = new Error('Not Acceptable');
err.status = err.statusCode = 406;
err.types = normalizeTypes(keys).map(function(o){ return o.value });
next(err);
next(createError(406, {
types: normalizeTypes(keys).map(function (o) { return o.value })
}))
}
return this;
@@ -804,6 +822,14 @@ res.get = function(field){
*/
res.clearCookie = function clearCookie(name, options) {
if (options) {
if (options.maxAge) {
deprecate('res.clearCookie: Passing "options.maxAge" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.');
}
if (options.expires) {
deprecate('res.clearCookie: Passing "options.expires" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.');
}
}
var opts = merge({ expires: new Date(1), path: '/' }, options);
return this.cookie(name, '', opts);
@@ -850,9 +876,13 @@ res.cookie = function (name, value, options) {
val = 's:' + sign(val, secret);
}
if ('maxAge' in opts) {
opts.expires = new Date(Date.now() + opts.maxAge);
opts.maxAge /= 1000;
if (opts.maxAge != null) {
var maxAge = opts.maxAge - 0
if (!isNaN(maxAge)) {
opts.expires = new Date(Date.now() + maxAge)
opts.maxAge = Math.floor(maxAge / 1000)
}
}
if (opts.path == null) {
@@ -882,14 +912,16 @@ res.cookie = function (name, value, options) {
*/
res.location = function location(url) {
var loc = url;
var loc;
// "back" is an alias for the referrer
if (url === 'back') {
deprecate('res.location("back"): use res.location(req.get("Referrer") || "/") and refer to https://dub.sh/security-redirect for best practices');
loc = this.req.get('Referrer') || '/';
} else {
loc = String(url);
}
// set location
return this.set('Location', encodeUrl(loc));
};
@@ -933,12 +965,12 @@ res.redirect = function redirect(url) {
// Support text/{plain,html} by default
this.format({
text: function(){
body = statuses[status] + '. Redirecting to ' + address
body = statuses.message[status] + '. Redirecting to ' + address
},
html: function(){
var u = escapeHtml(address);
body = '<p>' + statuses[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
body = '<p>' + statuses.message[status] + '. Redirecting to ' + u + '</p>'
},
default: function(){
@@ -1113,7 +1145,7 @@ function sendfile(res, file, options, callback) {
* ability to escape characters that can trigger HTML sniffing.
*
* @param {*} value
* @param {function} replaces
* @param {function} replacer
* @param {number} spaces
* @param {boolean} escape
* @returns {string}

View File

@@ -36,7 +36,7 @@ var toString = Object.prototype.toString;
* Initialize a new `Router` with the given `options`.
*
* @param {Object} [options]
* @return {Router} which is an callable function
* @return {Router} which is a callable function
* @public
*/
@@ -108,8 +108,8 @@ proto.param = function param(name, fn) {
var ret;
if (name[0] === ':') {
deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead');
name = name.substr(1);
deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.slice(1)) + ', fn) instead')
name = name.slice(1)
}
for (var i = 0; i < len; ++i) {
@@ -142,6 +142,7 @@ proto.handle = function handle(req, res, out) {
var protohost = getProtohost(req.url) || ''
var removed = '';
var slashAdded = false;
var sync = 0
var paramcalled = {};
// store options for OPTIONS request
@@ -180,14 +181,14 @@ proto.handle = function handle(req, res, out) {
// remove added slash
if (slashAdded) {
req.url = req.url.substr(1);
req.url = req.url.slice(1)
slashAdded = false;
}
// restore altered req.url
if (removed.length !== 0) {
req.baseUrl = parentUrl;
req.url = protohost + removed + req.url.substr(protohost.length);
req.url = protohost + removed + req.url.slice(protohost.length)
removed = '';
}
@@ -203,6 +204,11 @@ proto.handle = function handle(req, res, out) {
return;
}
// max sync stack
if (++sync > 100) {
return setImmediate(next, err)
}
// get pathname of request
var path = getPathname(req);
@@ -251,7 +257,6 @@ proto.handle = function handle(req, res, out) {
// don't even bother matching route
if (!has_method && method !== 'HEAD') {
match = false;
continue;
}
}
@@ -274,21 +279,21 @@ proto.handle = function handle(req, res, out) {
// this should be done for the layer
self.process_params(layer, paramcalled, req, res, function (err) {
if (err) {
return next(layerError || err);
next(layerError || err)
} else if (route) {
layer.handle_request(req, res, next)
} else {
trim_prefix(layer, layerError, layerPath, path)
}
if (route) {
return layer.handle_request(req, res, next);
}
trim_prefix(layer, layerError, layerPath, path);
sync = 0
});
}
function trim_prefix(layer, layerError, layerPath, path) {
if (layerPath.length !== 0) {
// Validate path is a prefix match
if (layerPath !== path.substr(0, layerPath.length)) {
if (layerPath !== path.slice(0, layerPath.length)) {
next(layerError)
return
}
@@ -301,7 +306,7 @@ proto.handle = function handle(req, res, out) {
// middleware (.use stuff) needs to have the path stripped
debug('trim prefix (%s) from url %s', layerPath, req.url);
removed = layerPath;
req.url = protohost + req.url.substr(protohost.length + removed.length);
req.url = protohost + req.url.slice(protohost.length + removed.length)
// Ensure leading slash
if (!protohost && req.url[0] !== '/') {
@@ -547,10 +552,10 @@ function getProtohost(url) {
var pathLength = searchIndex !== -1
? searchIndex
: url.length
var fqdnIndex = url.substr(0, pathLength).indexOf('://')
var fqdnIndex = url.slice(0, pathLength).indexOf('://')
return fqdnIndex !== -1
? url.substr(0, url.indexOf('/', 3 + fqdnIndex))
? url.substring(0, url.indexOf('/', 3 + fqdnIndex))
: undefined
}

View File

@@ -60,7 +60,10 @@ Route.prototype._handles_method = function _handles_method(method) {
return true;
}
var name = method.toLowerCase();
// normalize name
var name = typeof method === 'string'
? method.toLowerCase()
: method
if (name === 'head' && !this.methods['head']) {
name = 'get';
@@ -98,11 +101,15 @@ Route.prototype._options = function _options() {
Route.prototype.dispatch = function dispatch(req, res, done) {
var idx = 0;
var stack = this.stack;
var sync = 0
if (stack.length === 0) {
return done();
}
var method = typeof req.method === 'string'
? req.method.toLowerCase()
: req.method
var method = req.method.toLowerCase();
if (method === 'head' && !this.methods['head']) {
method = 'get';
}
@@ -122,20 +129,27 @@ Route.prototype.dispatch = function dispatch(req, res, done) {
return done(err)
}
var layer = stack[idx++];
// max sync stack
if (++sync > 100) {
return setImmediate(next, err)
}
var layer = stack[idx++]
// end of layers
if (!layer) {
return done(err);
return done(err)
}
if (layer.method && layer.method !== method) {
return next(err);
}
if (err) {
next(err)
} else if (err) {
layer.handle_error(err, req, res, next);
} else {
layer.handle_request(req, res, next);
}
sync = 0
}
};

View File

@@ -117,16 +117,15 @@ exports.contentDisposition = deprecate.function(contentDisposition,
/**
* Parse accept params `str` returning an
* object with `.value`, `.quality` and `.params`.
* also includes `.originalIndex` for stable sorting
*
* @param {String} str
* @return {Object}
* @api private
*/
function acceptParams(str, index) {
function acceptParams (str) {
var parts = str.split(/ *; */);
var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
var ret = { value: parts[0], quality: 1, params: {} }
for (var i = 1; i < parts.length; ++i) {
var pms = parts[i].split(/ *= */);
@@ -228,7 +227,8 @@ exports.compileTrust = function(val) {
if (typeof val === 'string') {
// Support comma-separated values
val = val.split(/ *, */);
val = val.split(',')
.map(function (v) { return v.trim() })
}
return proxyaddr.compile(val || []);
@@ -280,6 +280,7 @@ function createETagGenerator (options) {
/**
* Parse an extended query string with qs.
*
* @param {String} str
* @return {Object}
* @private
*/

View File

@@ -74,7 +74,7 @@ function View(name, options) {
if (!opts.engines[this.ext]) {
// load engine
var mod = this.ext.substr(1)
var mod = this.ext.slice(1)
debug('require "%s"', mod)
// default engine export

View File

@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Fast, unopinionated, minimalist web framework",
"version": "4.17.2",
"version": "4.22.1",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
@@ -15,6 +15,10 @@
"license": "MIT",
"repository": "expressjs/express",
"homepage": "http://expressjs.com/",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
},
"keywords": [
"express",
"framework",
@@ -28,33 +32,34 @@
"api"
],
"dependencies": {
"accepts": "~1.3.7",
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.19.1",
"content-disposition": "0.5.4",
"body-parser": "~1.20.4",
"content-disposition": "~0.5.4",
"content-type": "~1.0.4",
"cookie": "0.4.1",
"cookie-signature": "1.0.6",
"cookie": "~0.7.1",
"cookie-signature": "~1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"finalhandler": "~1.3.1",
"fresh": "~0.5.2",
"http-errors": "~2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"on-finished": "~2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"path-to-regexp": "~0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.9.6",
"qs": "~6.14.1",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.17.2",
"serve-static": "1.14.2",
"send": "~0.19.0",
"serve-static": "~1.16.2",
"setprototypeof": "1.2.0",
"statuses": "~1.5.0",
"statuses": "~2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
@@ -64,19 +69,17 @@
"connect-redis": "3.4.2",
"cookie-parser": "1.4.6",
"cookie-session": "2.0.0",
"ejs": "3.1.6",
"eslint": "7.32.0",
"ejs": "3.1.9",
"eslint": "8.47.0",
"express-session": "1.17.2",
"hbs": "4.2.0",
"istanbul": "0.4.5",
"marked": "0.7.0",
"method-override": "3.0.0",
"mocha": "9.1.3",
"mocha": "^6.2.2",
"morgan": "1.10.0",
"multiparty": "4.2.2",
"nyc": "^14.1.1",
"pbkdf2-password": "1.2.1",
"should": "13.2.3",
"supertest": "6.1.6",
"supertest": "^6.1.6",
"vhost": "~3.0.2"
},
"engines": {
@@ -92,8 +95,8 @@
"scripts": {
"lint": "eslint .",
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-ci": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=lcovonly --reporter=text npm test",
"test-cov": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=html --reporter=text npm test",
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
}
}

View File

@@ -1,6 +1,7 @@
'use strict'
var after = require('after');
var should = require('should');
var assert = require('assert')
var express = require('../')
, Route = express.Route
, methods = require('methods')
@@ -12,6 +13,37 @@ describe('Route', function(){
route.dispatch(req, {}, done)
})
it('should not stack overflow with a large sync stack', function (done) {
this.timeout(5000) // long-running test
var req = { method: 'GET', url: '/' }
var route = new Route('/foo')
route.get(function (req, res, next) {
req.counter = 0
next()
})
for (var i = 0; i < 6000; i++) {
route.all(function (req, res, next) {
req.counter++
next()
})
}
route.get(function (req, res, next) {
req.called = true
next()
})
route.dispatch(req, {}, function (err) {
if (err) return done(err)
assert.ok(req.called)
assert.strictEqual(req.counter, 6000)
done()
})
})
describe('.all', function(){
it('should add handler', function(done){
var req = { method: 'GET', url: '/' };
@@ -24,7 +56,7 @@ describe('Route', function(){
route.dispatch(req, {}, function (err) {
if (err) return done(err);
should(req.called).be.ok()
assert.ok(req.called)
done();
});
})
@@ -34,7 +66,7 @@ describe('Route', function(){
var route = new Route('/foo');
var cb = after(methods.length, function (err) {
if (err) return done(err);
count.should.equal(methods.length);
assert.strictEqual(count, methods.length)
done();
});
@@ -65,7 +97,7 @@ describe('Route', function(){
route.dispatch(req, {}, function (err) {
if (err) return done(err);
req.count.should.equal(2);
assert.strictEqual(req.count, 2)
done();
});
})
@@ -83,7 +115,7 @@ describe('Route', function(){
route.dispatch(req, {}, function (err) {
if (err) return done(err);
should(req.called).be.ok()
assert.ok(req.called)
done();
});
})
@@ -92,7 +124,7 @@ describe('Route', function(){
var req = { method: 'POST', url: '/' };
var route = new Route('');
route.get(function(req, res, next) {
route.get(function () {
throw new Error('not me!');
})
@@ -103,7 +135,7 @@ describe('Route', function(){
route.dispatch(req, {}, function (err) {
if (err) return done(err);
should(req.called).be.true()
assert.ok(req.called)
done();
});
})
@@ -129,7 +161,7 @@ describe('Route', function(){
route.dispatch(req, {}, function (err) {
if (err) return done(err);
req.order.should.equal('abc');
assert.strictEqual(req.order, 'abc')
done();
});
})
@@ -155,9 +187,9 @@ describe('Route', function(){
});
route.dispatch(req, {}, function (err) {
should(err).be.ok()
should(err.message).equal('foobar');
req.order.should.equal('a');
assert.ok(err)
assert.strictEqual(err.message, 'foobar')
assert.strictEqual(req.order, 'a')
done();
});
})
@@ -166,7 +198,7 @@ describe('Route', function(){
var req = { order: '', method: 'GET', url: '/' };
var route = new Route('');
route.all(function(req, res, next){
route.all(function () {
throw new Error('foobar');
});
@@ -181,9 +213,9 @@ describe('Route', function(){
});
route.dispatch(req, {}, function (err) {
should(err).be.ok()
should(err.message).equal('foobar');
req.order.should.equal('a');
assert.ok(err)
assert.strictEqual(err.message, 'foobar')
assert.strictEqual(req.order, 'a')
done();
});
});
@@ -192,7 +224,7 @@ describe('Route', function(){
var req = { method: 'GET', url: '/' };
var route = new Route('');
route.get(function(req, res, next){
route.get(function () {
throw new Error('boom!');
});
@@ -207,7 +239,7 @@ describe('Route', function(){
route.dispatch(req, {}, function (err) {
if (err) return done(err);
should(req.message).equal('oops');
assert.strictEqual(req.message, 'oops')
done();
});
});
@@ -221,8 +253,8 @@ describe('Route', function(){
});
route.dispatch(req, {}, function(err){
should(err).be.ok()
err.message.should.equal('boom!');
assert.ok(err)
assert.strictEqual(err.message, 'boom!')
done();
});
});
@@ -233,7 +265,7 @@ describe('Route', function(){
route.all(function(err, req, res, next){
// this should not execute
true.should.be.false()
throw new Error('should not be called')
});
route.dispatch(req, {}, done);

View File

@@ -1,3 +1,4 @@
'use strict'
var after = require('after');
var express = require('../')
@@ -60,7 +61,36 @@ describe('Router', function(){
router.handle({ method: 'GET' }, {}, done)
})
it('handle missing method', function (done) {
var all = false
var router = new Router()
var route = router.route('/foo')
var use = false
route.post(function (req, res, next) { next(new Error('should not run')) })
route.all(function (req, res, next) {
all = true
next()
})
route.get(function (req, res, next) { next(new Error('should not run')) })
router.get('/foo', function (req, res, next) { next(new Error('should not run')) })
router.use(function (req, res, next) {
use = true
next()
})
router.handle({ url: '/foo' }, {}, function (err) {
if (err) return done(err)
assert.ok(all)
assert.ok(use)
done()
})
})
it('should not stack overflow with many registered routes', function(done){
this.timeout(5000) // long-running test
var handler = function(req, res){ res.end(new Error('wrong handler')) };
var router = new Router();
@@ -75,6 +105,56 @@ describe('Router', function(){
router.handle({ url: '/', method: 'GET' }, { end: done });
});
it('should not stack overflow with a large sync route stack', function (done) {
this.timeout(5000) // long-running test
var router = new Router()
router.get('/foo', function (req, res, next) {
req.counter = 0
next()
})
for (var i = 0; i < 6000; i++) {
router.get('/foo', function (req, res, next) {
req.counter++
next()
})
}
router.get('/foo', function (req, res) {
assert.strictEqual(req.counter, 6000)
res.end()
})
router.handle({ url: '/foo', method: 'GET' }, { end: done })
})
it('should not stack overflow with a large sync middleware stack', function (done) {
this.timeout(5000) // long-running test
var router = new Router()
router.use(function (req, res, next) {
req.counter = 0
next()
})
for (var i = 0; i < 6000; i++) {
router.use(function (req, res, next) {
req.counter++
next()
})
}
router.use(function (req, res) {
assert.strictEqual(req.counter, 6000)
res.end()
})
router.handle({ url: '/', method: 'GET' }, { end: done })
})
describe('.handle', function(){
it('should dispatch', function(done){
var router = new Router();
@@ -148,7 +228,7 @@ describe('Router', function(){
it('should handle throwing inside routes with params', function(done) {
var router = new Router();
router.get('/foo/:id', function(req, res, next){
router.get('/foo/:id', function () {
throw new Error('foo');
});
@@ -526,8 +606,8 @@ describe('Router', function(){
var req2 = { url: '/foo/10/bar', method: 'get' };
var router = new Router();
var sub = new Router();
var cb = after(2, done)
done = after(2, done);
sub.get('/bar', function(req, res, next) {
next();
@@ -546,14 +626,14 @@ describe('Router', function(){
assert.ifError(err);
assert.equal(req1.ms, 50);
assert.equal(req1.originalUrl, '/foo/50/bar');
done();
cb()
});
router.handle(req2, {}, function(err) {
assert.ifError(err);
assert.equal(req2.ms, 10);
assert.equal(req2.originalUrl, '/foo/10/bar');
done();
cb()
});
});
});

View File

@@ -22,7 +22,7 @@ describe('auth', function(){
.expect(200, /<form/, done)
})
it('should display login error', function(done){
it('should display login error for bad user', function (done) {
request(app)
.post('/login')
.type('urlencoded')
@@ -36,6 +36,21 @@ describe('auth', function(){
.expect(200, /Authentication failed/, done)
})
})
it('should display login error for bad password', function (done) {
request(app)
.post('/login')
.type('urlencoded')
.send('username=tj&password=nogood')
.expect('Location', '/login')
.expect(302, function (err, res) {
if (err) return done(err)
request(app)
.get('/login')
.set('Cookie', getCookie(res))
.expect(200, /Authentication failed/, done)
})
})
})
describe('GET /logout',function(){

View File

@@ -36,4 +36,12 @@ describe('downloads', function(){
.expect(404, done)
})
})
describe('GET /files/../index.js', function () {
it('should respond with 403', function (done) {
request(app)
.get('/files/../index.js')
.expect(403, done)
})
})
})

View File

@@ -0,0 +1,21 @@
var app = require('../../examples/hello-world')
var request = require('supertest')
describe('hello-world', function () {
describe('GET /', function () {
it('should respond with hello world', function (done) {
request(app)
.get('/')
.expect(200, 'Hello World', done)
})
})
describe('GET /missing', function () {
it('should respond with 404', function (done) {
request(app)
.get('/missing')
.expect(404, done)
})
})
})

View File

@@ -1,22 +1,25 @@
'use strict'
var after = require('after')
var express = require('../')
, request = require('supertest');
describe('app.all()', function(){
it('should add a router per method', function(done){
var app = express();
var cb = after(2, done)
app.all('/tobi', function(req, res){
res.end(req.method);
});
request(app)
.put('/tobi')
.expect('PUT', function(){
request(app)
.put('/tobi')
.expect(200, 'PUT', cb)
request(app)
.get('/tobi')
.expect('GET', done);
});
.expect(200, 'GET', cb)
})
it('should run the callback for a method just once', function(done){

View File

@@ -1,3 +1,4 @@
'use strict'
var express = require('../')
, request = require('supertest');

View File

@@ -1,4 +1,6 @@
'use strict'
var assert = require('assert')
var express = require('../')
, fs = require('fs');
var path = require('path')
@@ -22,16 +24,16 @@ describe('app', function(){
app.render('user.html', function(err, str){
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
it('should throw when the callback is missing', function(){
var app = express();
(function(){
assert.throws(function () {
app.engine('.html', null);
}).should.throw('callback function required');
}, /callback function required/)
})
it('should work without leading "."', function(done){
@@ -43,7 +45,7 @@ describe('app', function(){
app.render('user.html', function(err, str){
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -58,7 +60,7 @@ describe('app', function(){
app.render('user', function(err, str){
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -73,7 +75,7 @@ describe('app', function(){
app.render('user', function(err, str){
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})

View File

@@ -1,3 +1,4 @@
'use strict'
var express = require('../');
var request = require('supertest');
@@ -46,23 +47,20 @@ describe('HEAD', function(){
describe('app.head()', function(){
it('should override', function(done){
var app = express()
, called;
app.head('/tobi', function(req, res){
called = true;
res.end('');
res.header('x-method', 'head')
res.end()
});
app.get('/tobi', function(req, res){
assert(0, 'should not call GET');
res.header('x-method', 'get')
res.send('tobi');
});
request(app)
.head('/tobi')
.expect(200, function(){
assert(called);
done();
});
.head('/tobi')
.expect('x-method', 'head')
.expect(200, done)
})
})

View File

@@ -1,3 +1,4 @@
'use strict'
var assert = require('assert')
var express = require('..')
@@ -32,8 +33,8 @@ describe('app.parent', function(){
blog.use('/admin', blogAdmin);
assert(!app.parent, 'app.parent');
blog.parent.should.equal(app);
blogAdmin.parent.should.equal(blog);
assert.strictEqual(blog.parent, app)
assert.strictEqual(blogAdmin.parent, blog)
})
})
@@ -48,10 +49,10 @@ describe('app.mountpath', function(){
app.use(fallback);
blog.use('/admin', admin);
admin.mountpath.should.equal('/admin');
app.mountpath.should.equal('/');
blog.mountpath.should.equal('/blog');
fallback.mountpath.should.equal('/');
assert.strictEqual(admin.mountpath, '/admin')
assert.strictEqual(app.mountpath, '/')
assert.strictEqual(blog.mountpath, '/blog')
assert.strictEqual(fallback.mountpath, '/')
})
})
@@ -76,35 +77,56 @@ describe('app.path()', function(){
app.use('/blog', blog);
blog.use('/admin', blogAdmin);
app.path().should.equal('');
blog.path().should.equal('/blog');
blogAdmin.path().should.equal('/blog/admin');
assert.strictEqual(app.path(), '')
assert.strictEqual(blog.path(), '/blog')
assert.strictEqual(blogAdmin.path(), '/blog/admin')
})
})
describe('in development', function(){
before(function () {
this.env = process.env.NODE_ENV
process.env.NODE_ENV = 'development'
})
after(function () {
process.env.NODE_ENV = this.env
})
it('should disable "view cache"', function(){
process.env.NODE_ENV = 'development';
var app = express();
app.enabled('view cache').should.be.false()
process.env.NODE_ENV = 'test';
assert.ok(!app.enabled('view cache'))
})
})
describe('in production', function(){
before(function () {
this.env = process.env.NODE_ENV
process.env.NODE_ENV = 'production'
})
after(function () {
process.env.NODE_ENV = this.env
})
it('should enable "view cache"', function(){
process.env.NODE_ENV = 'production';
var app = express();
app.enabled('view cache').should.be.true()
process.env.NODE_ENV = 'test';
assert.ok(app.enabled('view cache'))
})
})
describe('without NODE_ENV', function(){
before(function () {
this.env = process.env.NODE_ENV
process.env.NODE_ENV = ''
})
after(function () {
process.env.NODE_ENV = this.env
})
it('should default to development', function(){
process.env.NODE_ENV = '';
var app = express();
app.get('env').should.equal('development');
process.env.NODE_ENV = 'test';
assert.strictEqual(app.get('env'), 'development')
})
})

View File

@@ -1,3 +1,4 @@
'use strict'
var express = require('../')
@@ -5,9 +6,8 @@ describe('app.listen()', function(){
it('should wrap with an HTTP server', function(done){
var app = express();
var server = app.listen(9999, function(){
server.close();
done();
var server = app.listen(0, function () {
server.close(done)
});
})
})

View File

@@ -1,26 +1,25 @@
'use strict'
var assert = require('assert')
var express = require('../')
describe('app', function(){
describe('.locals(obj)', function(){
it('should merge locals', function(){
var app = express();
Object.keys(app.locals).should.eql(['settings']);
app.locals.user = 'tobi';
app.locals.age = 2;
Object.keys(app.locals).should.eql(['settings', 'user', 'age']);
app.locals.user.should.equal('tobi');
app.locals.age.should.equal(2);
describe('.locals', function () {
it('should default object', function () {
var app = express()
assert.ok(app.locals)
assert.strictEqual(typeof app.locals, 'object')
})
})
describe('.locals.settings', function(){
it('should expose app settings', function(){
var app = express();
app.set('title', 'House of Manny');
var obj = app.locals.settings;
obj.should.have.property('env', 'test');
obj.should.have.property('title', 'House of Manny');
describe('.settings', function () {
it('should contain app settings ', function () {
var app = express()
app.set('title', 'Express')
assert.ok(app.locals.settings)
assert.strictEqual(typeof app.locals.settings, 'object')
assert.strictEqual(app.locals.settings, app.settings)
assert.strictEqual(app.locals.settings.title, 'Express')
})
})
})
})

View File

@@ -1,3 +1,4 @@
'use strict'
var express = require('../')
, request = require('supertest');

View File

@@ -1,4 +1,6 @@
'use strict'
var assert = require('assert')
var express = require('../')
, request = require('supertest');
@@ -40,7 +42,7 @@ describe('app', function(){
it('should fail if not given fn', function(){
var app = express();
app.param.bind(app, ':name', 'bob').should.throw();
assert.throws(app.param.bind(app, ':name', 'bob'))
})
})
@@ -57,24 +59,22 @@ describe('app', function(){
app.get('/post/:id', function(req, res){
var id = req.params.id;
id.should.be.a.Number()
res.send('' + id);
res.send((typeof id) + ':' + id)
});
app.get('/user/:uid', function(req, res){
var id = req.params.id;
id.should.be.a.Number()
res.send('' + id);
res.send((typeof id) + ':' + id)
});
request(app)
.get('/user/123')
.expect(200, '123', function (err) {
if (err) return done(err)
request(app)
.get('/post/123')
.expect('123', done);
})
.get('/user/123')
.expect(200, 'number:123', function (err) {
if (err) return done(err)
request(app)
.get('/post/123')
.expect('number:123', done)
})
})
})
@@ -91,13 +91,12 @@ describe('app', function(){
app.get('/user/:id', function(req, res){
var id = req.params.id;
id.should.be.a.Number()
res.send('' + id);
res.send((typeof id) + ':' + id)
});
request(app)
.get('/user/123')
.expect('123', done);
.get('/user/123')
.expect(200, 'number:123', done)
})
it('should only call once per request', function(done) {
@@ -167,7 +166,7 @@ describe('app', function(){
app.get('/:user', function(req, res, next) {
next('route');
});
app.get('/:user', function(req, res, next) {
app.get('/:user', function (req, res) {
res.send(req.params.user);
});
@@ -188,11 +187,11 @@ describe('app', function(){
next(new Error('invalid invocation'))
});
app.post('/:user', function(req, res, next) {
app.post('/:user', function (req, res) {
res.send(req.params.user);
});
app.get('/:thing', function(req, res, next) {
app.get('/:thing', function (req, res) {
res.send(req.thing);
});

View File

@@ -1,3 +1,4 @@
'use strict'
var assert = require('assert')
var express = require('..');
@@ -13,7 +14,7 @@ describe('app', function(){
app.render(path.join(__dirname, 'fixtures', 'user.tmpl'), function (err, str) {
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -26,7 +27,7 @@ describe('app', function(){
app.render(path.join(__dirname, 'fixtures', 'user'), function (err, str) {
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -39,7 +40,7 @@ describe('app', function(){
app.render('user.tmpl', function (err, str) {
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -52,7 +53,7 @@ describe('app', function(){
app.render('blog/post', function (err, str) {
if (err) return done(err);
str.should.equal('<h1>blog post</h1>');
assert.strictEqual(str, '<h1>blog post</h1>')
done();
})
})
@@ -72,8 +73,8 @@ describe('app', function(){
app.set('view', View);
app.render('something', function(err, str){
err.should.be.ok()
err.message.should.equal('err!');
assert.ok(err)
assert.strictEqual(err.message, 'err!')
done();
})
})
@@ -113,7 +114,7 @@ describe('app', function(){
app.render('email.tmpl', function (err, str) {
if (err) return done(err);
str.should.equal('<p>This is an email</p>');
assert.strictEqual(str, '<p>This is an email</p>')
done();
})
})
@@ -128,7 +129,7 @@ describe('app', function(){
app.render('email', function(err, str){
if (err) return done(err);
str.should.equal('<p>This is an email</p>');
assert.strictEqual(str, '<p>This is an email</p>')
done();
})
})
@@ -143,7 +144,7 @@ describe('app', function(){
app.render('user.tmpl', function (err, str) {
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -161,7 +162,7 @@ describe('app', function(){
app.render('user.tmpl', function (err, str) {
if (err) return done(err);
str.should.equal('<span>tobi</span>');
assert.strictEqual(str, '<span>tobi</span>')
done();
})
})
@@ -178,7 +179,7 @@ describe('app', function(){
app.render('name.tmpl', function (err, str) {
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -219,7 +220,7 @@ describe('app', function(){
app.render('something', function(err, str){
if (err) return done(err);
str.should.equal('abstract engine');
assert.strictEqual(str, 'abstract engine')
done();
})
})
@@ -245,12 +246,12 @@ describe('app', function(){
app.render('something', function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
assert.strictEqual(count, 1)
assert.strictEqual(str, 'abstract engine')
app.render('something', function(err, str){
if (err) return done(err);
count.should.equal(2);
str.should.equal('abstract engine');
assert.strictEqual(count, 2)
assert.strictEqual(str, 'abstract engine')
done();
})
})
@@ -275,12 +276,12 @@ describe('app', function(){
app.render('something', function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
assert.strictEqual(count, 1)
assert.strictEqual(str, 'abstract engine')
app.render('something', function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
assert.strictEqual(count, 1)
assert.strictEqual(str, 'abstract engine')
done();
})
})
@@ -298,7 +299,7 @@ describe('app', function(){
app.render('user.tmpl', { user: user }, function (err, str) {
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -311,7 +312,7 @@ describe('app', function(){
app.render('user.tmpl', {}, function (err, str) {
if (err) return done(err);
str.should.equal('<p>tobi</p>');
assert.strictEqual(str, '<p>tobi</p>')
done();
})
})
@@ -325,7 +326,7 @@ describe('app', function(){
app.render('user.tmpl', { user: jane }, function (err, str) {
if (err) return done(err);
str.should.equal('<p>jane</p>');
assert.strictEqual(str, '<p>jane</p>')
done();
})
})
@@ -350,12 +351,12 @@ describe('app', function(){
app.render('something', {cache: true}, function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
assert.strictEqual(count, 1)
assert.strictEqual(str, 'abstract engine')
app.render('something', {cache: true}, function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
assert.strictEqual(count, 1)
assert.strictEqual(str, 'abstract engine')
done();
})
})

View File

@@ -1,4 +1,6 @@
'use strict'
var after = require('after')
var express = require('../')
, request = require('supertest');
@@ -19,5 +21,123 @@ describe('app', function(){
.get('/foo?name=tobi')
.expect('name=tobi', done);
})
it('should only extend for the referenced app', function (done) {
var app1 = express()
var app2 = express()
var cb = after(2, done)
app1.request.foobar = function () {
return 'tobi'
}
app1.get('/', function (req, res) {
res.send(req.foobar())
})
app2.get('/', function (req, res) {
res.send(req.foobar())
})
request(app1)
.get('/')
.expect(200, 'tobi', cb)
request(app2)
.get('/')
.expect(500, /(?:not a function|has no method)/, cb)
})
it('should inherit to sub apps', function (done) {
var app1 = express()
var app2 = express()
var cb = after(2, done)
app1.request.foobar = function () {
return 'tobi'
}
app1.use('/sub', app2)
app1.get('/', function (req, res) {
res.send(req.foobar())
})
app2.get('/', function (req, res) {
res.send(req.foobar())
})
request(app1)
.get('/')
.expect(200, 'tobi', cb)
request(app1)
.get('/sub')
.expect(200, 'tobi', cb)
})
it('should allow sub app to override', function (done) {
var app1 = express()
var app2 = express()
var cb = after(2, done)
app1.request.foobar = function () {
return 'tobi'
}
app2.request.foobar = function () {
return 'loki'
}
app1.use('/sub', app2)
app1.get('/', function (req, res) {
res.send(req.foobar())
})
app2.get('/', function (req, res) {
res.send(req.foobar())
})
request(app1)
.get('/')
.expect(200, 'tobi', cb)
request(app1)
.get('/sub')
.expect(200, 'loki', cb)
})
it('should not pollute parent app', function (done) {
var app1 = express()
var app2 = express()
var cb = after(2, done)
app1.request.foobar = function () {
return 'tobi'
}
app2.request.foobar = function () {
return 'loki'
}
app1.use('/sub', app2)
app1.get('/sub/foo', function (req, res) {
res.send(req.foobar())
})
app2.get('/', function (req, res) {
res.send(req.foobar())
})
request(app1)
.get('/sub')
.expect(200, 'loki', cb)
request(app1)
.get('/sub/foo')
.expect(200, 'tobi', cb)
})
})
})

View File

@@ -1,4 +1,6 @@
'use strict'
var after = require('after')
var express = require('../')
, request = require('supertest');
@@ -20,25 +22,122 @@ describe('app', function(){
.expect('HEY', done);
})
it('should not be influenced by other app protos', function(done){
var app = express()
, app2 = express();
it('should only extend for the referenced app', function (done) {
var app1 = express()
var app2 = express()
var cb = after(2, done)
app.response.shout = function(str){
this.send(str.toUpperCase());
};
app1.response.shout = function (str) {
this.send(str.toUpperCase())
}
app2.response.shout = function(str){
this.send(str);
};
app1.get('/', function (req, res) {
res.shout('foo')
})
app.use(function(req, res){
res.shout('hey');
});
app2.get('/', function (req, res) {
res.shout('foo')
})
request(app)
.get('/')
.expect('HEY', done);
request(app1)
.get('/')
.expect(200, 'FOO', cb)
request(app2)
.get('/')
.expect(500, /(?:not a function|has no method)/, cb)
})
it('should inherit to sub apps', function (done) {
var app1 = express()
var app2 = express()
var cb = after(2, done)
app1.response.shout = function (str) {
this.send(str.toUpperCase())
}
app1.use('/sub', app2)
app1.get('/', function (req, res) {
res.shout('foo')
})
app2.get('/', function (req, res) {
res.shout('foo')
})
request(app1)
.get('/')
.expect(200, 'FOO', cb)
request(app1)
.get('/sub')
.expect(200, 'FOO', cb)
})
it('should allow sub app to override', function (done) {
var app1 = express()
var app2 = express()
var cb = after(2, done)
app1.response.shout = function (str) {
this.send(str.toUpperCase())
}
app2.response.shout = function (str) {
this.send(str + '!')
}
app1.use('/sub', app2)
app1.get('/', function (req, res) {
res.shout('foo')
})
app2.get('/', function (req, res) {
res.shout('foo')
})
request(app1)
.get('/')
.expect(200, 'FOO', cb)
request(app1)
.get('/sub')
.expect(200, 'foo!', cb)
})
it('should not pollute parent app', function (done) {
var app1 = express()
var app2 = express()
var cb = after(2, done)
app1.response.shout = function (str) {
this.send(str.toUpperCase())
}
app2.response.shout = function (str) {
this.send(str + '!')
}
app1.use('/sub', app2)
app1.get('/sub/foo', function (req, res) {
res.shout('foo')
})
app2.get('/', function (req, res) {
res.shout('foo')
})
request(app1)
.get('/sub')
.expect(200, 'foo!', cb)
request(app1)
.get('/sub/foo')
.expect(200, 'FOO', cb)
})
})
})

View File

@@ -1,3 +1,5 @@
'use strict'
var express = require('../');
var request = require('supertest');

View File

@@ -1,3 +1,4 @@
'use strict'
var after = require('after');
var express = require('../')
@@ -5,6 +6,8 @@ var express = require('../')
, assert = require('assert')
, methods = require('methods');
var shouldSkipQuery = require('./support/utils').shouldSkipQuery
describe('app.router', function(){
it('should restore req.params after leaving router', function(done){
var app = express();
@@ -38,6 +41,9 @@ describe('app.router', function(){
if (method === 'connect') return;
it('should include ' + method.toUpperCase(), function(done){
if (method === 'query' && shouldSkipQuery(process.versions.node)) {
this.skip()
}
var app = express();
app[method]('/foo', function(req, res){
@@ -49,9 +55,20 @@ describe('app.router', function(){
.expect(200, done)
})
it('should not support ' + method.toUpperCase() + ' without a path', function () {
if (method === 'get' || (method === 'query' && shouldSkipQuery(process.versions.node))) {
this.skip();
}
var app = express();
assert.throws(function () {
app[method](function (req, res) { });
});
});
it('should reject numbers for app.' + method, function(){
var app = express();
app[method].bind(app, '/', 3).should.throw(/Number/);
assert.throws(app[method].bind(app, '/', 3), /Number/)
})
});
@@ -89,7 +106,7 @@ describe('app.router', function(){
it('should decode correct params', function(done){
var app = express();
app.get('/:name', function(req, res, next){
app.get('/:name', function (req, res) {
res.send(req.params.name);
});
@@ -101,7 +118,7 @@ describe('app.router', function(){
it('should not accept params in malformed paths', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
app.get('/:name', function (req, res) {
res.send(req.params.name);
});
@@ -113,7 +130,7 @@ describe('app.router', function(){
it('should not decode spaces', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
app.get('/:name', function (req, res) {
res.send(req.params.name);
});
@@ -125,7 +142,7 @@ describe('app.router', function(){
it('should work with unicode', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
app.get('/:name', function (req, res) {
res.send(req.params.name);
});
@@ -187,6 +204,23 @@ describe('app.router', function(){
.expect('editing user 10', done);
})
if (supportsRegexp('(?<foo>.*)')) {
it('should populate req.params with named captures', function(done){
var app = express();
var re = new RegExp('^/user/(?<userId>[0-9]+)/(view|edit)?$');
app.get(re, function(req, res){
var id = req.params.userId
, op = req.params[0];
res.end(op + 'ing user ' + id);
});
request(app)
.get('/user/10/edit')
.expect('editing user 10', done);
})
}
it('should ensure regexp matches path prefix', function (done) {
var app = express()
var p = []
@@ -636,18 +670,19 @@ describe('app.router', function(){
it('should work cross-segment', function(done){
var app = express();
var cb = after(2, done)
app.get('/api*', function(req, res){
res.send(req.params[0]);
});
request(app)
.get('/api')
.expect('', function(){
request(app)
.get('/api')
.expect(200, '', cb)
request(app)
.get('/api/hey')
.expect('/hey', done);
});
.expect(200, '/hey', cb)
})
it('should allow naming', function(done){
@@ -863,36 +898,38 @@ describe('app.router', function(){
describe('.:name', function(){
it('should denote a format', function(done){
var app = express();
var cb = after(2, done)
app.get('/:name.:format', function(req, res){
res.end(req.params.name + ' as ' + req.params.format);
});
request(app)
.get('/foo.json')
.expect('foo as json', function(){
request(app)
.get('/foo.json')
.expect(200, 'foo as json', cb)
request(app)
.get('/foo')
.expect(404, done);
});
.expect(404, cb)
})
})
describe('.:name?', function(){
it('should denote an optional format', function(done){
var app = express();
var cb = after(2, done)
app.get('/:name.:format?', function(req, res){
res.end(req.params.name + ' as ' + (req.params.format || 'html'));
});
request(app)
.get('/foo')
.expect('foo as html', function(){
request(app)
.get('/foo')
.expect(200, 'foo as html', cb)
request(app)
.get('/foo.json')
.expect('foo as json', done);
});
.expect(200, 'foo as json', cb)
})
})
@@ -906,7 +943,7 @@ describe('app.router', function(){
next();
});
app.get('/bar', function(req, res){
app.get('/bar', function () {
assert(0);
});
@@ -915,7 +952,7 @@ describe('app.router', function(){
next();
});
app.get('/foo', function(req, res, next){
app.get('/foo', function (req, res) {
calls.push('/foo 2');
res.json(calls)
});
@@ -935,7 +972,7 @@ describe('app.router', function(){
next('route')
}
app.get('/foo', fn, function(req, res, next){
app.get('/foo', fn, function (req, res) {
res.end('failure')
});
@@ -960,11 +997,11 @@ describe('app.router', function(){
next('router')
}
router.get('/foo', fn, function (req, res, next) {
router.get('/foo', fn, function (req, res) {
res.end('failure')
})
router.get('/foo', function (req, res, next) {
router.get('/foo', function (req, res) {
res.end('failure')
})
@@ -991,7 +1028,7 @@ describe('app.router', function(){
next();
});
app.get('/bar', function(req, res){
app.get('/bar', function () {
assert(0);
});
@@ -1000,7 +1037,7 @@ describe('app.router', function(){
next(new Error('fail'));
});
app.get('/foo', function(req, res, next){
app.get('/foo', function () {
assert(0);
});
@@ -1102,6 +1139,15 @@ describe('app.router', function(){
it('should be chainable', function(){
var app = express();
app.get('/', function(){}).should.equal(app);
assert.strictEqual(app.get('/', function () {}), app)
})
})
function supportsRegexp(source) {
try {
new RegExp(source)
return true
} catch (e) {
return false
}
}

View File

@@ -1,3 +1,6 @@
'use strict'
var assert = require('assert')
var express = require('../')
, request = require('supertest');
@@ -34,20 +37,20 @@ describe('app', function(){
next();
}, function(err, req, res, next){
b = true;
err.message.should.equal('fabricated error');
assert.strictEqual(err.message, 'fabricated error')
next(err);
}, function(err, req, res, next){
c = true;
err.message.should.equal('fabricated error');
assert.strictEqual(err.message, 'fabricated error')
next();
}, function(err, req, res, next){
d = true;
next();
}, function(req, res){
a.should.be.false()
b.should.be.true()
c.should.be.true()
d.should.be.false()
assert.ok(!a)
assert.ok(b)
assert.ok(c)
assert.ok(!d)
res.send(204);
});

View File

@@ -1,3 +1,4 @@
'use strict'
var after = require('after');
var assert = require('assert')
@@ -10,7 +11,7 @@ describe('app', function(){
, app = express();
blog.on('mount', function(arg){
arg.should.equal(app);
assert.strictEqual(arg, app)
done();
});
@@ -37,6 +38,7 @@ describe('app', function(){
var blog = express()
, forum = express()
, app = express();
var cb = after(2, done)
blog.get('/', function(req, res){
res.end('blog');
@@ -50,12 +52,12 @@ describe('app', function(){
app.use('/forum', forum);
request(app)
.get('/blog')
.expect('blog', function(){
request(app)
.get('/blog')
.expect(200, 'blog', cb)
request(app)
.get('/forum')
.expect('forum', done);
});
.expect(200, 'forum', cb)
})
it('should set the child\'s .parent', function(){
@@ -63,7 +65,7 @@ describe('app', function(){
, app = express();
app.use('/blog', blog);
blog.parent.should.equal(app);
assert.strictEqual(blog.parent, app)
})
it('should support dynamic routes', function(done){
@@ -102,11 +104,11 @@ describe('app', function(){
});
blog.once('mount', function (parent) {
parent.should.equal(app);
assert.strictEqual(parent, app)
cb();
});
other.once('mount', function (parent) {
parent.should.equal(app);
assert.strictEqual(parent, app)
cb();
});

View File

@@ -1,3 +1,4 @@
'use strict'
var assert = require('assert');
var express = require('..');
@@ -10,6 +11,12 @@ describe('config', function () {
assert.equal(app.get('foo'), 'bar');
})
it('should set prototype values', function () {
var app = express()
app.set('hasOwnProperty', 42)
assert.strictEqual(app.get('hasOwnProperty'), 42)
})
it('should return the app', function () {
var app = express();
assert.equal(app.set('foo', 'bar'), app);
@@ -20,6 +27,17 @@ describe('config', function () {
assert.equal(app.set('foo', undefined), app);
})
it('should return set value', function () {
var app = express()
app.set('foo', 'bar')
assert.strictEqual(app.set('foo'), 'bar')
})
it('should return undefined for prototype values', function () {
var app = express()
assert.strictEqual(app.set('hasOwnProperty'), undefined)
})
describe('"etag"', function(){
it('should throw on bad value', function(){
var app = express();
@@ -50,6 +68,11 @@ describe('config', function () {
assert.strictEqual(app.get('foo'), undefined);
})
it('should return undefined for prototype values', function () {
var app = express()
assert.strictEqual(app.get('hasOwnProperty'), undefined)
})
it('should otherwise return the value', function(){
var app = express();
app.set('foo', 'bar');
@@ -124,6 +147,12 @@ describe('config', function () {
assert.equal(app.enable('tobi'), app);
assert.strictEqual(app.get('tobi'), true);
})
it('should set prototype values', function () {
var app = express()
app.enable('hasOwnProperty')
assert.strictEqual(app.get('hasOwnProperty'), true)
})
})
describe('.disable()', function(){
@@ -132,6 +161,12 @@ describe('config', function () {
assert.equal(app.disable('tobi'), app);
assert.strictEqual(app.get('tobi'), false);
})
it('should set prototype values', function () {
var app = express()
app.disable('hasOwnProperty')
assert.strictEqual(app.get('hasOwnProperty'), false)
})
})
describe('.enabled()', function(){
@@ -145,6 +180,11 @@ describe('config', function () {
app.set('foo', 'bar');
assert.strictEqual(app.enabled('foo'), true);
})
it('should default to false for prototype values', function () {
var app = express()
assert.strictEqual(app.enabled('hasOwnProperty'), false)
})
})
describe('.disabled()', function(){
@@ -158,5 +198,10 @@ describe('config', function () {
app.set('foo', 'bar');
assert.strictEqual(app.disabled('foo'), false);
})
it('should default to true for prototype values', function () {
var app = express()
assert.strictEqual(app.disabled('hasOwnProperty'), true)
})
})
})

View File

@@ -1,12 +1,12 @@
'use strict'
var assert = require('assert')
var express = require('../');
var request = require('supertest');
var should = require('should');
describe('exports', function(){
it('should expose Router', function(){
express.Router.should.be.a.Function()
assert.strictEqual(typeof express.Router, 'function')
})
it('should expose json middleware', function () {
@@ -35,20 +35,23 @@ describe('exports', function(){
})
it('should expose the application prototype', function(){
express.application.set.should.be.a.Function()
assert.strictEqual(typeof express.application, 'object')
assert.strictEqual(typeof express.application.set, 'function')
})
it('should expose the request prototype', function(){
express.request.accepts.should.be.a.Function()
assert.strictEqual(typeof express.request, 'object')
assert.strictEqual(typeof express.request.accepts, 'function')
})
it('should expose the response prototype', function(){
express.response.send.should.be.a.Function()
assert.strictEqual(typeof express.response, 'object')
assert.strictEqual(typeof express.response.send, 'function')
})
it('should permit modifying the .application prototype', function(){
express.application.foo = function(){ return 'bar'; };
express().foo().should.equal('bar');
assert.strictEqual(express().foo(), 'bar')
})
it('should permit modifying the .request prototype', function(done){
@@ -78,10 +81,7 @@ describe('exports', function(){
})
it('should throw on old middlewares', function(){
var error;
try { express.bodyParser; } catch (e) { error = e; }
should(error).have.property('message');
error.message.should.containEql('middleware');
error.message.should.containEql('bodyParser');
assert.throws(function () { express.bodyParser() }, /Error:.*middleware.*bodyParser/)
assert.throws(function () { express.limit() }, /Error:.*middleware.*limit/)
})
})

View File

@@ -1,9 +1,15 @@
'use strict'
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var express = require('..')
var request = require('supertest')
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('express.json()', function () {
it('should parse JSON', function (done) {
request(createApp())
@@ -37,6 +43,14 @@ describe('express.json()', function () {
.expect(200, '{}', done)
})
it('should 400 when only whitespace', function (done) {
request(createApp())
.post('/')
.set('Content-Type', 'application/json')
.send(' \n')
.expect(400, '[entity.parse.failed] ' + parseError(' '), done)
})
it('should 400 when invalid content-length', function (done) {
var app = express()
@@ -58,6 +72,32 @@ describe('express.json()', function () {
.expect(400, /content length/, done)
})
it('should 500 if stream not readable', function (done) {
var app = express()
app.use(function (req, res, next) {
req.on('end', next)
req.resume()
})
app.use(express.json())
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send('[' + err.type + '] ' + err.message)
})
app.post('/', function (req, res) {
res.json(req.body)
})
request(app)
.post('/')
.set('Content-Type', 'application/json')
.send('{"user":"tobi"}')
.expect(500, '[stream.not.readable] stream is not readable', done)
})
it('should handle duplicated middleware', function (done) {
var app = express()
@@ -85,7 +125,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/json')
.send('{:')
.expect(400, parseError('{:'), done)
.expect(400, '[entity.parse.failed] ' + parseError('{:'), done)
})
it('should 400 for incomplete', function (done) {
@@ -93,16 +133,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/json')
.send('{"user"')
.expect(400, parseError('{"user"'), done)
})
it('should error with type = "entity.parse.failed"', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/json')
.set('X-Error-Property', 'type')
.send(' {"user"')
.expect(400, 'entity.parse.failed', done)
.expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done)
})
it('should include original body on error object', function (done) {
@@ -123,24 +154,13 @@ describe('express.json()', function () {
.set('Content-Type', 'application/json')
.set('Content-Length', '1034')
.send(JSON.stringify({ str: buf.toString() }))
.expect(413, done)
})
it('should error with type = "entity.too.large"', function (done) {
var buf = Buffer.alloc(1024, '.')
request(createApp({ limit: '1kb' }))
.post('/')
.set('Content-Type', 'application/json')
.set('Content-Length', '1034')
.set('X-Error-Property', 'type')
.send(JSON.stringify({ str: buf.toString() }))
.expect(413, 'entity.too.large', done)
.expect(413, '[entity.too.large] request entity too large', done)
})
it('should 413 when over limit with chunked encoding', function (done) {
var app = createApp({ limit: '1kb' })
var buf = Buffer.alloc(1024, '.')
var server = createApp({ limit: '1kb' })
var test = request(server).post('/')
var test = request(app).post('/')
test.set('Content-Type', 'application/json')
test.set('Transfer-Encoding', 'chunked')
test.write('{"str":')
@@ -148,6 +168,15 @@ describe('express.json()', function () {
test.expect(413, done)
})
it('should 413 when inflated body over limit', function (done) {
var app = createApp({ limit: '1kb' })
var test = request(app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex'))
test.expect(413, done)
})
it('should accept number of bytes', function (done) {
var buf = Buffer.alloc(1024, '.')
request(createApp({ limit: 1024 }))
@@ -160,11 +189,11 @@ describe('express.json()', function () {
it('should not change when options altered', function (done) {
var buf = Buffer.alloc(1024, '.')
var options = { limit: '1kb' }
var server = createApp(options)
var app = createApp(options)
options.limit = '100kb'
request(server)
request(app)
.post('/')
.set('Content-Type', 'application/json')
.send(JSON.stringify({ str: buf.toString() }))
@@ -173,14 +202,23 @@ describe('express.json()', function () {
it('should not hang response', function (done) {
var buf = Buffer.alloc(10240, '.')
var server = createApp({ limit: '8kb' })
var test = request(server).post('/')
var app = createApp({ limit: '8kb' })
var test = request(app).post('/')
test.set('Content-Type', 'application/json')
test.write(buf)
test.write(buf)
test.write(buf)
test.expect(413, done)
})
it('should not error when inflating', function (done) {
var app = createApp({ limit: '1kb' })
var test = request(app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex'))
test.expect(413, done)
})
})
describe('with inflate option', function () {
@@ -194,7 +232,7 @@ describe('express.json()', function () {
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
test.expect(415, 'content encoding unsupported', done)
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
})
})
@@ -224,7 +262,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/json')
.send('true')
.expect(400, parseError('#rue').replace('#', 't'), done)
.expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
})
})
@@ -252,7 +290,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/json')
.send('true')
.expect(400, parseError('#rue').replace('#', 't'), done)
.expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
})
it('should not parse primitives with leading whitespaces', function (done) {
@@ -260,7 +298,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/json')
.send(' true')
.expect(400, parseError(' #rue').replace('#', 't'), done)
.expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done)
})
it('should allow leading whitespaces in JSON', function (done) {
@@ -271,15 +309,6 @@ describe('express.json()', function () {
.expect(200, '{"user":"tobi"}', done)
})
it('should error with type = "entity.parse.failed"', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/json')
.set('X-Error-Property', 'type')
.send('true')
.expect(400, 'entity.parse.failed', done)
})
it('should include correct message in stack trace', function (done) {
request(this.app)
.post('/')
@@ -287,7 +316,7 @@ describe('express.json()', function () {
.set('X-Error-Property', 'stack')
.send('true')
.expect(400)
.expect(shouldContainInBody(parseError('#rue').replace('#', 't')))
.expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't')))
.end(done)
})
})
@@ -396,65 +425,59 @@ describe('express.json()', function () {
})
it('should error from verify', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
}
})
request(app)
.post('/')
.set('Content-Type', 'application/json')
.send('["tobi"]')
.expect(403, 'no arrays', done)
})
it('should error with type = "entity.verify.failed"', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
} })
request(app)
.post('/')
.set('Content-Type', 'application/json')
.set('X-Error-Property', 'type')
.send('["tobi"]')
.expect(403, 'entity.verify.failed', done)
.expect(403, '[entity.verify.failed] no arrays', done)
})
it('should allow custom codes', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] !== 0x5b) return
var err = new Error('no arrays')
err.status = 400
throw err
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x5b) return
var err = new Error('no arrays')
err.status = 400
throw err
}
})
request(app)
.post('/')
.set('Content-Type', 'application/json')
.send('["tobi"]')
.expect(400, 'no arrays', done)
.expect(400, '[entity.verify.failed] no arrays', done)
})
it('should allow custom type', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] !== 0x5b) return
var err = new Error('no arrays')
err.type = 'foo.bar'
throw err
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x5b) return
var err = new Error('no arrays')
err.type = 'foo.bar'
throw err
}
})
request(app)
.post('/')
.set('Content-Type', 'application/json')
.set('X-Error-Property', 'type')
.send('["tobi"]')
.expect(403, 'foo.bar', done)
.expect(403, '[foo.bar] no arrays', done)
})
it('should include original body on error object', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
}
})
request(app)
.post('/')
@@ -465,9 +488,11 @@ describe('express.json()', function () {
})
it('should allow pass-through', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
}
})
request(app)
.post('/')
@@ -477,9 +502,11 @@ describe('express.json()', function () {
})
it('should work with different charsets', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
}
})
var test = request(app).post('/')
test.set('Content-Type', 'application/json; charset=utf-16')
@@ -488,14 +515,120 @@ describe('express.json()', function () {
})
it('should 415 on unknown charset prior to verify', function (done) {
var app = createApp({ verify: function (req, res, buf) {
throw new Error('unexpected verify call')
} })
var app = createApp({
verify: function (req, res, buf) {
throw new Error('unexpected verify call')
}
})
var test = request(app).post('/')
test.set('Content-Type', 'application/json; charset=x-bogus')
test.write(Buffer.from('00000000', 'hex'))
test.expect(415, 'unsupported charset "X-BOGUS"', done)
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
})
})
describeAsyncHooks('async local storage', function () {
before(function () {
var app = express()
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
app.use(express.json())
app.use(function (req, res, next) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
next()
})
app.use(function (err, req, res, next) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
res.status(err.status || 500)
res.send('[' + err.type + '] ' + err.message)
})
app.post('/', function (req, res) {
res.json(req.body)
})
this.app = app
})
it('should presist store', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/json')
.send('{"user":"tobi"}')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{"user":"tobi"}')
.end(done)
})
it('should presist store when unmatched content-type', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/fizzbuzz')
.send('buzz')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{}')
.end(done)
})
it('should presist store when inflated', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
test.expect(200)
test.expect('x-store-foo', 'bar')
test.expect('{"name":"论"}')
test.end(done)
})
it('should presist store when inflate error', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
test.expect(400)
test.expect('x-store-foo', 'bar')
test.end(done)
})
it('should presist store when parse error', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/json')
.send('{"user":')
.expect(400)
.expect('x-store-foo', 'bar')
.end(done)
})
it('should presist store when limit exceeded', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/json')
.send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}')
.expect(413)
.expect('x-store-foo', 'bar')
.end(done)
})
})
@@ -537,15 +670,7 @@ describe('express.json()', function () {
var test = request(this.app).post('/')
test.set('Content-Type', 'application/json; charset=koi8-r')
test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
test.expect(415, 'unsupported charset "KOI8-R"', done)
})
it('should error with type = "charset.unsupported"', function (done) {
var test = request(this.app).post('/')
test.set('Content-Type', 'application/json; charset=koi8-r')
test.set('X-Error-Property', 'type')
test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
test.expect(415, 'charset.unsupported', done)
test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
})
})
@@ -598,16 +723,7 @@ describe('express.json()', function () {
test.set('Content-Encoding', 'nulls')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('000000000000', 'hex'))
test.expect(415, 'unsupported content encoding "nulls"', done)
})
it('should error with type = "encoding.unsupported"', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'nulls')
test.set('Content-Type', 'application/json')
test.set('X-Error-Property', 'type')
test.write(Buffer.from('000000000000', 'hex'))
test.expect(415, 'encoding.unsupported', done)
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
})
it('should 400 on malformed encoding', function (done) {
@@ -638,7 +754,9 @@ function createApp (options) {
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send(String(err[req.headers['x-error-property'] || 'message']))
res.send(String(req.headers['x-error-property']
? err[req.headers['x-error-property']]
: ('[' + err.type + '] ' + err.message)))
})
app.post('/', function (req, res) {
@@ -662,3 +780,11 @@ function shouldContainInBody (str) {
'expected \'' + res.text + '\' to contain \'' + str + '\'')
}
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@@ -1,9 +1,15 @@
'use strict'
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var express = require('..')
var request = require('supertest')
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('express.raw()', function () {
before(function () {
this.app = createApp()
@@ -59,6 +65,36 @@ describe('express.raw()', function () {
.expect(200, { buf: '' }, done)
})
it('should 500 if stream not readable', function (done) {
var app = express()
app.use(function (req, res, next) {
req.on('end', next)
req.resume()
})
app.use(express.raw())
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send('[' + err.type + '] ' + err.message)
})
app.post('/', function (req, res) {
if (Buffer.isBuffer(req.body)) {
res.json({ buf: req.body.toString('hex') })
} else {
res.json(req.body)
}
})
request(app)
.post('/')
.set('Content-Type', 'application/octet-stream')
.send('the user is tobi')
.expect(500, '[stream.not.readable] stream is not readable', done)
})
it('should handle duplicated middleware', function (done) {
var app = express()
@@ -101,6 +137,15 @@ describe('express.raw()', function () {
test.expect(413, done)
})
it('should 413 when inflated body over limit', function (done) {
var app = createApp({ limit: '1kb' })
var test = request(app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))
test.expect(413, done)
})
it('should accept number of bytes', function (done) {
var buf = Buffer.alloc(1028, '.')
var app = createApp({ limit: 1024 })
@@ -133,6 +178,15 @@ describe('express.raw()', function () {
test.write(buf)
test.expect(413, done)
})
it('should not error when inflating', function (done) {
var app = createApp({ limit: '1kb' })
var test = request(app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex'))
test.expect(413, done)
})
})
describe('with inflate option', function () {
@@ -146,7 +200,7 @@ describe('express.raw()', function () {
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
test.expect(415, 'content encoding unsupported', done)
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
})
})
@@ -262,34 +316,40 @@ describe('express.raw()', function () {
})
it('should error from verify', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x00) throw new Error('no leading null')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x00) throw new Error('no leading null')
}
})
var test = request(app).post('/')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('000102', 'hex'))
test.expect(403, 'no leading null', done)
test.expect(403, '[entity.verify.failed] no leading null', done)
})
it('should allow custom codes', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] !== 0x00) return
var err = new Error('no leading null')
err.status = 400
throw err
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x00) return
var err = new Error('no leading null')
err.status = 400
throw err
}
})
var test = request(app).post('/')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('000102', 'hex'))
test.expect(400, 'no leading null', done)
test.expect(400, '[entity.verify.failed] no leading null', done)
})
it('should allow pass-through', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x00) throw new Error('no leading null')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x00) throw new Error('no leading null')
}
})
var test = request(app).post('/')
test.set('Content-Type', 'application/octet-stream')
@@ -298,6 +358,104 @@ describe('express.raw()', function () {
})
})
describeAsyncHooks('async local storage', function () {
before(function () {
var app = express()
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
app.use(express.raw())
app.use(function (req, res, next) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
next()
})
app.use(function (err, req, res, next) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
res.status(err.status || 500)
res.send('[' + err.type + '] ' + err.message)
})
app.post('/', function (req, res) {
if (Buffer.isBuffer(req.body)) {
res.json({ buf: req.body.toString('hex') })
} else {
res.json(req.body)
}
})
this.app = app
})
it('should presist store', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/octet-stream')
.send('the user is tobi')
.expect(200)
.expect('x-store-foo', 'bar')
.expect({ buf: '746865207573657220697320746f6269' })
.end(done)
})
it('should presist store when unmatched content-type', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/fizzbuzz')
.send('buzz')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{}')
.end(done)
})
it('should presist store when inflated', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
test.expect(200)
test.expect('x-store-foo', 'bar')
test.expect({ buf: '6e616d653de8aeba' })
test.end(done)
})
it('should presist store when inflate error', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))
test.expect(400)
test.expect('x-store-foo', 'bar')
test.end(done)
})
it('should presist store when limit exceeded', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/octet-stream')
.send('the user is ' + Buffer.alloc(1024 * 100, '.').toString())
.expect(413)
.expect('x-store-foo', 'bar')
.end(done)
})
})
describe('charset', function () {
before(function () {
this.app = createApp()
@@ -355,12 +513,12 @@ describe('express.raw()', function () {
test.expect(200, { buf: '6e616d653de8aeba' }, done)
})
it('should fail on unknown encoding', function (done) {
it('should 415 on unknown encoding', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'nulls')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('000000000000', 'hex'))
test.expect(415, 'unsupported content encoding "nulls"', done)
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
})
})
})
@@ -372,7 +530,9 @@ function createApp (options) {
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send(String(err[req.headers['x-error-property'] || 'message']))
res.send(String(req.headers['x-error-property']
? err[req.headers['x-error-property']]
: ('[' + err.type + '] ' + err.message)))
})
app.post('/', function (req, res) {
@@ -385,3 +545,11 @@ function createApp (options) {
return app
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@@ -1,3 +1,4 @@
'use strict'
var assert = require('assert')
var Buffer = require('safe-buffer').Buffer
@@ -485,7 +486,7 @@ describe('express.static()', function () {
request(this.app)
.get('/users')
.expect('Location', '/users/')
.expect(301, /<a href="\/users\/">/, done)
.expect(301, /\/users\//, done)
})
it('should redirect directories with query string', function (done) {
@@ -507,7 +508,7 @@ describe('express.static()', function () {
.get('/snow')
.expect('Location', '/snow%20%E2%98%83/')
.expect('Content-Type', /html/)
.expect(301, />Redirecting to <a href="\/snow%20%E2%98%83\/">\/snow%20%E2%98%83\/<\/a></, done)
.expect(301, />Redirecting to \/snow%20%E2%98%83\/</, done)
})
it('should respond with default Content-Security-Policy', function (done) {

View File

@@ -1,9 +1,15 @@
'use strict'
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var express = require('..')
var request = require('supertest')
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('express.text()', function () {
before(function () {
this.app = createApp()
@@ -55,6 +61,32 @@ describe('express.text()', function () {
.expect(200, '""', done)
})
it('should 500 if stream not readable', function (done) {
var app = express()
app.use(function (req, res, next) {
req.on('end', next)
req.resume()
})
app.use(express.text())
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send('[' + err.type + '] ' + err.message)
})
app.post('/', function (req, res) {
res.json(req.body)
})
request(app)
.post('/')
.set('Content-Type', 'text/plain')
.send('user is tobi')
.expect(500, '[stream.not.readable] stream is not readable', done)
})
it('should handle duplicated middleware', function (done) {
var app = express()
@@ -74,16 +106,16 @@ describe('express.text()', function () {
describe('with defaultCharset option', function () {
it('should change default charset', function (done) {
var app = createApp({ defaultCharset: 'koi8-r' })
var test = request(app).post('/')
var server = createApp({ defaultCharset: 'koi8-r' })
var test = request(server).post('/')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('6e616d6520697320cec5d4', 'hex'))
test.expect(200, '"name is нет"', done)
})
it('should honor content-type charset', function (done) {
var app = createApp({ defaultCharset: 'koi8-r' })
var test = request(app).post('/')
var server = createApp({ defaultCharset: 'koi8-r' })
var test = request(server).post('/')
test.set('Content-Type', 'text/plain; charset=utf-8')
test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))
test.expect(200, '"name is 论"', done)
@@ -102,8 +134,8 @@ describe('express.text()', function () {
})
it('should 413 when over limit with chunked encoding', function (done) {
var buf = Buffer.alloc(1028, '.')
var app = createApp({ limit: '1kb' })
var buf = Buffer.alloc(1028, '.')
var test = request(app).post('/')
test.set('Content-Type', 'text/plain')
test.set('Transfer-Encoding', 'chunked')
@@ -111,6 +143,15 @@ describe('express.text()', function () {
test.expect(413, done)
})
it('should 413 when inflated body over limit', function (done) {
var app = createApp({ limit: '1kb' })
var test = request(app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))
test.expect(413, done)
})
it('should accept number of bytes', function (done) {
var buf = Buffer.alloc(1028, '.')
request(createApp({ limit: 1024 }))
@@ -135,8 +176,8 @@ describe('express.text()', function () {
})
it('should not hang response', function (done) {
var buf = Buffer.alloc(10240, '.')
var app = createApp({ limit: '8kb' })
var buf = Buffer.alloc(10240, '.')
var test = request(app).post('/')
test.set('Content-Type', 'text/plain')
test.write(buf)
@@ -144,6 +185,17 @@ describe('express.text()', function () {
test.write(buf)
test.expect(413, done)
})
it('should not error when inflating', function (done) {
var app = createApp({ limit: '1kb' })
var test = request(app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a1470404', 'hex'))
setTimeout(function () {
test.expect(413, done)
}, 100)
})
})
describe('with inflate option', function () {
@@ -157,7 +209,7 @@ describe('express.text()', function () {
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))
test.expect(415, 'content encoding unsupported', done)
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
})
})
@@ -277,36 +329,42 @@ describe('express.text()', function () {
})
it('should error from verify', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error('no leading space')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error('no leading space')
}
})
request(app)
.post('/')
.set('Content-Type', 'text/plain')
.send(' user is tobi')
.expect(403, 'no leading space', done)
.expect(403, '[entity.verify.failed] no leading space', done)
})
it('should allow custom codes', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] !== 0x20) return
var err = new Error('no leading space')
err.status = 400
throw err
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x20) return
var err = new Error('no leading space')
err.status = 400
throw err
}
})
request(app)
.post('/')
.set('Content-Type', 'text/plain')
.send(' user is tobi')
.expect(400, 'no leading space', done)
.expect(400, '[entity.verify.failed] no leading space', done)
})
it('should allow pass-through', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error('no leading space')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error('no leading space')
}
})
request(app)
.post('/')
@@ -316,14 +374,110 @@ describe('express.text()', function () {
})
it('should 415 on unknown charset prior to verify', function (done) {
var app = createApp({ verify: function (req, res, buf) {
throw new Error('unexpected verify call')
} })
var app = createApp({
verify: function (req, res, buf) {
throw new Error('unexpected verify call')
}
})
var test = request(app).post('/')
test.set('Content-Type', 'text/plain; charset=x-bogus')
test.write(Buffer.from('00000000', 'hex'))
test.expect(415, 'unsupported charset "X-BOGUS"', done)
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
})
})
describeAsyncHooks('async local storage', function () {
before(function () {
var app = express()
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
app.use(express.text())
app.use(function (req, res, next) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
next()
})
app.use(function (err, req, res, next) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
res.status(err.status || 500)
res.send('[' + err.type + '] ' + err.message)
})
app.post('/', function (req, res) {
res.json(req.body)
})
this.app = app
})
it('should presist store', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'text/plain')
.send('user is tobi')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('"user is tobi"')
.end(done)
})
it('should presist store when unmatched content-type', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/fizzbuzz')
.send('buzz')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{}')
.end(done)
})
it('should presist store when inflated', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))
test.expect(200)
test.expect('x-store-foo', 'bar')
test.expect('"name is 论"')
test.end(done)
})
it('should presist store when inflate error', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000', 'hex'))
test.expect(400)
test.expect('x-store-foo', 'bar')
test.end(done)
})
it('should presist store when limit exceeded', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'text/plain')
.send('user is ' + Buffer.alloc(1024 * 100, '.').toString())
.expect(413)
.expect('x-store-foo', 'bar')
.end(done)
})
})
@@ -365,7 +519,7 @@ describe('express.text()', function () {
var test = request(this.app).post('/')
test.set('Content-Type', 'text/plain; charset=x-bogus')
test.write(Buffer.from('00000000', 'hex'))
test.expect(415, 'unsupported charset "X-BOGUS"', done)
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
})
})
@@ -413,12 +567,12 @@ describe('express.text()', function () {
test.expect(200, '"name is 论"', done)
})
it('should fail on unknown encoding', function (done) {
it('should 415 on unknown encoding', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'nulls')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('000000000000', 'hex'))
test.expect(415, 'unsupported content encoding "nulls"', done)
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
})
})
})
@@ -430,7 +584,9 @@ function createApp (options) {
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send(err.message)
res.send(String(req.headers['x-error-property']
? err[req.headers['x-error-property']]
: ('[' + err.type + '] ' + err.message)))
})
app.post('/', function (req, res) {
@@ -439,3 +595,11 @@ function createApp (options) {
return app
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@@ -1,9 +1,15 @@
'use strict'
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var express = require('..')
var request = require('supertest')
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('express.urlencoded()', function () {
before(function () {
this.app = createApp()
@@ -56,6 +62,32 @@ describe('express.urlencoded()', function () {
.expect(200, '{}', done)
})
it('should 500 if stream not readable', function (done) {
var app = express()
app.use(function (req, res, next) {
req.on('end', next)
req.resume()
})
app.use(express.urlencoded())
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send('[' + err.type + '] ' + err.message)
})
app.post('/', function (req, res) {
res.json(req.body)
})
request(app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('user=tobi')
.expect(500, '[stream.not.readable] stream is not readable', done)
})
it('should handle duplicated middleware', function (done) {
var app = express()
@@ -180,7 +212,7 @@ describe('express.urlencoded()', function () {
it('should parse deep object', function (done) {
var str = 'foo'
for (var i = 0; i < 500; i++) {
for (var i = 0; i < 32; i++) {
str += '[p]'
}
@@ -198,7 +230,7 @@ describe('express.urlencoded()', function () {
var depth = 0
var ref = obj.foo
while ((ref = ref.p)) { depth++ }
assert.strictEqual(depth, 500)
assert.strictEqual(depth, 32)
})
.expect(200, done)
})
@@ -216,7 +248,7 @@ describe('express.urlencoded()', function () {
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
test.expect(415, 'content encoding unsupported', done)
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
})
})
@@ -247,8 +279,8 @@ describe('express.urlencoded()', function () {
})
it('should 413 when over limit with chunked encoding', function (done) {
var buf = Buffer.alloc(1024, '.')
var app = createApp({ limit: '1kb' })
var buf = Buffer.alloc(1024, '.')
var test = request(app).post('/')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.set('Transfer-Encoding', 'chunked')
@@ -257,6 +289,15 @@ describe('express.urlencoded()', function () {
test.expect(413, done)
})
it('should 413 when inflated body over limit', function (done) {
var app = createApp({ limit: '1kb' })
var test = request(app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex'))
test.expect(413, done)
})
it('should accept number of bytes', function (done) {
var buf = Buffer.alloc(1024, '.')
request(createApp({ limit: 1024 }))
@@ -281,8 +322,8 @@ describe('express.urlencoded()', function () {
})
it('should not hang response', function (done) {
var buf = Buffer.alloc(10240, '.')
var app = createApp({ limit: '8kb' })
var buf = Buffer.alloc(10240, '.')
var test = request(app).post('/')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(buf)
@@ -290,6 +331,15 @@ describe('express.urlencoded()', function () {
test.write(buf)
test.expect(413, done)
})
it('should not error when inflating', function (done) {
var app = createApp({ limit: '1kb' })
var test = request(app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex'))
test.expect(413, done)
})
})
describe('with parameterLimit option', function () {
@@ -309,16 +359,7 @@ describe('express.urlencoded()', function () {
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(createManyParams(11))
.expect(413, /too many parameters/, done)
})
it('should error with type = "parameters.too.many"', function (done) {
request(createApp({ extended: false, parameterLimit: 10 }))
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('X-Error-Property', 'type')
.send(createManyParams(11))
.expect(413, 'parameters.too.many', done)
.expect(413, '[parameters.too.many] too many parameters', done)
})
it('should work when at the limit', function (done) {
@@ -373,16 +414,7 @@ describe('express.urlencoded()', function () {
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(createManyParams(11))
.expect(413, /too many parameters/, done)
})
it('should error with type = "parameters.too.many"', function (done) {
request(createApp({ extended: true, parameterLimit: 10 }))
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('X-Error-Property', 'type')
.send(createManyParams(11))
.expect(413, 'parameters.too.many', done)
.expect(413, '[parameters.too.many] too many parameters', done)
})
it('should work when at the limit', function (done) {
@@ -525,65 +557,59 @@ describe('express.urlencoded()', function () {
})
it('should error from verify', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error('no leading space')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error('no leading space')
}
})
request(app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(' user=tobi')
.expect(403, 'no leading space', done)
})
it('should error with type = "entity.verify.failed"', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error('no leading space')
} })
request(app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('X-Error-Property', 'type')
.send(' user=tobi')
.expect(403, 'entity.verify.failed', done)
.expect(403, '[entity.verify.failed] no leading space', done)
})
it('should allow custom codes', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] !== 0x20) return
var err = new Error('no leading space')
err.status = 400
throw err
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x20) return
var err = new Error('no leading space')
err.status = 400
throw err
}
})
request(app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(' user=tobi')
.expect(400, 'no leading space', done)
.expect(400, '[entity.verify.failed] no leading space', done)
})
it('should allow custom type', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] !== 0x20) return
var err = new Error('no leading space')
err.type = 'foo.bar'
throw err
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x20) return
var err = new Error('no leading space')
err.type = 'foo.bar'
throw err
}
})
request(app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('X-Error-Property', 'type')
.send(' user=tobi')
.expect(403, 'foo.bar', done)
.expect(403, '[foo.bar] no leading space', done)
})
it('should allow pass-through', function (done) {
var app = createApp({ verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
} })
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error('no arrays')
}
})
request(app)
.post('/')
@@ -593,14 +619,110 @@ describe('express.urlencoded()', function () {
})
it('should 415 on unknown charset prior to verify', function (done) {
var app = createApp({ verify: function (req, res, buf) {
throw new Error('unexpected verify call')
} })
var app = createApp({
verify: function (req, res, buf) {
throw new Error('unexpected verify call')
}
})
var test = request(app).post('/')
test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus')
test.write(Buffer.from('00000000', 'hex'))
test.expect(415, 'unsupported charset "X-BOGUS"', done)
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
})
})
describeAsyncHooks('async local storage', function () {
before(function () {
var app = express()
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
app.use(express.urlencoded())
app.use(function (req, res, next) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
next()
})
app.use(function (err, req, res, next) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
res.status(err.status || 500)
res.send('[' + err.type + '] ' + err.message)
})
app.post('/', function (req, res) {
res.json(req.body)
})
this.app = app
})
it('should presist store', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('user=tobi')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{"user":"tobi"}')
.end(done)
})
it('should presist store when unmatched content-type', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/fizzbuzz')
.send('buzz')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{}')
.end(done)
})
it('should presist store when inflated', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
test.expect(200)
test.expect('x-store-foo', 'bar')
test.expect('{"name":"论"}')
test.end(done)
})
it('should presist store when inflate error', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))
test.expect(400)
test.expect('x-store-foo', 'bar')
test.end(done)
})
it('should presist store when limit exceeded', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('user=' + Buffer.alloc(1024 * 100, '.').toString())
.expect(413)
.expect('x-store-foo', 'bar')
.end(done)
})
})
@@ -635,7 +757,7 @@ describe('express.urlencoded()', function () {
var test = request(this.app).post('/')
test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r')
test.write(Buffer.from('6e616d653dcec5d4', 'hex'))
test.expect(415, 'unsupported charset "KOI8-R"', done)
test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
})
})
@@ -683,12 +805,12 @@ describe('express.urlencoded()', function () {
test.expect(200, '{"name":"论"}', done)
})
it('should fail on unknown encoding', function (done) {
it('should 415 on unknown encoding', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'nulls')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('000000000000', 'hex'))
test.expect(415, 'unsupported content encoding "nulls"', done)
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
})
})
})
@@ -717,7 +839,9 @@ function createApp (options) {
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send(String(err[req.headers['x-error-property'] || 'message']))
res.send(String(req.headers['x-error-property']
? err[req.headers['x-error-property']]
: ('[' + err.type + '] ' + err.message)))
})
app.post('/', function (req, res) {
@@ -732,3 +856,11 @@ function expectKeyCount (count) {
assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count)
}
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

Some files were not shown because too many files have changed in this diff Show More