Compare commits

...

179 Commits

Author SHA1 Message Date
Andrea Polverino
caa4f68ee8
feat: Extend res.links() to allow adding multiple links with the same rel (closes #2729) (#4885)
Some checks failed
ci / Lint (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (18, ubuntu-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (18, windows-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (19, ubuntu-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (19, windows-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (20, ubuntu-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (20, windows-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (21, ubuntu-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (21, windows-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (22, ubuntu-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (22, windows-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (23, ubuntu-latest) (push) Has been cancelled
ci / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (23, windows-latest) (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
legacy / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (16, ubuntu-latest) (push) Has been cancelled
legacy / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (16, windows-latest) (push) Has been cancelled
legacy / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (17, ubuntu-latest) (push) Has been cancelled
legacy / Node.js ${{ matrix.node-version }} - ${{matrix.os}} (17, windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
ci / coverage (push) Has been cancelled
legacy / coverage (push) Has been cancelled
2025-02-14 10:20:53 -06:00
Phillip Barta
6ed3439584
fix(docs): Update multiple links to use https instead of http (#6338) 2025-02-14 09:51:27 -06:00
Juan José
327af123a1
feat: add support for ETag option in res.sendFile (#6073)
This patch introduces the ability to control the ETag generation
through the `res.sendFile` function. Specifically, the ETag option
is wired to the application's configuration, allowing it to be
enabled or disabled based on the app's settings.

Fixes: https://github.com/expressjs/express/issues/2294

Signed-off-by: Juan José Arboleda <soyjuanarbol@gmail.com>
2025-02-13 13:39:31 -06:00
Wes Todd
d2de128a32
fix (deps): update deps (#6337)
fix(deps): mocha@^10.7.3 (closes #6121)

fix(deps): marked@^15.0.3 (closes #6120)

fix(deps): express-session@^1.18.1 (closes #6119)

fix(deps): ejs@^3.1.10 (closes #6117)

fix(deps): content-type@^1.0.5 (closes #6115)

fix(deps): connect-redis@^8.0.1 (closes #6114)

fix(deps): supertest@^6.3.4 (closes #6112)

Co-authored-by: agungjati <agungjati94@gmail.com>
2025-02-13 10:44:50 -06:00
Agung Jati
2a53336e5d
fix(deps): nyc@^17.1.0 (#6122) 2025-02-12 10:47:19 -06:00
Ulises Gascón
a42413d4e3
fix(docs): Update repo captains (#6234)
* docs: update repo captains

* docs: update repo captain nomination policies

Ref: https://github.com/expressjs/express/pull/6234#issuecomment-2578555232
2025-02-12 10:22:11 -06:00
Wes Todd
c2f576cbe9
feat(deps): router@^2.1.0 (#6331) 2025-02-12 10:09:50 -06:00
Wes Todd
99473c593a
feat(deps): body-parser@^2.1.0 (#6332) 2025-02-12 09:44:53 -06:00
Dustin Popp
2d589b644a
fix(docs): retroactively note 5.0.0-beta.1 api change in history file (#6333)
Signed-off-by: Dustin Popp <dustinpopp@ibm.com>
2025-02-12 09:38:10 -06:00
Phillip Barta
85e48bb8c1
fix(deps): update debug to ^4.4.0 (#6313) 2025-02-10 13:41:39 -06:00
Alexander Cerutti
55869f49a6 feat: Added check to support Uint8Array in response sending (#6285)
Unified usage of ArrayBuffer.isView to comprehend Buffer and removed isView function check

Co-authored-by: Wes Todd <wes@wesleytodd.com>

Added Uint8Array test with encoding

fix: added history.md entry
2025-02-10 11:23:02 -06:00
Dustin Popp
af7cd90893
feat(deps): use carat notation for dependency versions (#6317)
Signed-off-by: Dustin Popp <dustinpopp@ibm.com>
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2025-02-05 12:05:39 -06:00
Phillip Barta
ae6a4621bc
fix(ci): updated scorecard actions (#6322) 2025-02-05 11:40:08 -06:00
Phillip Barta
8d39345902
fix(ci): fix npm install --include typo (#6324) 2025-02-05 11:25:48 -06:00
Phillip Barta
a5cb681eb8
ci: updated github actions ci workflow (#6314) 2025-02-05 13:27:59 +01:00
Ayoub Mabrouk
511d9dfca8
refactor: simplify normalizeTypes function (#6097) 2025-02-04 11:59:48 +01:00
Ulises Gascón
7f13d572c1
docs: include team email in the security policy (#6278) 2025-02-04 11:28:18 +01:00
prajesh
62336717bf
fix: added a missing semicolon in css styles in examples/auth (#6297) 2025-01-26 11:54:07 +01:00
Sebastian Beltran
3bbffdc41c docs: add @Phillip9587 to the triage team 2025-01-23 09:51:45 -06:00
Ulises Gascón
ff86319ed5
ci: add support for OSSF scorecard reporting (#5431) 2025-01-15 07:39:30 +01:00
Hamir Mahal
1c5cf0fead refactor: remove Invalid action input 2025-01-14 13:01:39 -06:00
Jon Koops
256a3d1527 Remove unused depd dependency
Signed-off-by: Jon Koops <jonkoops@gmail.com>
2025-01-12 11:08:51 -06:00
Wes Todd
4f952a953b fix: remove download size badges 2025-01-11 11:12:04 -06:00
Szymon Łągiewka
41113599af fix(refactor): prefix built-in node module imports
Since v5 relies on node >= 18, this is now possible (since v16, v14.18.0
[^1][^2]).

It's functionally irrelevant:
1. It's not required for CJS nor ESM (with a few exceptions [^3])
2. It has no performance promises

However, there are upsides to this approach:
1. It brings clear boundaries to what's a built-in and what's an
external dependency
2. It reduces the risk of importing unwanted deps where a built-in is
expected
3. It's slightly more interoperable with other JS runtimes that provide
node compatibility[^4], albeit only during development. Once imported
from npm, built-ins are assumed.

[^1]:https://nodejs.org/docs/latest-v22.x/api/modules.html#built-in-modules
[^2]:https://github.com/nodejs/node/pull/37246
[^3]:https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
[^4]:https://docs.deno.com/runtime/fundamentals/node/#using-node's-built-in-modules
2025-01-10 11:53:44 -06:00
AbdelMonaam Aouini
6a40af8293
fix(devdeps): update dev deps (#6211)
Co-authored-by: Monaam Aouini <abdelmonaem.aouini@mispay.co>
2025-01-08 14:45:36 -06:00
Phillip Barta
246f6f5aee
fix: Remove utils-merge dependency - use spread syntax instead (#6091) 2025-01-08 09:56:16 -06:00
Jon Koops
b11122be85
chore: replace methods dependency with standard library (#6196) 2025-01-02 08:00:30 +01:00
Sebastian Beltran
43020ff275
docs: clarify the security process in the triage role (#6217) 2024-12-20 18:18:55 +01:00
Shahan Arshad
e4a61bd88e
refactor: improve readability (#6173) 2024-11-27 21:22:22 +01:00
Ulises Gascón
39f5d633b5
docs: add @rxmarbles to the triage team (#6151) 2024-11-27 19:59:36 +01:00
Jon Church
52ed64606f
update history.md for acceptParams change (#6177) 2024-11-20 14:40:39 -05:00
Phillip Barta
4e92ac9031
cleanup: remove AsyncLocalStorage check from tests (#6147)
Co-authored-by: Wes Todd <wes@wesleytodd.com>
2024-11-15 10:23:42 -06:00
Phillip9587
9f8589e31c cleanup: remove unnecessary require for global Buffer
The Buffer object is globally available in Node.js, so there is no need to explicitly require it.
2024-11-15 10:16:29 -06:00
Sebastian Beltran
cc751cff8f improve step update documentation 2024-11-15 08:53:37 -06:00
Blake Embrey
805ef52ae6
Use loop for acceptParams (#6066) 2024-11-14 16:01:25 -05:00
Phillip Barta
9e3dbb4374
chore(test): remove promise support check from tests (#6148)
Promises are supported in all supported Node.js version so the check is unnecessary
2024-11-12 09:30:34 -06:00
Ulises Gascón
b31910c542
docs: Add DCO (#6048)
Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-10-29 13:35:44 +01:00
Bhavya Dhiman
c70197ad33
fix(buffer): use node:buffer instead of safe-buffer (#6071)
Main Changes:
- Removed dependency `safe-buffer@5.2.1`
- Use `node:buffer` core library instead of safe-buffer
2024-10-27 11:10:33 +01:00
Phillip Barta
8cb53ea5c3
refactor: Remove Object.setPrototypeOf polyfill (#6081) 2024-10-22 20:22:26 +02:00
Rand McKinney
e162764f0f
docs: add bjohansebas as repo captain for expressjs.com (#6058) 2024-10-20 20:10:23 +02:00
Sebastian Beltran
508c74091f
docs: update readme (#5994) 2024-10-20 20:09:00 +02:00
Sebastian Beltran
b274047a5d
docs: update homepage link http to https (#5920)
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2024-10-20 20:07:22 +02:00
Chris de Almeida
082d6d1253
test: add discarded middleware test (#5819) 2024-10-20 20:06:49 +02:00
Sebastian Beltran
94546a3cc5
docs: add funding (#6064) 2024-10-20 19:58:49 +02:00
Sebastian Beltran
ab02240336
build: Node.js 23.0 (#6075) 2024-10-20 19:25:56 +02:00
Ulises Gascón
a46cfdc37f
docs: update captains
PR-URL: https://github.com/expressjs/express/pull/6027
2024-10-09 21:40:05 +02:00
Ulises Gascón
d14b2de782
5.0.1
PR-URL: https://github.com/expressjs/express/pull/6032
2024-10-08 21:31:10 +02:00
Josh Buker
2027b87a27
fix(deps): cookie@0.7.0
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2024-10-08 12:10:56 +02:00
Blake Embrey
2cbf22721d
Link and update captains (#6013) 2024-10-06 14:06:57 -07:00
Ulises Gascón
3e1a1cedb2
Add @bjohansebas to the triage team (#6009)
Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2024-10-06 17:34:26 +02:00
Jon Church
6340d1509f
remove --bail from test script (#5962) 2024-09-30 16:49:26 -04:00
Wes Todd
344b022fc7 5.0.0
closes #2237
closes https://github.com/expressjs/discussions/issues/233
2024-09-09 23:37:22 -05:00
Wes Todd
0c49926a9b fix(deps): send@^1.1.0 2024-09-09 23:34:03 -05:00
Wes Todd
b3906cbdde fix(deps): serve-static@^2.1.0 2024-09-09 23:32:19 -05:00
Wes Todd
fed8c2a885 fix(deps): body-parser@^2.0.1 2024-09-09 23:27:40 -05:00
Blake Embrey
bdd81f8670
Delete back as a magic string (#5933) 2024-09-09 20:28:55 -07:00
ctcpip
6c98f80b6a 🔧 update CI, remove unsupported versions, clean up 2024-09-09 21:49:35 -05:00
Wes Todd
f9256ef36f
Merge branch '5.0' into 5-merge 2024-09-09 21:11:23 -05:00
Wes Todd
e5feb9fcc9 Merge tag '4.20.0' into 5.0 2024-09-09 21:07:57 -05: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
Wes Todd
0264908903
feat(deps)!: router@^2.0.0 (#5885) 2024-09-09 17:50:11 -05:00
Jon Church
4d713d2b76
update to fresh@2.0.0 (#5916)
fixes handling of If-Modified-Since in combination with If-None-Match
2024-09-09 17:03:32 -05: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
Wes Todd
accafc652e
fix(deps): finalhandler@^2.0.0 (#5899) 2024-09-02 13:36:21 -05:00
Wes Todd
05f40f4321
fix(deps)!: content-disposition@^1.0.0 (#5884) 2024-08-31 13:09:21 -05:00
Wes Todd
402e7f653f
fix(deps): type-is@^2.0.0 (#5883) 2024-08-31 12:31:31 -05:00
Wes Todd
4e61d0100d
fix(deps)!: mime-types@^3.0.0 (#5882) 2024-08-31 11:06:25 -05:00
Wes Todd
7748475747
fix(deps): accepts@^2.0.0 (#5881) 2024-08-31 10:55:04 -05:00
S M Mahmudul Hasan
91a58b5b03
cookie-signature@^1.2.1 (#5833)
* upgraded `cookie-signature` to 1.2.1

* declared cookie-signature deps in history

* add caret in version
2024-08-23 18:17:12 -05:00
Carlos Serrano
13e6894393
chore: qs@6.13.0 (#5847)
Co-authored-by: Wes Todd <wtodd@netflix.com>
2024-08-23 14:10:16 -07:00
Wes Todd
65b62065d2
fix(deps) serve-staic@2.0.0 (#5790) 2024-08-23 14:07:45 -07:00
Anna Bocharova
0b243b1aee
5.x: Upgrading merge-descriptors with allowing minors (#5782)
* Upgrading `merge-descriptors` with allowing minors in v5

* Using ^2.0.0 as per request

* Reflecting in History.md.

* Update History.md
2024-08-23 13:37:27 -07:00
Carlos Serrano
09831580ec
refactor: replace 'path-is-absolute' dep with node:path isAbsolute method (#5830)
* refactor: replace 'path-is-absolute' dep with node:path isAbsolute method

* docs: add path-is-absolute dep removal to History.md
2024-08-17 10:21:29 -05:00
Carlos Serrano
41c054cff1
chore: upgrade debug dep from 3.10 to 4.3.6 (#5829)
* chore: upgrade debug dep from 3.10 to 4.3.6

* docs: add debug dep upgrade to History.md
2024-08-17 10:20:25 -05:00
Wes Todd
ecf762ff38
fix(deps)!: send@^1.0.0 (#5786) 2024-08-09 09:59:53 -07:00
ctcpip
63992bb1d7 Merge branch 'ci/v5-node-lts' into 5-merge 2024-08-02 16:24:55 -05:00
ctcpip
ea49706052 Merge branch 'master' into 5-merge 2024-08-02 15:57:43 -05:00
ctcpip
dde1f7d6e8 Merge branch '5.0' into 5-merge 2024-08-02 15:38:18 -05:00
Jon Church
82fc12a40b
Ignore expires and maxAge in res.clearCookie() (#5792)
* add test for removing user provided expires

* rework impl and tests to ignore maxAge, do not set it

this is to take into account the built-in relative expires when passing
a maxAge to res.cookie

I realized that using maxAge to invalidate cookies inherrently hit this
relativee expires behavior, and the goal of this PR is not to rework
that relative expires behavior w/ maxAge, but to prevent users from
overwriting these values by accident when clearing cookies

* update history.md

* explicitly delete maxAge instead of setting as undefined

* drop the spread, use object.assign

* wording, review comment on history.md

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

* ♻️ use spread, update supported ecmascript version

---------

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-08-02 15:26:45 -05:00
ctcpip
9c756b0105 💚 remove node <11, all failing permanently now 2024-08-02 13:15:51 -05:00
Ulises Gascón
160b91cbf7
feat: adopt Node@18 as the minimum supported version (#5803)
- PR-URL: https://github.com/expressjs/express/pull/5803
- This is a BREAKING CHANGE
2024-08-02 16:07:36 +02:00
Mick A.
d106bf5324
Use Array.flat instead of array-flatten (#5677) 2024-08-01 19:42:07 -04:00
Jon Church
723b5451bb
Throw on invalid status codes (#4212)
* check status code is integer, or string integer, in range

* fix tests, update jsdoc comment for res.status

* throw if number is string

* narrow valid range to between 1xx and 5xx

* disambiguate the error message

* update skipped tests, remove invalid string test

* remove invalid float test

* fixup! remove invalid float test

* fix invalid range tests error assertions

* remove unused deprecate function

* add test to assert on 200.00 coming through as 200

this is the behavior of node's underlying HTTP module

* revert back to throwing only on > 999 and < 100

* update implementation for > 999

* add test for 700 status code

* update history with change

* update jsdoc

* clarify jsdoc comment

* one more round of jsdoc

* update 501 test

* add invalid status code test for res.sendStatus

* add test describe block for valid range

* fixup! add test describe block for valid range

* reduce the describe nesting

* switch to testing status 100, to avoid 100-continue behavior

* fix 900 test

* stringify code in thrown RangeError message

* remove accidentally duplicated res.status method

* fix error range message

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

* update sendStatus invalid code test to use sendStatus

---------

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-07-30 14:49:13 -07:00
ctcpip
c96c690dc0 Merge branch 'master' into 5.0 2024-07-25 16:55:48 -05:00
ctcpip
088856c3f8 💚 add legacy CI, clean up 2024-07-25 12:39:38 -05:00
Wes
ee40a881f5 call callback once on listen error 2024-05-17 13:47:56 -07:00
Evan Hahn
26801a0afd Use object with null prototype for settings closes #4835 2024-04-29 10:12:23 -05:00
Evan Hahn
14439731f9 Use object with null prototype for various app properties
`app.cache`, `app.engines`, and `app.settings` are now created with
`Object.create(null)` instead of `{}`.

This also updates a test to ensure that `app.locals` is created the same
way.
2024-04-29 09:43:25 -05:00
Ulises Gascón
4b3b8cc231
feat: adopt Node@18 as the minimum supported version 2024-04-11 19:19:47 +02:00
Ulises Gascón
e9bcdd399b
ci: adopt Node@18 as the minimum supported version 2024-04-11 19:16:20 +02:00
Wes Todd
cd7d79f92a v5.0.0-beta.3 2024-03-25 09:41:30 -05:00
Wes Todd
5e2345e966 Merge branch '5.0' into 5.x 2024-03-25 09:38:58 -05:00
Wes Todd
6415f7035b Merge tag '4.19.2' into 5.0
4.19.2
2024-03-25 09:37:02 -05:00
Wes Todd
04bc62787b 4.19.2 2024-03-25 09:26:03 -05:00
Wes Todd
da4d763ff6 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:22:34 -05:00
Wes Todd
7091ec17f0 5.0.0-beta.2 2024-03-20 22:00:19 -05:00
Wes Todd
416ba025a1 build: disable test for 4.x which is failing in v5 2024-03-20 21:52:27 -05:00
Wes Todd
60fb1d2acd Merge tag '4.19.1' into 5.x
4.19.1
2024-03-20 21:17:26 -05:00
Wes Todd
e9f9aaeebd Merge tag '4.19.0' into 5.x
4.19.0
2024-03-20 21:14:00 -05:00
Douglas Christopher Wilson
318fd4b543 Merge tag '4.17.3' 2022-02-17 00:27:11 -05:00
Douglas Christopher Wilson
6faf26d59f 5.0.0-beta.1 2022-02-14 19:13:14 -05:00
Douglas Christopher Wilson
5213bd9fe7 docs: fix entry in history 2022-02-08 18:55:09 -05:00
Douglas Christopher Wilson
669c805615 deps: send@1.0.0-beta.1 2022-02-08 09:57:29 -05:00
Douglas Christopher Wilson
620df0e35e deps: serve-static@2.0.0-beta.1 2022-02-08 09:55:19 -05:00
Douglas Christopher Wilson
f6db4ee805 Drop support for Node.js below 4 2022-02-08 09:48:54 -05:00
Douglas Christopher Wilson
a0276c6c91 Use mime-types for file to content type mapping 2022-02-06 10:26:18 -05:00
Douglas Christopher Wilson
3d05e85b0c deps: array-flatten@3.0.0 2022-02-01 21:51:28 -05:00
Czarek
450c468d04 Change query parser setting default to 'simple'
closes #3361
closes #3621
2021-12-17 23:15:29 -05:00
Douglas Christopher Wilson
af341b0f09 deps: body-parser@2.0.0-beta.1 2021-12-17 23:02:38 -05:00
Douglas Christopher Wilson
1574925cad deps: router@2.0.0-beta.1
closes #4321
2021-12-16 23:05:41 -05:00
Douglas Christopher Wilson
c7d528cdc0 Merge tag '4.17.2' 2021-12-16 23:01:28 -05:00
Alexander Belov
8aabecaf1f docs: fix typo in history
closes #4286
closes #4327
2020-06-29 00:29:30 -04:00
Douglas Christopher Wilson
bd04d8a87f 5.0.0-alpha.8 2020-03-25 20:14:47 -04:00
Douglas Christopher Wilson
121fe9982b Merge tag '4.17.1' 2019-06-08 19:43:21 -04:00
Douglas Christopher Wilson
5f0c829d7c 5.0.0-alpha.7 2018-10-26 22:29:15 -04:00
Douglas Christopher Wilson
c82fa19447 tests: add router promise tests 2018-10-26 21:31:28 -04:00
Douglas Christopher Wilson
fa22245cc6 deps: router@2.0.0-alpha.1 2018-10-23 21:02:44 -04:00
Douglas Christopher Wilson
302a6152b4 Merge tag '4.16.4' 2018-10-23 20:38:42 -04:00
Douglas Christopher Wilson
659fcc1598 deps: router@~1.3.2 2017-10-13 22:59:21 -04:00
Douglas Christopher Wilson
a163e2cdf4 deps: debug@3.1.0 2017-10-13 22:57:58 -04:00
Douglas Christopher Wilson
62e12fe710 Merge tag '4.16.2' 2017-10-13 22:27:30 -04:00
Douglas Christopher Wilson
8fabed82aa Remove path-to-regexp dependency 2017-10-13 22:12:14 -04:00
Douglas Christopher Wilson
f4120a6453 5.0.0-alpha.6 2017-09-25 01:28:00 -04:00
Douglas Christopher Wilson
19c8d64855 Merge tag '4.15.5' 2017-09-25 01:07:08 -04:00
Mike Tunnicliffe
71395f5933 Remove res.redirect(url, status) signature
closes #2941
2017-08-07 20:17:52 -04:00
Mike Tunnicliffe
e3bd14dcca Remove res.send(status, body) signature
closes #2942
2017-08-07 20:06:44 -04:00
Douglas Christopher Wilson
c319fe260a Merge tag '4.15.4' 2017-08-07 19:33:00 -04:00
Douglas Christopher Wilson
21f725e0ef 5.0.0-alpha.5 2017-03-06 08:43:58 -05:00
Douglas Christopher Wilson
e5dbb0cb4e Merge tag '4.15.2' 2017-03-06 08:40:02 -05:00
Douglas Christopher Wilson
a3a9166c52 5.0.0-alpha.4 2017-03-01 18:51:29 -05:00
Wes
06f423d4f5 Remove Express 3.x middleware error stubs
closes #3217
2017-03-01 18:29:48 -05:00
Douglas Christopher Wilson
501e24e0a9 Merge tag '4.15.0' 2017-03-01 18:17:04 -05:00
Douglas Christopher Wilson
c8d9223e93 5.0.0-alpha.3 2017-01-28 22:21:29 -05:00
Douglas Christopher Wilson
4b39a01e6a deps: path-is-absolute@1.0.1 2017-01-28 21:56:53 -05:00
Douglas Christopher Wilson
ad4d52de29 deps: array-flatten@2.1.1 2017-01-28 21:55:32 -05:00
Douglas Christopher Wilson
07077c4457 deps: router@~1.1.5 2017-01-28 21:54:42 -05:00
Douglas Christopher Wilson
bcbb9d56c5 Merge tag '4.14.1' 2017-01-28 21:02:36 -05:00
Mike Tunnicliffe
ab1c9e924e Remove res.jsonp(status, obj) signature
closes #2940
2017-01-27 23:53:41 -05:00
Mike Tunnicliffe
25fdefac45 Remove res.json(status, obj) signature
closes #2939
2017-01-27 23:46:54 -05:00
Mike Tunnicliffe
a856456a95 Remove res.vary() (no arguments) signature
closes #2943
2017-01-27 22:04:36 -05:00
Douglas Christopher Wilson
1dbfee6623 Merge tag '4.14.0' 2016-06-20 00:37:34 -04:00
Douglas Christopher Wilson
8a387d3ede deps: array-flatten@2.0.0 2016-01-21 21:28:24 -05:00
Douglas Christopher Wilson
943f28f05f deps: router@~1.1.3 2016-01-21 21:26:03 -05:00
Douglas Christopher Wilson
7cafdb5824 Merge tag '4.13.4' 2016-01-21 21:23:07 -05:00
Douglas Christopher Wilson
2c668f87c7 5.0.0-alpha.2 2015-07-07 01:33:55 -04:00
Douglas Christopher Wilson
6343288bef Make res.render callback is always async, even for sync view engines
closes #2668
2015-07-07 01:02:13 -04:00
Jeremiah Senkpiel
694869d2aa Use path-is-absolute module for absolute path detection
closes #2620
2015-07-06 23:55:17 -04:00
Douglas Christopher Wilson
cec5780db4 Use router module for routing
closes #2411
2015-07-06 23:46:00 -04:00
Douglas Christopher Wilson
21d52daafb Remove req.param() 2015-07-06 21:15:16 -04:00
Douglas Christopher Wilson
a7d15f382e Remove un-used depd import in router 2015-07-06 21:13:49 -04:00
Douglas Christopher Wilson
6c751191dd Remove utils.flatten 2015-07-06 21:12:44 -04:00
Douglas Christopher Wilson
1e2951a832 Remove app.param(fn) signature 2015-07-06 16:45:09 -04:00
Douglas Christopher Wilson
3a1f27fcde Remove ":" stripping from app.param() 2015-07-06 16:31:51 -04:00
Douglas Christopher Wilson
b309b873f1 Merge tag '4.13.1' 2015-07-06 16:13:54 -04:00
Douglas Christopher Wilson
f90e045334 Merge tag '4.12.4' 2015-05-18 00:41:42 -04:00
Douglas Christopher Wilson
f6ec710534 Merge tag '4.12.0' 2015-02-23 01:00:12 -05:00
Douglas Christopher Wilson
a9ef9e13fb Merge tag '4.11.2' 2015-02-01 15:41:51 -05:00
Douglas Christopher Wilson
f56e2a2503 docs: simplify 5.x history 2015-01-21 04:01:41 -05:00
Douglas Christopher Wilson
8a5ecd3d89 Merge tag '4.11.1' 2015-01-21 03:58:41 -05:00
Douglas Christopher Wilson
4052c15c7f 5.0.0-alpha.1 2014-11-06 21:52:29 -05:00
Douglas Christopher Wilson
97ccc52207 Remove res.send(status) signature 2014-11-06 21:20:46 -05:00
Douglas Christopher Wilson
0fc4f0735a Remove res.sendfile 2014-11-06 21:03:04 -05:00
Douglas Christopher Wilson
ccdbe4ea37 Remove req.acceptsLanguage 2014-11-06 20:54:29 -05:00
Douglas Christopher Wilson
59f2b4048a Remove acceptsEncoding 2014-11-06 20:53:17 -05:00
Douglas Christopher Wilson
7f2532808a Remove req.acceptsCharset 2014-11-06 20:52:14 -05:00
Douglas Christopher Wilson
be35e4927c Remove utils.contentDisposition 2014-11-06 20:48:32 -05:00
Douglas Christopher Wilson
f31dcff10c Remove app.del 2014-11-06 20:44:42 -05:00
Douglas Christopher Wilson
509ebb1aff Add app.router reference back 2014-11-06 20:20:33 -05:00
Douglas Christopher Wilson
78e50547f1 Refactor away init middleware 2014-11-06 20:18:51 -05:00
Douglas Christopher Wilson
dcc4eaabe8 Change req.query to a getter 2014-11-06 20:18:48 -05:00
Douglas Christopher Wilson
8c6f9c4253 Remove app.router error message 2014-11-06 20:18:44 -05:00
Douglas Christopher Wilson
88103063fe Remove res.jsonp(obj, status) signature 2014-11-06 20:18:42 -05:00
Douglas Christopher Wilson
164638b24f Remove res.json(obj, status) signature 2014-11-06 20:18:40 -05:00
Douglas Christopher Wilson
e66625be50 Remove res.send(body, status) signature 2014-11-06 20:18:39 -05:00
Douglas Christopher Wilson
085a29685a Change req.host to return host 2014-11-06 20:18:37 -05:00
99 changed files with 2036 additions and 4133 deletions

View File

@ -1,4 +1,4 @@
# http://editorconfig.org
# https://editorconfig.org
root = true
[*]

View File

@ -1,5 +1,7 @@
root: true
env:
es2022: true
node: true
rules:
eol-last: error
eqeqeq: [error, allow-null]

View File

@ -7,12 +7,16 @@ on:
- develop
- '4.x'
- '5.x'
- '5.0'
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:
@ -25,90 +29,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js {{ matrix.node-version }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
persist-credentials: false
- name: Install dependencies
run: npm install --ignore-scripts --only=dev
run: npm install --ignore-scripts --include=dev
- name: Run lint
run: npm run lint
test:
name: Run tests
strategy:
fail-fast: false
matrix:
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"
# Use supported versions of our testing tools under older versions of Node
# Install npm in some specific cases where we need to
include:
- 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"
node-version: [18, 19, 20, 21, 22, 23]
# Node.js release schedule: https://nodejs.org/en/about/releases/
- node-version: "0.12"
npm-i: "mocha@3.5.3 nyc@10.3.2 supertest@2.0.0"
npm-version: "npm@2.15.11"
- node-version: "4"
npm-i: "mocha@5.2.0 nyc@11.9.0 supertest@3.4.2"
- 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"
- node-version: "6"
npm-i: "mocha@6.2.2 nyc@14.1.1 supertest@3.4.2"
- node-version: "7"
npm-i: "mocha@6.2.2 nyc@14.1.1 supertest@6.1.6"
- node-version: "8"
npm-i: "mocha@7.2.0 nyc@14.1.1"
- node-version: "9"
npm-i: "mocha@7.2.0 nyc@14.1.1"
- node-version: "10"
npm-i: "mocha@8.4.0"
- node-version: "11"
npm-i: "mocha@8.4.0"
- node-version: "12"
npm-i: "mocha@9.2.2"
- node-version: "13"
npm-i: "mocha@9.2.2"
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
runs-on: ${{ matrix.os }}
steps:
@ -121,10 +61,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Npm version fixes
if: ${{matrix.npm-version != ''}}
run: npm install -g ${{ matrix.npm-version }}
- name: Configure npm loglevel
run: |
npm config set loglevel error
@ -133,13 +69,6 @@ jobs:
- name: Install dependencies
run: npm install
- name: Install Node version specific dev deps
if: ${{ matrix.npm-i != '' }}
run: npm install --save-dev ${{ matrix.npm-i }}
- name: Remove non-test dependencies
run: npm rm --silent --save-dev connect-redis
- name: Output Node and NPM versions
run: |
echo "Node.js version: $(node -v)"
@ -147,44 +76,39 @@ jobs:
- name: Run tests
shell: bash
run: |
npm run test-ci
cp coverage/lcov.info "coverage/${{ matrix.node-version }}.lcov"
- name: Collect code coverage
run: |
mv ./coverage "./${{ matrix.node-version }}"
mkdir ./coverage
mv "./${{ matrix.node-version }}" "./coverage/${{ matrix.node-version }}"
run: npm run test-ci
- name: Upload code coverage
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: coverage
path: ./coverage
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
path: ./coverage/lcov.info
retention-days: 1
coverage:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
- name: Install lcov
shell: bash
run: sudo apt-get -y install lcov
- name: Install lcov
shell: bash
run: sudo apt-get -y install lcov
- name: Collect coverage reports
uses: actions/download-artifact@v3
with:
name: coverage
path: ./coverage
- name: 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 ./coverage/lcov.info
- 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@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload coverage report
uses: coverallsapp/github-action@v2
with:
file: ./lcov.info

View File

@ -1,69 +0,0 @@
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

98
.github/workflows/legacy.yml vendored Normal file
View File

@ -0,0 +1,98 @@
name: legacy
on:
push:
branches:
- master
- develop
- '4.x'
- '5.x'
- '5.0'
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:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [16, 17]
# Node.js release schedule: https://nodejs.org/en/about/releases/
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Configure npm loglevel
run: |
npm config set loglevel error
shell: bash
- name: Install dependencies
run: npm install
- name: Output Node and NPM versions
run: |
echo "Node.js version: $(node -v)"
echo "NPM version: $(npm -v)"
- name: Run tests
shell: bash
run: npm run test-ci
- name: Upload code coverage
uses: actions/upload-artifact@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:
- 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

72
.github/workflows/scorecard.yml vendored Normal file
View File

@ -0,0 +1,72 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '16 21 * * 1'
push:
branches: [ "master" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
with:
sarif_file: results.sarif

View File

@ -20,11 +20,11 @@ alike.
Express is made of many modules spread between three GitHub Orgs:
- [expressjs](http://github.com/expressjs/): Top level middleware and
- [expressjs](https://github.com/expressjs/): Top level middleware and
libraries
- [pillarjs](http://github.com/pillarjs/): Components which make up
- [pillarjs](https://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](https://github.com/jshttp/): Low level HTTP libraries
### 1.2: Out-of-Scope

View File

@ -7,7 +7,7 @@ Open issues for the expressjs.com website in https://github.com/expressjs/expres
## PRs and Code contributions
* Tests must pass.
* Follow the [JavaScript Standard Style](http://standardjs.com/) and `npm run lint`.
* Follow the [JavaScript Standard Style](https://standardjs.com/) and `npm run lint`.
* If you fix a bug, add a test.
## Branches

View File

@ -132,78 +132,113 @@ 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.
have 2FA enabled on both their GitHub and npm accounts.
Any TC member or an existing captain on the **same** repo can nominate another committer
to the captain role. To do so, they should submit a PR to this document, updating the
**Active Project Captains** section (while maintaining the sort order) with the project
name, the nominee's GitHub handle, and their npm username (if different).
- Repos can have as many captains as make sense for the scope of work.
- A TC member or an existing repo captain **on the same project** can nominate a new captain.
Repo captains from other projects should not nominate captains for a different project.
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
- [`expressjs/badgeboard`](https://github.com/expressjs/badgeboard): @wesleytodd
- [`expressjs/basic-auth-connect`](https://github.com/expressjs/basic-auth-connect): @ulisesGascon
- [`expressjs/body-parser`](https://github.com/expressjs/body-parser): @wesleytodd, @jonchurch, @ulisesGascon
- [`expressjs/compression`](https://github.com/expressjs/compression): @ulisesGascon
- [`expressjs/connect-multiparty`](https://github.com/expressjs/connect-multiparty): @ulisesGascon
- [`expressjs/cookie-parser`](https://github.com/expressjs/cookie-parser): @wesleytodd, @UlisesGascon
- [`expressjs/cookie-session`](https://github.com/expressjs/cookie-session): @ulisesGascon
- [`expressjs/cors`](https://github.com/expressjs/cors): @jonchurch, @ulisesGascon
- [`expressjs/discussions`](https://github.com/expressjs/discussions): @wesleytodd
- [`expressjs/errorhandler`](https://github.com/expressjs/errorhandler): @ulisesGascon
- [`expressjs/express-paginate`](https://github.com/expressjs/express-paginate): @ulisesGascon
- [`expressjs/express`](https://github.com/expressjs/express): @wesleytodd, @ulisesGascon
- [`expressjs/expressjs.com`](https://github.com/expressjs/expressjs.com): @crandmck, @jonchurch, @bjohansebas
- [`expressjs/flash`](https://github.com/expressjs/flash): @ulisesGascon
- [`expressjs/generator`](https://github.com/expressjs/generator): @wesleytodd
- [`expressjs/method-override`](https://github.com/expressjs/method-override): @ulisesGascon
- [`expressjs/morgan`](https://github.com/expressjs/morgan): @jonchurch, @ulisesGascon
- [`expressjs/multer`](https://github.com/expressjs/multer): @LinusU, @ulisesGascon
- [`expressjs/response-time`](https://github.com/expressjs/response-time): @UlisesGascon
- [`expressjs/serve-favicon`](https://github.com/expressjs/serve-favicon): @ulisesGascon
- [`expressjs/serve-index`](https://github.com/expressjs/serve-index): @ulisesGascon
- [`expressjs/serve-static`](https://github.com/expressjs/serve-static): @ulisesGascon
- [`expressjs/session`](https://github.com/expressjs/session): @ulisesGascon
- [`expressjs/statusboard`](https://github.com/expressjs/statusboard): @wesleytodd
- [`expressjs/timeout`](https://github.com/expressjs/timeout): @ulisesGascon
- [`expressjs/vhost`](https://github.com/expressjs/vhost): @ulisesGascon
- [`jshttp/accepts`](https://github.com/jshttp/accepts): @blakeembrey
- [`jshttp/basic-auth`](https://github.com/jshttp/basic-auth): @blakeembrey
- [`jshttp/compressible`](https://github.com/jshttp/compressible): @blakeembrey
- [`jshttp/content-disposition`](https://github.com/jshttp/content-disposition): @blakeembrey
- [`jshttp/content-type`](https://github.com/jshttp/content-type): @blakeembrey
- [`jshttp/cookie`](https://github.com/jshttp/cookie): @blakeembrey
- [`jshttp/etag`](https://github.com/jshttp/etag): @blakeembrey
- [`jshttp/forwarded`](https://github.com/jshttp/forwarded): @blakeembrey
- [`jshttp/fresh`](https://github.com/jshttp/fresh): @blakeembrey
- [`jshttp/http-assert`](https://github.com/jshttp/http-assert): @wesleytodd, @jonchurch, @ulisesGascon
- [`jshttp/http-errors`](https://github.com/jshttp/http-errors): @wesleytodd, @jonchurch, @ulisesGascon
- [`jshttp/media-typer`](https://github.com/jshttp/media-typer): @blakeembrey
- [`jshttp/methods`](https://github.com/jshttp/methods): @blakeembrey
- [`jshttp/mime-db`](https://github.com/jshttp/mime-db): @blakeembrey, @UlisesGascon
- [`jshttp/mime-types`](https://github.com/jshttp/mime-types): @blakeembrey, @UlisesGascon
- [`jshttp/negotiator`](https://github.com/jshttp/negotiator): @blakeembrey
- [`jshttp/on-finished`](https://github.com/jshttp/on-finished): @wesleytodd, @ulisesGascon
- [`jshttp/on-headers`](https://github.com/jshttp/on-headers): @blakeembrey
- [`jshttp/proxy-addr`](https://github.com/jshttp/proxy-addr): @wesleytodd, @ulisesGascon
- [`jshttp/range-parser`](https://github.com/jshttp/range-parser): @blakeembrey
- [`jshttp/statuses`](https://github.com/jshttp/statuses): @blakeembrey
- [`jshttp/type-is`](https://github.com/jshttp/type-is): @blakeembrey
- [`jshttp/vary`](https://github.com/jshttp/vary): @blakeembrey
- [`pillarjs/cookies`](https://github.com/pillarjs/cookies): @blakeembrey
- [`pillarjs/csrf`](https://github.com/pillarjs/csrf): @ulisesGascon
- [`pillarjs/encodeurl`](https://github.com/pillarjs/encodeurl): @blakeembrey
- [`pillarjs/finalhandler`](https://github.com/pillarjs/finalhandler): @wesleytodd, @ulisesGascon
- [`pillarjs/hbs`](https://github.com/pillarjs/hbs): @ulisesGascon
- [`pillarjs/multiparty`](https://github.com/pillarjs/multiparty): @blakeembrey
- [`pillarjs/parseurl`](https://github.com/pillarjs/parseurl): @blakeembrey
- [`pillarjs/path-to-regexp`](https://github.com/pillarjs/path-to-regexp): @blakeembrey
- [`pillarjs/request`](https://github.com/pillarjs/request): @wesleytodd
- [`pillarjs/resolve-path`](https://github.com/pillarjs/resolve-path): @blakeembrey
- [`pillarjs/router`](https://github.com/pillarjs/router): @wesleytodd, @ulisesGascon
- [`pillarjs/send`](https://github.com/pillarjs/send): @blakeembrey
- [`pillarjs/understanding-csrf`](https://github.com/pillarjs/understanding-csrf): @ulisesGascon
### Current Initiative Captains
- Triage team [ref](https://github.com/expressjs/discussions/issues/227): @UlisesGascon
## Developer's Certificate of Origin 1.1
```text
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```

View File

@ -1,8 +1,244 @@
unreleased
========================
* Remove `utils-merge` dependency - use spread syntax instead
* Remove `Object.setPrototypeOf` polyfill
* cleanup: remove AsyncLocalStorage check from tests
* cleanup: remove unnecessary require for global Buffer
* perf: use loop for acceptParams
* Replace `methods` dependency with standard library
* refactor: prefix built-in node module imports
* Remove unused `depd` dependency
* Add support for `Uint8Array` in `res.send`
* Add support for ETag option in res.sendFile
* Extend res.links() to allow adding multiple links with the same rel
* deps: debug@^4.4.0
* deps: body-parser@^2.1.0
* deps: router@^2.1.0
* deps: nyc@^17.1.0
* deps: mocha@^10.7.3
* deps: marked@^15.0.3
* deps: express-session@^1.18.1
* deps: ejs@^3.1.10
* deps: content-type@^1.0.5
* deps: connect-redis@^8.0.1
* deps: supertest@^6.3.4
5.0.1 / 2024-10-08
==========
* deps: path-to-regexp@0.1.8
* Update `cookie` semver lock to address [CVE-2024-47764](https://nvd.nist.gov/vuln/detail/CVE-2024-47764)
5.0.0 / 2024-09-10
=========================
* remove:
- `path-is-absolute` dependency - use `path.isAbsolute` instead
* breaking:
* `res.status()` accepts only integers, and input must be greater than 99 and less than 1000
* will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range
* will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs
* deps: send@1.0.0
* `res.redirect('back')` and `res.location('back')` is no longer a supported magic string, explicitly use `req.get('Referrer') || '/'`.
* change:
- `res.clearCookie` will ignore user provided `maxAge` and `expires` options
* deps: cookie-signature@^1.2.1
* deps: debug@4.3.6
* deps: merge-descriptors@^2.0.0
* deps: serve-static@^2.1.0
* deps: qs@6.13.0
* deps: accepts@^2.0.0
* deps: mime-types@^3.0.0
- `application/javascript` => `text/javascript`
* deps: type-is@^2.0.0
* deps: content-disposition@^1.0.0
* deps: finalhandler@^2.0.0
* deps: fresh@^2.0.0
* deps: body-parser@^2.0.1
* deps: send@^1.1.0
5.0.0-beta.3 / 2024-03-25
=========================
This incorporates all changes after 4.19.1 up to 4.19.2.
5.0.0-beta.2 / 2024-03-20
=========================
This incorporates all changes after 4.17.2 up to 4.19.1.
5.0.0-beta.1 / 2022-02-14
=========================
This is the first Express 5.0 beta release, based off 4.17.2 and includes
changes from 5.0.0-alpha.8.
* change:
- Default "query parser" setting to `'simple'`
- Requires Node.js 4+
- Use `mime-types` for file to content type mapping
* deps: array-flatten@3.0.0
* deps: body-parser@2.0.0-beta.1
- `req.body` is no longer always initialized to `{}`
- `urlencoded` parser now defaults `extended` to `false`
- Use `on-finished` to determine when body read
* deps: router@2.0.0-beta.1
- Add new `?`, `*`, and `+` parameter modifiers
- Internalize private `router.process_params` method
- Matching group expressions are only RegExp syntax
- Named matching groups no longer available by position in `req.params`
- Regular expressions can only be used in a matching group
- Remove `debug` dependency
- Special `*` path segment behavior removed
- deps: array-flatten@3.0.0
- deps: parseurl@~1.3.3
- deps: path-to-regexp@3.2.0
- deps: setprototypeof@1.2.0
* deps: send@1.0.0-beta.1
- Change `dotfiles` option default to `'ignore'`
- Remove `hidden` option; use `dotfiles` option instead
- Use `mime-types` for file to content type mapping
- deps: debug@3.1.0
* deps: serve-static@2.0.0-beta.1
- Change `dotfiles` option default to `'ignore'`
- Remove `hidden` option; use `dotfiles` option instead
- Use `mime-types` for file to content type mapping
- Remove `express.static.mime` export; use `mime-types` package instead
- deps: send@1.0.0-beta.1
5.0.0-alpha.8 / 2020-03-25
==========================
This is the eighth Express 5.0 alpha release, based off 4.17.1 and includes
changes from 5.0.0-alpha.7.
5.0.0-alpha.7 / 2018-10-26
==========================
This is the seventh Express 5.0 alpha release, based off 4.16.4 and includes
changes from 5.0.0-alpha.6.
The major change with this alpha is the basic support for returned, rejected
Promises in the router.
* remove:
- `path-to-regexp` dependency
* deps: debug@3.1.0
- Add `DEBUG_HIDE_DATE` environment variable
- Change timer to per-namespace instead of global
- Change non-TTY date format
- Remove `DEBUG_FD` environment variable support
- Support 256 namespace colors
* deps: router@2.0.0-alpha.1
- Add basic support for returned, rejected Promises
- Fix JSDoc for `Router` constructor
- deps: debug@3.1.0
- deps: parseurl@~1.3.2
- deps: setprototypeof@1.1.0
- deps: utils-merge@1.0.1
5.0.0-alpha.6 / 2017-09-24
==========================
This is the sixth Express 5.0 alpha release, based off 4.15.5 and includes
changes from 5.0.0-alpha.5.
* remove:
- `res.redirect(url, status)` signature - use `res.redirect(status, url)`
- `res.send(status, body)` signature - use `res.status(status).send(body)`
* deps: router@~1.3.1
- deps: debug@2.6.8
5.0.0-alpha.5 / 2017-03-06
==========================
This is the fifth Express 5.0 alpha release, based off 4.15.2 and includes
changes from 5.0.0-alpha.4.
5.0.0-alpha.4 / 2017-03-01
==========================
This is the fourth Express 5.0 alpha release, based off 4.15.0 and includes
changes from 5.0.0-alpha.3.
* remove:
- Remove Express 3.x middleware error stubs
* deps: router@~1.3.0
- Add `next("router")` to exit from router
- Fix case where `router.use` skipped requests routes did not
- Skip routing when `req.url` is not set
- Use `%o` in path debug to tell types apart
- deps: debug@2.6.1
- deps: setprototypeof@1.0.3
- perf: add fast match path for `*` route
5.0.0-alpha.3 / 2017-01-28
==========================
This is the third Express 5.0 alpha release, based off 4.14.1 and includes
changes from 5.0.0-alpha.2.
* remove:
- `res.json(status, obj)` signature - use `res.status(status).json(obj)`
- `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)`
- `res.vary()` (no arguments) -- provide a field name as an argument
* deps: array-flatten@2.1.1
* deps: path-is-absolute@1.0.1
* deps: router@~1.1.5
- deps: array-flatten@2.0.1
- deps: methods@~1.1.2
- deps: parseurl@~1.3.1
- deps: setprototypeof@1.0.2
5.0.0-alpha.2 / 2015-07-06
==========================
This is the second Express 5.0 alpha release, based off 4.13.1 and includes
changes from 5.0.0-alpha.1.
* remove:
- `app.param(fn)`
- `req.param()` -- use `req.params`, `req.body`, or `req.query` instead
* change:
- `res.render` callback is always async, even for sync view engines
- The leading `:` character in `name` for `app.param(name, fn)` is no longer removed
- Use `router` module for routing
- Use `path-is-absolute` module for absolute path detection
5.0.0-alpha.1 / 2014-11-06
==========================
This is the first Express 5.0 alpha release, based off 4.10.1.
* remove:
- `app.del` - use `app.delete`
- `req.acceptsCharset` - use `req.acceptsCharsets`
- `req.acceptsEncoding` - use `req.acceptsEncodings`
- `req.acceptsLanguage` - use `req.acceptsLanguages`
- `res.json(obj, status)` signature - use `res.json(status, obj)`
- `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)`
- `res.send(body, status)` signature - use `res.send(status, body)`
- `res.send(status)` signature - use `res.sendStatus(status)`
- `res.sendfile` - use `res.sendFile` instead
- `express.query` middleware
* change:
- `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname
- `req.query` is now a getter instead of a plain property
* add:
- `app.router` is a reference to the base router
4.20.0 / 2024-09-10
==========
* deps: serve-static@0.16.0
* Remove link renderization in html while redirecting
* deps: send@0.19.0
* Remove link renderization in html while redirecting
* deps: body-parser@0.6.0
* add `depth` option to customize the depth level in the parser
* IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`)
* Remove link renderization in html while using `res.redirect`
* deps: path-to-regexp@0.1.10
- Adds support for named matching groups in the routes using a regex
- Adds backtracking protection to parameters without regexes defined
* deps: encodeurl@~2.0.0
- Removes encoding of `\`, `|`, and `^` to align better with URL spec
* Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie`

View File

@ -1,6 +1,6 @@
[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/)
[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](https://expressjs.com/)
**Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org).**
**Fast, unopinionated, minimalist web framework for [Node.js](https://nodejs.org).**
**This project has a [Code of Conduct][].**
@ -20,16 +20,16 @@
[![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]
```js
const express = require('express')
import express from 'express'
const app = express()
app.get('/', function (req, res) {
app.get('/', (req, res) => {
res.send('Hello World')
})
@ -42,7 +42,7 @@ This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/).
Before installing, [download and install Node.js](https://nodejs.org/en/download/).
Node.js 0.10 or higher is required.
Node.js 18 or higher is required.
If this is a brand new project, make sure to create a `package.json` first with
the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
@ -50,11 +50,11 @@ the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
```console
$ npm install express
```bash
npm install express
```
Follow [our installing guide](http://expressjs.com/en/starter/installing.html)
Follow [our installing guide](https://expressjs.com/en/starter/installing.html)
for more information.
## Features
@ -69,14 +69,11 @@ for more information.
## Docs & Community
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
* [#express](https://web.libera.chat/#express) on [Libera Chat](https://libera.chat) IRC
* [Website and Documentation](https://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
* [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules
* Visit the [Wiki](https://github.com/expressjs/express/wiki)
* [Google Group](https://groups.google.com/group/express-js) for discussion
* [Gitter](https://gitter.im/expressjs/express) for support and discussion
* [Github Discussions](https://github.com/expressjs/discussions) for discussion on the development and usage of Express
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/expressjs/express/wiki/New-features-in-4.x).
**PROTIP** Be sure to read the [migration guide to v5](https://expressjs.com/en/guide/migrating-5)
## Quick Start
@ -84,26 +81,26 @@ for more information.
Install the executable. The executable's major version will match Express's:
```console
$ npm install -g express-generator@4
```bash
npm install -g express-generator@4
```
Create the app:
```console
$ express /tmp/foo && cd /tmp/foo
```bash
express /tmp/foo && cd /tmp/foo
```
Install dependencies:
```console
$ npm install
```bash
npm install
```
Start the server:
```console
$ npm start
```bash
npm start
```
View the website at: http://localhost:3000
@ -115,29 +112,32 @@ $ npm start
HTTP APIs.
Express does not force you to use any specific ORM or template engine. With support for over
14 template engines via [Consolidate.js](https://github.com/tj/consolidate.js),
14 template engines via [@ladjs/consolidate](https://github.com/ladjs/consolidate),
you can quickly craft your perfect framework.
## Examples
To view the examples, clone the Express repo and install the dependencies:
To view the examples, clone the Express repository:
```console
$ git clone https://github.com/expressjs/express.git --depth 1
$ cd express
$ npm install
```bash
git clone https://github.com/expressjs/express.git --depth 1 && cd express
```
Then install the dependencies:
```bash
npm install
```
Then run whichever example you want:
```console
$ node examples/content-negotiation
```bash
node examples/content-negotiation
```
## Contributing
[![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,
@ -152,11 +152,16 @@ If you discover a security vulnerability in Express, please see [Security Polici
### Running Tests
To run the test suite, first install the dependencies, then run `npm test`:
To run the test suite, first install the dependencies:
```console
$ npm install
$ npm test
```bash
npm install
```
Then run `npm test`:
```bash
npm test
```
## People
@ -192,6 +197,7 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
### Triagers
* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair**
* [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran**
* [carpasse](https://github.com/carpasse) - **Carlos Serrano**
* [CBID2](https://github.com/CBID2) - **Christine Belzie**
* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**
@ -203,7 +209,9 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
* [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)
* [Phillip9587](https://github.com/Phillip9587) - **Phillip Barta**
* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
* [rxmarbles](https://github.com/rxmarbles) **Rick Markins** (He/him)
<details>
<summary>Triagers emeriti members</summary>
@ -243,16 +251,12 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
[MIT](LICENSE)
[appveyor-image]: https://badgen.net/appveyor/ci/dougwilson/express/master?label=windows
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/express
[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-image]: https://badgen.net/github/checks/expressjs/express/master?label=CI
[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

View File

@ -31,7 +31,7 @@ Before publishing, the following preconditions should be met:
below) will exist documenting:
- the proposed changes
- the type of release: patch, minor or major
- the version number (according to semantic versioning - http://semver.org)
- the version number (according to semantic versioning - https://semver.org)
- The proposed changes should be complete.
There are two main release flows: patch and non-patch.
@ -129,9 +129,10 @@ $ git merge --ff-only <proposal-branch>
<release-branch> - see "Release branch" of "Branches" above.
<proposal-branch> - see "Proposal branch" of "Non-patch flow" above.
**NOTE:** You may need to rebase the proposal branch to allow a fast-forward
merge. Using a fast-forward merge keeps the history clean as it does
not introduce merge commits.
> [!NOTE]
> You may need to rebase the proposal branch to allow a fast-forward
> merge. Using a fast-forward merge keeps the history clean as it does
> not introduce merge commits.
### Step 3. Update the History.md and package.json to the new version number
@ -189,11 +190,13 @@ $ npm login <npm-username>
$ npm publish
```
**NOTE:** The version number to publish will be picked up automatically from
package.json.
> [!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,
The documentation website https://expressjs.com/ documents the current release version in various places. To update these, follow these steps:
1. Manually run the [`Update External Docs` workflow](https://github.com/expressjs/expressjs.com/actions/workflows/update-external-docs.yml) in expressjs.com repository.
2. Add a new section to the [changelog](https://github.com/expressjs/expressjs.com/blob/gh-pages/en/changelog/index.md) in the expressjs.com website.

View File

@ -14,7 +14,7 @@ Thank you for improving the security of Express. We appreciate your efforts and
responsible disclosure and will make every effort to acknowledge your
contributions.
Report security bugs by emailing the lead maintainer in the Readme.md file.
Report security bugs by emailing `express-security@lists.openjsf.org`.
To ensure the timely response to your report, please ensure that the entirety
of the report is contained within the email body and not solely behind a web

View File

@ -68,3 +68,5 @@ If you have questions feel free to reach out to any of the TC members.
- For recurring issues, it is helpful to create functional examples to demonstrate (publish as gists or a repo)
- Review and identify the maintainers. If necessary, at-mention one or more of them if you are unsure what to do
- Make sure all your interactions are professional, welcoming, and respectful to the parties involved.
- When an issue refers to security concerns, responsibility is delegated to the repository captain or the security group in any public communication.
- If an issue has been open for a long time, the person in charge should be contacted internally through the private Slack chat.

View File

@ -1,113 +0,0 @@
environment:
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "1.8"
- nodejs_version: "2.5"
- nodejs_version: "3.3"
- nodejs_version: "4.9"
- nodejs_version: "5.12"
- nodejs_version: "6.17"
- nodejs_version: "7.10"
- nodejs_version: "8.17"
- nodejs_version: "9.11"
- nodejs_version: "10.24"
- nodejs_version: "11.15"
- nodejs_version: "12.22"
- nodejs_version: "13.14"
- nodejs_version: "14.20"
- nodejs_version: "15.14"
- nodejs_version: "16.20"
- nodejs_version: "17.9"
- nodejs_version: "18.19"
- nodejs_version: "19.9"
- nodejs_version: "20.11"
- nodejs_version: "21.6"
- 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) x64 }
# Configure npm
- ps: |
npm config set loglevel error
if ((npm config get package-lock) -eq "true") {
npm config set package-lock false
} else {
npm config set shrinkwrap false
}
# Remove all non-test dependencies
- ps: |
# Remove example dependencies
npm rm --silent --save-dev connect-redis
# Remove lint dependencies
cmd.exe /c "node -pe `"Object.keys(require('./package').devDependencies).join('\n')`"" | `
sls "^eslint(-|$)" | `
%{ npm rm --silent --save-dev $_ }
# Setup Node.js version-specific dependencies
- ps: |
# mocha for testing
# - use 3.x for Node.js < 4
# - use 5.x for Node.js < 6
# - use 6.x for Node.js < 8
# - use 7.x for Node.js < 10
# - use 8.x for Node.js < 12
# - use 9.x for Node.js < 14
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev mocha@3.5.3
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev mocha@5.2.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
npm install --silent --save-dev mocha@6.2.2
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
npm install --silent --save-dev mocha@7.2.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 12) {
npm install --silent --save-dev mocha@8.4.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 14) {
npm install --silent --save-dev mocha@9.2.2
}
- ps: |
# nyc for test coverage
# - use 10.3.2 for Node.js < 4
# - use 11.9.0 for Node.js < 6
# - use 14.1.1 for Node.js < 10
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev nyc@10.3.2
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev nyc@11.9.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
npm install --silent --save-dev nyc@14.1.1
}
- ps: |
# supertest for http calls
# - use 2.0.0 for Node.js < 4
# - use 3.4.2 for Node.js < 7
# - use 6.1.6 for Node.js < 8
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev supertest@2.0.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 7) {
npm install --silent --save-dev supertest@3.4.2
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
npm install --silent --save-dev supertest@6.1.6
}
# Update Node.js modules
- ps: |
# Prune & rebuild node_modules
if (Test-Path -Path node_modules) {
npm prune
npm rebuild
}
# Install Node.js modules
- npm install
build: off
test_script:
# Output version data
- ps: |
node --version
npm --version
# Run test script
- npm run test-ci
version: "{build}"

View File

@ -6,7 +6,7 @@
var express = require('../..');
var hash = require('pbkdf2-password')()
var path = require('path');
var path = require('node:path');
var session = require('express-session');
var app = module.exports = express();
@ -18,7 +18,7 @@ app.set('views', path.join(__dirname, 'views'));
// middleware
app.use(express.urlencoded({ extended: false }))
app.use(express.urlencoded())
app.use(session({
resave: false, // don't save session if unmodified
saveUninitialized: false, // don't create session until something stored
@ -102,6 +102,7 @@ app.get('/login', function(req, res){
});
app.post('/login', function (req, res, next) {
if (!req.body) return res.sendStatus(400)
authenticate(req.body.username, req.body.password, function(err, user){
if (err) return next(err)
if (user) {
@ -115,7 +116,7 @@ app.post('/login', function (req, res, next) {
req.session.success = 'Authenticated as ' + user.name
+ ' click to <a href="/logout">logout</a>. '
+ ' You may now access <a href="/restricted">/restricted</a>.';
res.redirect('back');
res.redirect(req.get('Referrer') || '/');
});
} else {
req.session.error = 'Authentication failed, please check your '

View File

@ -10,7 +10,7 @@
font: 13px Helvetica, Arial, sans-serif;
}
.error {
color: red
color: red;
}
.success {
color: green;

View File

@ -19,7 +19,7 @@ if (process.env.NODE_ENV !== 'test') app.use(logger(':method :url'))
app.use(cookieParser('my secret here'));
// parses x-www-form-urlencoded
app.use(express.urlencoded({ extended: false }))
app.use(express.urlencoded())
app.get('/', function(req, res){
if (req.cookies.remember) {
@ -33,13 +33,17 @@ app.get('/', function(req, res){
app.get('/forget', function(req, res){
res.clearCookie('remember');
res.redirect('back');
res.redirect(req.get('Referrer') || '/');
});
app.post('/', function(req, res){
var minute = 60000;
if (req.body.remember) res.cookie('remember', 1, { maxAge: minute });
res.redirect('back');
if (req.body && req.body.remember) {
res.cookie('remember', 1, { maxAge: minute })
}
res.redirect(req.get('Referrer') || '/');
});
/* istanbul ignore next */

View File

@ -5,7 +5,7 @@
*/
var express = require('../../');
var path = require('path');
var path = require('node:path');
var app = module.exports = express();
@ -23,8 +23,8 @@ 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){
res.download(req.params.file, { root: FILES_DIR }, function (err) {
app.get('/files/*file', function (req, res, next) {
res.download(req.params.file.join('/'), { root: FILES_DIR }, function (err) {
if (!err) return; // file sent
if (err.status !== 404) return next(err); // non-404 error
// file for download not found

View File

@ -5,7 +5,7 @@
*/
var express = require('../../');
var path = require('path');
var path = require('node:path');
var app = module.exports = express();

View File

@ -5,7 +5,7 @@
*/
var express = require('../../');
var path = require('path');
var path = require('node:path');
var app = module.exports = express();
var logger = require('morgan');
var silent = process.env.NODE_ENV === 'test'

View File

@ -6,9 +6,9 @@
var escapeHtml = require('escape-html');
var express = require('../..');
var fs = require('fs');
var fs = require('node:fs');
var marked = require('marked');
var path = require('path');
var path = require('node:path');
var app = module.exports = express();

View File

@ -6,7 +6,7 @@
var express = require('../..');
var logger = require('morgan');
var path = require('path');
var path = require('node:path');
var session = require('express-session');
var methodOverride = require('method-override');

View File

@ -5,8 +5,8 @@
*/
var express = require('../../..');
var fs = require('fs');
var path = require('path');
var fs = require('node:fs');
var path = require('node:path');
module.exports = function(parent, options){
var dir = path.join(__dirname, '..', 'controllers');

View File

@ -32,7 +32,8 @@ app.param(['to', 'from'], function(req, res, next, num, name){
// Load user by id
app.param('user', function(req, res, next, id){
if (req.user = users[id]) {
req.user = users[id]
if (req.user) {
next();
} else {
next(createError(404, 'failed to find user'));

View File

@ -12,7 +12,7 @@ var app = module.exports = express();
app.resource = function(path, obj) {
this.get(path, obj.index);
this.get(path + '/:a..:b.:format?', function(req, res){
this.get(path + '/:a..:b{.:format}', function(req, res){
var a = parseInt(req.params.a, 10);
var b = parseInt(req.params.b, 10);
var format = req.params.format;

View File

@ -5,7 +5,7 @@
*/
var express = require('../..');
var path = require('path');
var path = require('node:path');
var app = express();
var logger = require('morgan');
var cookieParser = require('cookie-parser');
@ -38,7 +38,7 @@ app.get('/', site.index);
// User
app.get('/users', user.list);
app.all('/user/:id/:op?', user.load);
app.all('/user/:id{/:op}', user.load);
app.get('/user/:id', user.view);
app.get('/user/:id/view', user.view);
app.get('/user/:id/edit', user.edit);

View File

@ -43,5 +43,5 @@ exports.update = function(req, res){
var user = req.body.user;
req.user.name = user.name;
req.user.email = user.email;
res.redirect('back');
res.redirect(req.get('Referrer') || '/');
};

View File

@ -12,7 +12,7 @@
*/
var express = require('../..');
var path = require('path');
var path = require('node:path');
var redis = require('redis');
var db = redis.createClient();
@ -35,10 +35,10 @@ db.sadd('cat', 'luna');
* GET search for :query.
*/
app.get('/search/:query?', function(req, res){
app.get('/search/:query?', function(req, res, next){
var query = req.params.query;
db.smembers(query, function(err, vals){
if (err) return res.send(500);
if (err) return next(err);
res.send(vals);
});
});

View File

@ -6,7 +6,7 @@
var express = require('../..');
var logger = require('morgan');
var path = require('path');
var path = require('node:path');
var app = express();
// log requests

View File

@ -4,8 +4,8 @@
* Module dependencies.
*/
var https = require('https');
var path = require('path');
var https = require('node:https');
var path = require('node:path');
var extname = path.extname;
/**

View File

@ -5,7 +5,7 @@
*/
var express = require('../..');
var path = require('path');
var path = require('node:path');
var User = require('./user');
var app = express();

View File

@ -14,29 +14,24 @@
*/
var finalhandler = require('finalhandler');
var Router = require('./router');
var methods = require('methods');
var middleware = require('./middleware/init');
var query = require('./middleware/query');
var debug = require('debug')('express:application');
var View = require('./view');
var http = require('http');
var http = require('node:http');
var methods = require('./utils').methods;
var compileETag = require('./utils').compileETag;
var compileQueryParser = require('./utils').compileQueryParser;
var compileTrust = require('./utils').compileTrust;
var deprecate = require('depd')('express');
var flatten = require('array-flatten');
var merge = require('utils-merge');
var resolve = require('path').resolve;
var setPrototypeOf = require('setprototypeof')
var resolve = require('node:path').resolve;
var once = require('once')
var Router = require('router');
/**
* Module variables.
* @private
*/
var hasOwnProperty = Object.prototype.hasOwnProperty
var slice = Array.prototype.slice;
var flatten = Array.prototype.flat;
/**
* Application prototype.
@ -62,11 +57,29 @@ var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
*/
app.init = function init() {
this.cache = {};
this.engines = {};
this.settings = {};
var router = null;
this.cache = Object.create(null);
this.engines = Object.create(null);
this.settings = Object.create(null);
this.defaultConfiguration();
// Setup getting to lazily add base router
Object.defineProperty(this, 'router', {
configurable: true,
enumerable: true,
get: function getrouter() {
if (router === null) {
router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
}
return router;
}
});
};
/**
@ -81,7 +94,7 @@ app.defaultConfiguration = function defaultConfiguration() {
this.enable('x-powered-by');
this.set('etag', 'weak');
this.set('env', env);
this.set('query parser', 'extended');
this.set('query parser', 'simple')
this.set('subdomain offset', 2);
this.set('trust proxy', false);
@ -102,10 +115,10 @@ app.defaultConfiguration = function defaultConfiguration() {
}
// inherit protos
setPrototypeOf(this.request, parent.request)
setPrototypeOf(this.response, parent.response)
setPrototypeOf(this.engines, parent.engines)
setPrototypeOf(this.settings, parent.settings)
Object.setPrototypeOf(this.request, parent.request)
Object.setPrototypeOf(this.response, parent.response)
Object.setPrototypeOf(this.engines, parent.engines)
Object.setPrototypeOf(this.settings, parent.settings)
});
// setup locals
@ -125,32 +138,6 @@ app.defaultConfiguration = function defaultConfiguration() {
if (env === 'production') {
this.enable('view cache');
}
Object.defineProperty(this, 'router', {
get: function() {
throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
}
});
};
/**
* lazily adds the base router if it has not yet been added.
*
* We cannot add the base router in the defaultConfiguration because
* it reads app settings which might be set after that has run.
*
* @private
*/
app.lazyrouter = function lazyrouter() {
if (!this._router) {
this._router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
this._router.use(query(this.get('query parser fn')));
this._router.use(middleware.init(this));
}
};
/**
@ -163,22 +150,31 @@ app.lazyrouter = function lazyrouter() {
*/
app.handle = function handle(req, res, callback) {
var router = this._router;
// final handler
var done = callback || finalhandler(req, res, {
env: this.get('env'),
onerror: logerror.bind(this)
});
// no routes
if (!router) {
debug('no routes defined on app');
done();
return;
// set powered by header
if (this.enabled('x-powered-by')) {
res.setHeader('X-Powered-By', 'Express');
}
router.handle(req, res, done);
// set circular references
req.res = res;
res.req = req;
// alter the prototypes
Object.setPrototypeOf(req, this.request)
Object.setPrototypeOf(res, this.response)
// setup locals
if (!res.locals) {
res.locals = Object.create(null);
}
this.router.handle(req, res, done);
};
/**
@ -211,15 +207,14 @@ app.use = function use(fn) {
}
}
var fns = flatten(slice.call(arguments, offset));
var fns = flatten.call(slice.call(arguments, offset), Infinity);
if (fns.length === 0) {
throw new TypeError('app.use() requires a middleware function')
}
// setup router
this.lazyrouter();
var router = this._router;
// get router
var router = this.router;
fns.forEach(function (fn) {
// non-express app
@ -235,8 +230,8 @@ app.use = function use(fn) {
router.use(path, function mounted_app(req, res, next) {
var orig = req.app;
fn.handle(req, res, function (err) {
setPrototypeOf(req, orig.request)
setPrototypeOf(res, orig.response)
Object.setPrototypeOf(req, orig.request)
Object.setPrototypeOf(res, orig.response)
next(err);
});
});
@ -259,8 +254,7 @@ app.use = function use(fn) {
*/
app.route = function route(path) {
this.lazyrouter();
return this._router.route(path);
return this.router.route(path);
};
/**
@ -326,8 +320,6 @@ app.engine = function engine(ext, fn) {
*/
app.param = function param(name, fn) {
this.lazyrouter();
if (Array.isArray(name)) {
for (var i = 0; i < name.length; i++) {
this.param(name[i], fn);
@ -336,7 +328,7 @@ app.param = function param(name, fn) {
return this;
}
this._router.param(name, fn);
this.router.param(name, fn);
return this;
};
@ -359,17 +351,7 @@ app.param = function param(name, fn) {
app.set = function set(setting, val) {
if (arguments.length === 1) {
// app.get(setting)
var settings = this.settings
while (settings && settings !== Object.prototype) {
if (hasOwnProperty.call(settings, setting)) {
return settings[setting]
}
settings = Object.getPrototypeOf(settings)
}
return undefined
return this.settings[setting];
}
debug('set "%s" to %o', setting, val);
@ -486,16 +468,14 @@ app.disable = function disable(setting) {
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
*/
methods.forEach(function(method){
app[method] = function(path){
methods.forEach(function (method) {
app[method] = function (path) {
if (method === 'get' && arguments.length === 1) {
// app.get(setting)
return this.set(path);
}
this.lazyrouter();
var route = this._router.route(path);
var route = this.route(path);
route[method].apply(route, slice.call(arguments, 1));
return this;
};
@ -512,9 +492,7 @@ methods.forEach(function(method){
*/
app.all = function all(path) {
this.lazyrouter();
var route = this._router.route(path);
var route = this.route(path);
var args = slice.call(arguments, 1);
for (var i = 0; i < methods.length; i++) {
@ -524,10 +502,6 @@ app.all = function all(path) {
return this;
};
// del -> delete alias
app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');
/**
* Render the given view `name` name with `options`
* and a callback accepting an error and the
@ -550,7 +524,6 @@ app.render = function render(name, options, callback) {
var done = callback;
var engines = this.engines;
var opts = options;
var renderOptions = {};
var view;
// support callback function as second arg
@ -559,16 +532,8 @@ app.render = function render(name, options, callback) {
opts = {};
}
// merge app.locals
merge(renderOptions, this.locals);
// merge options._locals
if (opts._locals) {
merge(renderOptions, opts._locals);
}
// merge options
merge(renderOptions, opts);
var renderOptions = { ...this.locals, ...opts._locals, ...opts };
// set .cache unless explicitly provided
if (renderOptions.cache == null) {
@ -618,8 +583,8 @@ app.render = function render(name, options, callback) {
* and HTTPS server you may do so with the "http"
* and "https" modules as shown here:
*
* var http = require('http')
* , https = require('https')
* var http = require('node:http')
* , https = require('node:https')
* , express = require('express')
* , app = express();
*
@ -631,9 +596,14 @@ app.render = function render(name, options, callback) {
*/
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
var server = http.createServer(this)
var args = Array.prototype.slice.call(arguments)
if (typeof args[args.length - 1] === 'function') {
var done = args[args.length - 1] = once(args[args.length - 1])
server.once('error', done)
}
return server.listen.apply(server, args)
}
/**
* Log error using console.error.

View File

@ -13,11 +13,10 @@
*/
var bodyParser = require('body-parser')
var EventEmitter = require('events').EventEmitter;
var EventEmitter = require('node:events').EventEmitter;
var mixin = require('merge-descriptors');
var proto = require('./application');
var Route = require('./router/route');
var Router = require('./router');
var Router = require('router');
var req = require('./request');
var res = require('./response');
@ -68,7 +67,7 @@ exports.response = res;
* Expose constructors.
*/
exports.Route = Route;
exports.Route = Router.Route;
exports.Router = Router;
/**
@ -76,41 +75,7 @@ exports.Router = Router;
*/
exports.json = bodyParser.json
exports.query = require('./middleware/query');
exports.raw = bodyParser.raw
exports.static = require('serve-static');
exports.text = bodyParser.text
exports.urlencoded = bodyParser.urlencoded
/**
* Replace removed middleware with an appropriate error message.
*/
var removedMiddlewares = [
'bodyParser',
'compress',
'cookieSession',
'session',
'logger',
'cookieParser',
'favicon',
'responseTime',
'errorHandler',
'timeout',
'methodOverride',
'vhost',
'csrf',
'directory',
'limit',
'multipart',
'staticCache'
]
removedMiddlewares.forEach(function (name) {
Object.defineProperty(exports, name, {
get: function () {
throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
},
configurable: true
});
});

View File

@ -1,43 +0,0 @@
/*!
* express
* Copyright(c) 2009-2013 TJ Holowaychuk
* Copyright(c) 2013 Roman Shtylman
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var setPrototypeOf = require('setprototypeof')
/**
* Initialization middleware, exposing the
* request and response to each other, as well
* as defaulting the X-Powered-By header field.
*
* @param {Function} app
* @return {Function}
* @api private
*/
exports.init = function(app){
return function expressInit(req, res, next){
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
req.res = res;
res.req = req;
req.next = next;
setPrototypeOf(req, app.request)
setPrototypeOf(res, app.response)
res.locals = res.locals || Object.create(null);
next();
};
};

View File

@ -1,47 +0,0 @@
/*!
* express
* Copyright(c) 2009-2013 TJ Holowaychuk
* Copyright(c) 2013 Roman Shtylman
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var merge = require('utils-merge')
var parseUrl = require('parseurl');
var qs = require('qs');
/**
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function query(options) {
var opts = merge({}, options)
var queryparse = qs.parse;
if (typeof options === 'function') {
queryparse = options;
opts = undefined;
}
if (opts !== undefined && opts.allowPrototypes === undefined) {
// back-compat for qs module
opts.allowPrototypes = true;
}
return function query(req, res, next){
if (!req.query) {
var val = parseUrl(req).query;
req.query = queryparse(val, opts);
}
next();
};
};

View File

@ -14,10 +14,9 @@
*/
var accepts = require('accepts');
var deprecate = require('depd')('express');
var isIP = require('net').isIP;
var isIP = require('node:net').isIP;
var typeis = require('type-is');
var http = require('http');
var http = require('node:http');
var fresh = require('fresh');
var parseRange = require('range-parser');
var parse = require('parseurl');
@ -147,9 +146,6 @@ req.acceptsEncodings = function(){
return accept.encodings.apply(accept, arguments);
};
req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
'req.acceptsEncoding: Use acceptsEncodings instead');
/**
* Check if the given `charset`s are acceptable,
* otherwise you should respond with 406 "Not Acceptable".
@ -164,9 +160,6 @@ req.acceptsCharsets = function(){
return accept.charsets.apply(accept, arguments);
};
req.acceptsCharset = deprecate.function(req.acceptsCharsets,
'req.acceptsCharset: Use acceptsCharsets instead');
/**
* Check if the given `lang`s are acceptable,
* otherwise you should respond with 406 "Not Acceptable".
@ -181,9 +174,6 @@ req.acceptsLanguages = function(){
return accept.languages.apply(accept, arguments);
};
req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
'req.acceptsLanguage: Use acceptsLanguages instead');
/**
* Parse Range header field, capping to the given `size`.
*
@ -216,38 +206,27 @@ req.range = function range(size, options) {
};
/**
* Return the value of param `name` when present or `defaultValue`.
* Parse the query string of `req.url`.
*
* - Checks route placeholders, ex: _/user/:id_
* - Checks body params, ex: id=12, {"id":12}
* - Checks query string params, ex: ?id=12
* This uses the "query parser" setting to parse the raw
* string into an object.
*
* To utilize request bodies, `req.body`
* should be an object. This can be done by using
* the `bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} [defaultValue]
* @return {String}
* @public
* @api public
*/
req.param = function param(name, defaultValue) {
var params = this.params || {};
var body = this.body || {};
var query = this.query || {};
defineGetter(req, 'query', function query(){
var queryparse = this.app.get('query parser fn');
var args = arguments.length === 1
? 'name'
: 'name, default';
deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead');
if (!queryparse) {
// parsing is disabled
return Object.create(null);
}
if (null != params[name] && params.hasOwnProperty(name)) return params[name];
if (null != body[name]) return body[name];
if (null != query[name]) return query[name];
var querystring = parse(this).query;
return defaultValue;
};
return queryparse(querystring);
});
/**
* Check if the incoming request contains the "Content-Type"
@ -414,7 +393,7 @@ defineGetter(req, 'path', function path() {
});
/**
* Parse the "Host" header field to a hostname.
* Parse the "Host" header field to a host.
*
* When the "trust proxy" setting trusts the socket
* address, the "X-Forwarded-Host" header field will
@ -424,18 +403,35 @@ defineGetter(req, 'path', function path() {
* @public
*/
defineGetter(req, 'hostname', function hostname(){
defineGetter(req, 'host', function host(){
var trust = this.app.get('trust proxy fn');
var host = this.get('X-Forwarded-Host');
var val = this.get('X-Forwarded-Host');
if (!host || !trust(this.connection.remoteAddress, 0)) {
host = this.get('Host');
} else if (host.indexOf(',') !== -1) {
if (!val || !trust(this.connection.remoteAddress, 0)) {
val = this.get('Host');
} else if (val.indexOf(',') !== -1) {
// Note: X-Forwarded-Host is normally only ever a
// single value, but this is to be safe.
host = host.substring(0, host.indexOf(',')).trimRight()
val = val.substring(0, val.indexOf(',')).trimRight()
}
return val || undefined;
});
/**
* Parse the "Host" header field to a hostname.
*
* When the "trust proxy" setting trusts the socket
* address, the "X-Forwarded-Host" header field will
* be trusted.
*
* @return {String}
* @api public
*/
defineGetter(req, 'hostname', function hostname(){
var host = this.host;
if (!host) return;
// IPv6 literal support
@ -449,15 +445,9 @@ defineGetter(req, 'hostname', function hostname(){
: host;
});
// TODO: change req.host to return host in next major
defineGetter(req, 'host', deprecate.function(function host(){
return this.hostname;
}, 'req.host: Use req.hostname instead'));
/**
* Check if the request is fresh, aka
* Last-Modified and/or the ETag
* Last-Modified or the ETag
* still match.
*
* @return {Boolean}

View File

@ -12,18 +12,16 @@
* @private
*/
var Buffer = require('safe-buffer').Buffer
var contentDisposition = require('content-disposition');
var createError = require('http-errors')
var deprecate = require('depd')('express');
var encodeUrl = require('encodeurl');
var escapeHtml = require('escape-html');
var http = require('http');
var isAbsolute = require('./utils').isAbsolute;
var http = require('node:http');
var onFinished = require('on-finished');
var path = require('path');
var mime = require('mime-types')
var path = require('node:path');
var pathIsAbsolute = require('node:path').isAbsolute;
var statuses = require('statuses')
var merge = require('utils-merge');
var sign = require('cookie-signature').sign;
var normalizeType = require('./utils').normalizeType;
var normalizeTypes = require('./utils').normalizeTypes;
@ -31,7 +29,6 @@ var setCharset = require('./utils').setCharset;
var cookie = require('cookie');
var send = require('send');
var extname = path.extname;
var mime = send.mime;
var resolve = path.resolve;
var vary = require('vary');
@ -50,24 +47,28 @@ var res = Object.create(http.ServerResponse.prototype)
module.exports = res
/**
* Module variables.
* @private
*/
var charsetRegExp = /;\s*charset\s*=/;
/**
* Set status `code`.
* Set the HTTP status code for the response.
*
* @param {Number} code
* @return {ServerResponse}
* Expects an integer value between 100 and 999 inclusive.
* Throws an error if the provided status code is not an integer or if it's outside the allowable range.
*
* @param {number} code - The HTTP status code to set.
* @return {ServerResponse} - Returns itself for chaining methods.
* @throws {TypeError} If `code` is not an integer.
* @throws {RangeError} If `code` is outside the range 100 to 999.
* @public
*/
res.status = function status(code) {
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')
// Check if the status code is not an integer
if (!Number.isInteger(code)) {
throw new TypeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be an integer.`);
}
// Check if the status code is outside of Node's valid range
if (code < 100 || code > 999) {
throw new RangeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be greater than 99 and less than 1000.`);
}
this.statusCode = code;
return this;
};
@ -79,7 +80,11 @@ res.status = function status(code) {
*
* res.links({
* next: 'http://api.example.com/users?page=2',
* last: 'http://api.example.com/users?page=5'
* last: 'http://api.example.com/users?page=5',
* pages: [
* 'http://api.example.com/users?page=1',
* 'http://api.example.com/users?page=2'
* ]
* });
*
* @param {Object} links
@ -87,11 +92,18 @@ res.status = function status(code) {
* @public
*/
res.links = function(links){
res.links = function(links) {
var link = this.get('Link') || '';
if (link) link += ', ';
return this.set('Link', link + Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
return this.set('Link', link + Object.keys(links).map(function(rel) {
// Allow multiple links if links[rel] is an array
if (Array.isArray(links[rel])) {
return links[rel].map(function (singleLink) {
return `<${singleLink}>; rel="${rel}"`;
}).join(', ');
} else {
return `<${links[rel]}>; rel="${rel}"`;
}
}).join(', '));
};
@ -117,31 +129,6 @@ res.send = function send(body) {
// settings
var app = this.app;
// allow status / body
if (arguments.length === 2) {
// res.send(body, status) backwards compat
if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
deprecate('res.send(body, status): Use res.status(status).send(body) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.send(status, body): Use res.status(status).send(body) instead');
this.statusCode = arguments[0];
chunk = arguments[1];
}
}
// disambiguate res.send(status) and res.send(status, num)
if (typeof chunk === 'number' && arguments.length === 1) {
// res.send(status) will set status message as text string
if (!this.get('Content-Type')) {
this.type('txt');
}
deprecate('res.send(status): Use res.sendStatus(status) instead');
this.statusCode = chunk;
chunk = statuses.message[chunk]
}
switch (typeof chunk) {
// string defaulting to html
case 'string':
@ -154,7 +141,7 @@ res.send = function send(body) {
case 'object':
if (chunk === null) {
chunk = '';
} else if (Buffer.isBuffer(chunk)) {
} else if (ArrayBuffer.isView(chunk)) {
if (!this.get('Content-Type')) {
this.type('bin');
}
@ -207,7 +194,7 @@ res.send = function send(body) {
}
// freshness
if (req.fresh) this.statusCode = 304;
if (req.fresh) this.status(304);
// strip irrelevant headers
if (204 === this.statusCode || 304 === this.statusCode) {
@ -248,27 +235,12 @@ res.send = function send(body) {
*/
res.json = function json(obj) {
var val = obj;
// allow status / body
if (arguments.length === 2) {
// res.json(body, status) backwards compat
if (typeof arguments[1] === 'number') {
deprecate('res.json(obj, status): Use res.status(status).json(obj) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.json(status, obj): Use res.status(status).json(obj) instead');
this.statusCode = arguments[0];
val = arguments[1];
}
}
// settings
var app = this.app;
var escape = app.get('json escape')
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = stringify(val, replacer, spaces, escape)
var body = stringify(obj, replacer, spaces, escape)
// content-type
if (!this.get('Content-Type')) {
@ -291,27 +263,12 @@ res.json = function json(obj) {
*/
res.jsonp = function jsonp(obj) {
var val = obj;
// allow status / body
if (arguments.length === 2) {
// res.jsonp(body, status) backwards compat
if (typeof arguments[1] === 'number') {
deprecate('res.jsonp(obj, status): Use res.status(status).jsonp(obj) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead');
this.statusCode = arguments[0];
val = arguments[1];
}
}
// settings
var app = this.app;
var escape = app.get('json escape')
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = stringify(val, replacer, spaces, escape)
var body = stringify(obj, replacer, spaces, escape)
var callback = this.req.query[app.get('jsonp callback name')];
// content-type
@ -369,7 +326,7 @@ res.jsonp = function jsonp(obj) {
res.sendStatus = function sendStatus(statusCode) {
var body = statuses.message[statusCode] || String(statusCode)
this.statusCode = statusCode;
this.status(statusCode);
this.type('txt');
return this.send(body);
@ -437,12 +394,15 @@ res.sendFile = function sendFile(path, options, callback) {
opts = {};
}
if (!opts.root && !isAbsolute(path)) {
if (!opts.root && !pathIsAbsolute(path)) {
throw new TypeError('path must be absolute or specify root to res.sendFile');
}
// create file stream
var pathname = encodeURI(path);
// wire application etag option to send
opts.etag = this.app.enabled('etag');
var file = send(req, pathname, opts);
// transfer
@ -457,78 +417,6 @@ res.sendFile = function sendFile(path, options, callback) {
});
};
/**
* Transfer the file at the given `path`.
*
* Automatically sets the _Content-Type_ response header field.
* The callback `callback(err)` is invoked when the transfer is complete
* or when an error occurs. Be sure to check `res.headersSent`
* if you wish to attempt responding, as the header and some data
* may have already been transferred.
*
* Options:
*
* - `maxAge` defaulting to 0 (can be string converted by `ms`)
* - `root` root directory for relative filenames
* - `headers` object of headers to serve with file
* - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
*
* Other options are passed along to `send`.
*
* Examples:
*
* The following example illustrates how `res.sendfile()` may
* be used as an alternative for the `static()` middleware for
* dynamic situations. The code backing `res.sendfile()` is actually
* the same code, so HTTP cache support etc is identical.
*
* app.get('/user/:uid/photos/:file', function(req, res){
* var uid = req.params.uid
* , file = req.params.file;
*
* req.user.mayViewFilesFrom(uid, function(yes){
* if (yes) {
* res.sendfile('/uploads/' + uid + '/' + file);
* } else {
* res.send(403, 'Sorry! you cant see that.');
* }
* });
* });
*
* @public
*/
res.sendfile = function (path, options, callback) {
var done = callback;
var req = this.req;
var res = this;
var next = req.next;
var opts = options || {};
// support function as second arg
if (typeof options === 'function') {
done = options;
opts = {};
}
// create file stream
var file = send(req, path, opts);
// transfer
sendfile(res, file, opts, function (err) {
if (done) return done(err);
if (err && err.code === 'EISDIR') return next();
// next() all but write errors
if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
next(err);
}
});
};
res.sendfile = deprecate.function(res.sendfile,
'res.sendfile: Use res.sendFile instead');
/**
* Transfer the file at the given `path` as an attachment.
*
@ -599,8 +487,10 @@ res.download = function download (path, filename, options, callback) {
};
/**
* Set _Content-Type_ response header with `type` through `mime.lookup()`
* Set _Content-Type_ response header with `type` through `mime.contentType()`
* when it does not contain "/", or set the Content-Type to `type` otherwise.
* When no mapping is found though `mime.contentType()`, the type is set to
* "application/octet-stream".
*
* Examples:
*
@ -618,7 +508,7 @@ res.download = function download (path, filename, options, callback) {
res.contentType =
res.type = function contentType(type) {
var ct = type.indexOf('/') === -1
? mime.lookup(type)
? (mime.contentType(type) || 'application/octet-stream')
: type;
return this.set('Content-Type', ct);
@ -767,6 +657,9 @@ res.append = function append(field, val) {
*
* Aliased as `res.header()`.
*
* When the set header is "Content-Type", the type is expanded to include
* the charset if not present using `mime.contentType()`.
*
* @param {String|Object} field
* @param {String|Array} val
* @return {ServerResponse} for chaining
@ -785,10 +678,7 @@ res.header = function header(field, val) {
if (Array.isArray(value)) {
throw new TypeError('Content-Type cannot be set to an Array');
}
if (!charsetRegExp.test(value)) {
var charset = mime.charsets.lookup(value.split(';')[0]);
if (charset) value += '; charset=' + charset.toLowerCase();
}
value = mime.contentType(value)
}
this.setHeader(field, value);
@ -822,15 +712,10 @@ 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);
// Force cookie expiration by setting expires to the past
const opts = { path: '/', ...options, expires: new Date(1)};
// ensure maxAge is not passed
delete opts.maxAge
return this.cookie(name, '', opts);
};
@ -860,7 +745,7 @@ res.clearCookie = function clearCookie(name, options) {
*/
res.cookie = function (name, value, options) {
var opts = merge({}, options);
var opts = { ...options };
var secret = this.req.secret;
var signed = opts.signed;
@ -912,26 +797,13 @@ res.cookie = function (name, value, options) {
*/
res.location = function location(url) {
var loc;
// "back" is an alias for the referrer
if (url === 'back') {
loc = this.req.get('Referrer') || '/';
} else {
loc = String(url);
}
return this.set('Location', encodeUrl(loc));
return this.set('Location', encodeUrl(url));
};
/**
* Redirect to the given `url` with optional response `status`
* defaulting to 302.
*
* The resulting `url` is determined by `res.location()`, so
* it will play nicely with mounted apps, relative paths,
* `"back"` etc.
*
* Examples:
*
* res.redirect('/foo/bar');
@ -949,13 +821,8 @@ res.redirect = function redirect(url) {
// allow status / url
if (arguments.length === 2) {
if (typeof arguments[0] === 'number') {
status = arguments[0];
address = arguments[1];
} else {
deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
status = arguments[1];
}
status = arguments[0]
address = arguments[1]
}
// Set location header
@ -969,7 +836,7 @@ res.redirect = function redirect(url) {
html: function(){
var u = escapeHtml(address);
body = '<p>' + statuses.message[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
body = '<p>' + statuses.message[status] + '. Redirecting to ' + u + '</p>'
},
default: function(){
@ -978,7 +845,7 @@ res.redirect = function redirect(url) {
});
// Respond
this.statusCode = status;
this.status(status);
this.set('Content-Length', Buffer.byteLength(body));
if (this.req.method === 'HEAD') {
@ -998,12 +865,6 @@ res.redirect = function redirect(url) {
*/
res.vary = function(field){
// checks for back-compat
if (!field || (Array.isArray(field) && !field.length)) {
deprecate('res.vary(): Provide a field name');
return this;
}
vary(this, field);
return this;

View File

@ -1,673 +0,0 @@
/*!
* express
* Copyright(c) 2009-2013 TJ Holowaychuk
* Copyright(c) 2013 Roman Shtylman
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var Route = require('./route');
var Layer = require('./layer');
var methods = require('methods');
var mixin = require('utils-merge');
var debug = require('debug')('express:router');
var deprecate = require('depd')('express');
var flatten = require('array-flatten');
var parseUrl = require('parseurl');
var setPrototypeOf = require('setprototypeof')
/**
* Module variables.
* @private
*/
var objectRegExp = /^\[object (\S+)\]$/;
var slice = Array.prototype.slice;
var toString = Object.prototype.toString;
/**
* Initialize a new `Router` with the given `options`.
*
* @param {Object} [options]
* @return {Router} which is a callable function
* @public
*/
var proto = module.exports = function(options) {
var opts = options || {};
function router(req, res, next) {
router.handle(req, res, next);
}
// mixin Router class functions
setPrototypeOf(router, proto)
router.params = {};
router._params = [];
router.caseSensitive = opts.caseSensitive;
router.mergeParams = opts.mergeParams;
router.strict = opts.strict;
router.stack = [];
return router;
};
/**
* Map the given param placeholder `name`(s) to the given callback.
*
* Parameter mapping is used to provide pre-conditions to routes
* which use normalized placeholders. For example a _:user_id_ parameter
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
*
* Just like in middleware, you must either respond to the request or call next
* to avoid stalling the request.
*
* app.param('user_id', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* return next(err);
* } else if (!user) {
* return next(new Error('failed to load user'));
* }
* req.user = user;
* next();
* });
* });
*
* @param {String} name
* @param {Function} fn
* @return {app} for chaining
* @public
*/
proto.param = function param(name, fn) {
// param logic
if (typeof name === 'function') {
deprecate('router.param(fn): Refactor to use path params');
this._params.push(name);
return;
}
// apply param functions
var params = this._params;
var len = params.length;
var ret;
if (name[0] === ':') {
deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.slice(1)) + ', fn) instead')
name = name.slice(1)
}
for (var i = 0; i < len; ++i) {
if (ret = params[i](name, fn)) {
fn = ret;
}
}
// ensure we end up with a
// middleware function
if ('function' !== typeof fn) {
throw new Error('invalid param() call for ' + name + ', got ' + fn);
}
(this.params[name] = this.params[name] || []).push(fn);
return this;
};
/**
* Dispatch a req, res into the router.
* @private
*/
proto.handle = function handle(req, res, out) {
var self = this;
debug('dispatching %s %s', req.method, req.url);
var idx = 0;
var protohost = getProtohost(req.url) || ''
var removed = '';
var slashAdded = false;
var sync = 0
var paramcalled = {};
// store options for OPTIONS request
// only used if OPTIONS request
var options = [];
// middleware and routes
var stack = self.stack;
// manage inter-router variables
var parentParams = req.params;
var parentUrl = req.baseUrl || '';
var done = restore(out, req, 'baseUrl', 'next', 'params');
// setup next layer
req.next = next;
// for options requests, respond with a default if nothing else responds
if (req.method === 'OPTIONS') {
done = wrap(done, function(old, err) {
if (err || options.length === 0) return old(err);
sendOptionsResponse(res, options, old);
});
}
// setup basic req values
req.baseUrl = parentUrl;
req.originalUrl = req.originalUrl || req.url;
next();
function next(err) {
var layerError = err === 'route'
? null
: err;
// remove added slash
if (slashAdded) {
req.url = req.url.slice(1)
slashAdded = false;
}
// restore altered req.url
if (removed.length !== 0) {
req.baseUrl = parentUrl;
req.url = protohost + removed + req.url.slice(protohost.length)
removed = '';
}
// signal to exit router
if (layerError === 'router') {
setImmediate(done, null)
return
}
// no more matching layers
if (idx >= stack.length) {
setImmediate(done, layerError);
return;
}
// max sync stack
if (++sync > 100) {
return setImmediate(next, err)
}
// get pathname of request
var path = getPathname(req);
if (path == null) {
return done(layerError);
}
// find next matching layer
var layer;
var match;
var route;
while (match !== true && idx < stack.length) {
layer = stack[idx++];
match = matchLayer(layer, path);
route = layer.route;
if (typeof match !== 'boolean') {
// hold on to layerError
layerError = layerError || match;
}
if (match !== true) {
continue;
}
if (!route) {
// process non-route handlers normally
continue;
}
if (layerError) {
// routes do not match with a pending error
match = false;
continue;
}
var method = req.method;
var has_method = route._handles_method(method);
// build up automatic options response
if (!has_method && method === 'OPTIONS') {
appendMethods(options, route._options());
}
// don't even bother matching route
if (!has_method && method !== 'HEAD') {
match = false;
}
}
// no match
if (match !== true) {
return done(layerError);
}
// store route for dispatch on change
if (route) {
req.route = route;
}
// Capture one-time layer values
req.params = self.mergeParams
? mergeParams(layer.params, parentParams)
: layer.params;
var layerPath = layer.path;
// this should be done for the layer
self.process_params(layer, paramcalled, req, res, function (err) {
if (err) {
next(layerError || err)
} else if (route) {
layer.handle_request(req, res, next)
} else {
trim_prefix(layer, layerError, layerPath, path)
}
sync = 0
});
}
function trim_prefix(layer, layerError, layerPath, path) {
if (layerPath.length !== 0) {
// Validate path is a prefix match
if (layerPath !== path.slice(0, layerPath.length)) {
next(layerError)
return
}
// Validate path breaks on a path separator
var c = path[layerPath.length]
if (c && c !== '/' && c !== '.') return next(layerError)
// Trim off the part of the url that matches the route
// middleware (.use stuff) needs to have the path stripped
debug('trim prefix (%s) from url %s', layerPath, req.url);
removed = layerPath;
req.url = protohost + req.url.slice(protohost.length + removed.length)
// Ensure leading slash
if (!protohost && req.url[0] !== '/') {
req.url = '/' + req.url;
slashAdded = true;
}
// Setup base URL (no trailing slash)
req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
? removed.substring(0, removed.length - 1)
: removed);
}
debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
if (layerError) {
layer.handle_error(layerError, req, res, next);
} else {
layer.handle_request(req, res, next);
}
}
};
/**
* Process any parameters for the layer.
* @private
*/
proto.process_params = function process_params(layer, called, req, res, done) {
var params = this.params;
// captured parameters from the layer, keys and values
var keys = layer.keys;
// fast track
if (!keys || keys.length === 0) {
return done();
}
var i = 0;
var name;
var paramIndex = 0;
var key;
var paramVal;
var paramCallbacks;
var paramCalled;
// process params in order
// param callbacks can be async
function param(err) {
if (err) {
return done(err);
}
if (i >= keys.length ) {
return done();
}
paramIndex = 0;
key = keys[i++];
name = key.name;
paramVal = req.params[name];
paramCallbacks = params[name];
paramCalled = called[name];
if (paramVal === undefined || !paramCallbacks) {
return param();
}
// param previously called with same value or error occurred
if (paramCalled && (paramCalled.match === paramVal
|| (paramCalled.error && paramCalled.error !== 'route'))) {
// restore value
req.params[name] = paramCalled.value;
// next param
return param(paramCalled.error);
}
called[name] = paramCalled = {
error: null,
match: paramVal,
value: paramVal
};
paramCallback();
}
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
// store updated value
paramCalled.value = req.params[key.name];
if (err) {
// store error
paramCalled.error = err;
param(err);
return;
}
if (!fn) return param();
try {
fn(req, res, paramCallback, paramVal, key.name);
} catch (e) {
paramCallback(e);
}
}
param();
};
/**
* Use the given middleware function, with optional path, defaulting to "/".
*
* Use (like `.all`) will run for any http METHOD, but it will not add
* handlers for those methods so OPTIONS requests will not consider `.use`
* functions even if they could respond.
*
* The other difference is that _route_ path is stripped and not visible
* to the handler function. The main effect of this feature is that mounted
* handlers can operate without any code changes regardless of the "prefix"
* pathname.
*
* @public
*/
proto.use = function use(fn) {
var offset = 0;
var path = '/';
// default path to '/'
// disambiguate router.use([fn])
if (typeof fn !== 'function') {
var arg = fn;
while (Array.isArray(arg) && arg.length !== 0) {
arg = arg[0];
}
// first arg is the path
if (typeof arg !== 'function') {
offset = 1;
path = fn;
}
}
var callbacks = flatten(slice.call(arguments, offset));
if (callbacks.length === 0) {
throw new TypeError('Router.use() requires a middleware function')
}
for (var i = 0; i < callbacks.length; i++) {
var fn = callbacks[i];
if (typeof fn !== 'function') {
throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))
}
// add the middleware
debug('use %o %s', path, fn.name || '<anonymous>')
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: false,
end: false
}, fn);
layer.route = undefined;
this.stack.push(layer);
}
return this;
};
/**
* Create a new Route for the given path.
*
* Each route contains a separate middleware stack and VERB handlers.
*
* See the Route api documentation for details on adding handlers
* and middleware to routes.
*
* @param {String} path
* @return {Route}
* @public
*/
proto.route = function route(path) {
var route = new Route(path);
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
};
// create Router#VERB functions
methods.concat('all').forEach(function(method){
proto[method] = function(path){
var route = this.route(path)
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});
// append methods to a list of methods
function appendMethods(list, addition) {
for (var i = 0; i < addition.length; i++) {
var method = addition[i];
if (list.indexOf(method) === -1) {
list.push(method);
}
}
}
// get pathname of request
function getPathname(req) {
try {
return parseUrl(req).pathname;
} catch (err) {
return undefined;
}
}
// Get get protocol + host for a URL
function getProtohost(url) {
if (typeof url !== 'string' || url.length === 0 || url[0] === '/') {
return undefined
}
var searchIndex = url.indexOf('?')
var pathLength = searchIndex !== -1
? searchIndex
: url.length
var fqdnIndex = url.slice(0, pathLength).indexOf('://')
return fqdnIndex !== -1
? url.substring(0, url.indexOf('/', 3 + fqdnIndex))
: undefined
}
// get type for error message
function gettype(obj) {
var type = typeof obj;
if (type !== 'object') {
return type;
}
// inspect [[Class]] for objects
return toString.call(obj)
.replace(objectRegExp, '$1');
}
/**
* Match path to a layer.
*
* @param {Layer} layer
* @param {string} path
* @private
*/
function matchLayer(layer, path) {
try {
return layer.match(path);
} catch (err) {
return err;
}
}
// merge params with parent params
function mergeParams(params, parent) {
if (typeof parent !== 'object' || !parent) {
return params;
}
// make copy of parent for base
var obj = mixin({}, parent);
// simple non-numeric merging
if (!(0 in params) || !(0 in parent)) {
return mixin(obj, params);
}
var i = 0;
var o = 0;
// determine numeric gaps
while (i in params) {
i++;
}
while (o in parent) {
o++;
}
// offset numeric indices in params before merge
for (i--; i >= 0; i--) {
params[i + o] = params[i];
// create holes for the merge when necessary
if (i < o) {
delete params[i];
}
}
return mixin(obj, params);
}
// restore obj props after function
function restore(fn, obj) {
var props = new Array(arguments.length - 2);
var vals = new Array(arguments.length - 2);
for (var i = 0; i < props.length; i++) {
props[i] = arguments[i + 2];
vals[i] = obj[props[i]];
}
return function () {
// restore vals
for (var i = 0; i < props.length; i++) {
obj[props[i]] = vals[i];
}
return fn.apply(this, arguments);
};
}
// send an OPTIONS response
function sendOptionsResponse(res, options, next) {
try {
var body = options.join(',');
res.set('Allow', body);
res.send(body);
} catch (err) {
next(err);
}
}
// wrap a function
function wrap(old, fn) {
return function proxy() {
var args = new Array(arguments.length + 1);
args[0] = old;
for (var i = 0, len = arguments.length; i < len; i++) {
args[i + 1] = arguments[i];
}
fn.apply(this, args);
};
}

View File

@ -1,181 +0,0 @@
/*!
* express
* Copyright(c) 2009-2013 TJ Holowaychuk
* Copyright(c) 2013 Roman Shtylman
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var pathRegexp = require('path-to-regexp');
var debug = require('debug')('express:router:layer');
/**
* Module variables.
* @private
*/
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* Module exports.
* @public
*/
module.exports = Layer;
function Layer(path, options, fn) {
if (!(this instanceof Layer)) {
return new Layer(path, options, fn);
}
debug('new %o', path)
var opts = options || {};
this.handle = fn;
this.name = fn.name || '<anonymous>';
this.params = undefined;
this.path = undefined;
this.regexp = pathRegexp(path, this.keys = [], opts);
// set fast path flags
this.regexp.fast_star = path === '*'
this.regexp.fast_slash = path === '/' && opts.end === false
}
/**
* Handle the error for the layer.
*
* @param {Error} error
* @param {Request} req
* @param {Response} res
* @param {function} next
* @api private
*/
Layer.prototype.handle_error = function handle_error(error, req, res, next) {
var fn = this.handle;
if (fn.length !== 4) {
// not a standard error handler
return next(error);
}
try {
fn(error, req, res, next);
} catch (err) {
next(err);
}
};
/**
* Handle the request for the layer.
*
* @param {Request} req
* @param {Response} res
* @param {function} next
* @api private
*/
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
/**
* Check if this route matches `path`, if so
* populate `.params`.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
Layer.prototype.match = function match(path) {
var match
if (path != null) {
// fast path non-ending match for / (any path matches)
if (this.regexp.fast_slash) {
this.params = {}
this.path = ''
return true
}
// fast path for * (everything matched in a param)
if (this.regexp.fast_star) {
this.params = {'0': decode_param(path)}
this.path = path
return true
}
// match the path
match = this.regexp.exec(path)
}
if (!match) {
this.params = undefined;
this.path = undefined;
return false;
}
// store values
this.params = {};
this.path = match[0]
var keys = this.keys;
var params = this.params;
for (var i = 1; i < match.length; i++) {
var key = keys[i - 1];
var prop = key.name;
var val = decode_param(match[i])
if (val !== undefined || !(hasOwnProperty.call(params, prop))) {
params[prop] = val;
}
}
return true;
};
/**
* Decode param value.
*
* @param {string} val
* @return {string}
* @private
*/
function decode_param(val) {
if (typeof val !== 'string' || val.length === 0) {
return val;
}
try {
return decodeURIComponent(val);
} catch (err) {
if (err instanceof URIError) {
err.message = 'Failed to decode param \'' + val + '\'';
err.status = err.statusCode = 400;
}
throw err;
}
}

View File

@ -1,230 +0,0 @@
/*!
* express
* Copyright(c) 2009-2013 TJ Holowaychuk
* Copyright(c) 2013 Roman Shtylman
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var debug = require('debug')('express:router:route');
var flatten = require('array-flatten');
var Layer = require('./layer');
var methods = require('methods');
/**
* Module variables.
* @private
*/
var slice = Array.prototype.slice;
var toString = Object.prototype.toString;
/**
* Module exports.
* @public
*/
module.exports = Route;
/**
* Initialize `Route` with the given `path`,
*
* @param {String} path
* @public
*/
function Route(path) {
this.path = path;
this.stack = [];
debug('new %o', path)
// route handlers for various http methods
this.methods = {};
}
/**
* Determine if the route handles a given method.
* @private
*/
Route.prototype._handles_method = function _handles_method(method) {
if (this.methods._all) {
return true;
}
// normalize name
var name = typeof method === 'string'
? method.toLowerCase()
: method
if (name === 'head' && !this.methods['head']) {
name = 'get';
}
return Boolean(this.methods[name]);
};
/**
* @return {Array} supported HTTP methods
* @private
*/
Route.prototype._options = function _options() {
var methods = Object.keys(this.methods);
// append automatic head
if (this.methods.get && !this.methods.head) {
methods.push('head');
}
for (var i = 0; i < methods.length; i++) {
// make upper case
methods[i] = methods[i].toUpperCase();
}
return methods;
};
/**
* dispatch req, res into this route
* @private
*/
Route.prototype.dispatch = function dispatch(req, res, done) {
var idx = 0;
var stack = this.stack;
var sync = 0
if (stack.length === 0) {
return done();
}
var method = typeof req.method === 'string'
? req.method.toLowerCase()
: req.method
if (method === 'head' && !this.methods['head']) {
method = 'get';
}
req.route = this;
next();
function next(err) {
// signal to exit route
if (err && err === 'route') {
return done();
}
// signal to exit router
if (err && err === 'router') {
return done(err)
}
// max sync stack
if (++sync > 100) {
return setImmediate(next, err)
}
var layer = stack[idx++]
// end of layers
if (!layer) {
return done(err)
}
if (layer.method && layer.method !== method) {
next(err)
} else if (err) {
layer.handle_error(err, req, res, next);
} else {
layer.handle_request(req, res, next);
}
sync = 0
}
};
/**
* Add a handler for all HTTP verbs to this route.
*
* Behaves just like middleware and can respond or call `next`
* to continue processing.
*
* You can use multiple `.all` call to add multiple handlers.
*
* function check_something(req, res, next){
* next();
* };
*
* function validate_user(req, res, next){
* next();
* };
*
* route
* .all(validate_user)
* .all(check_something)
* .get(function(req, res, next){
* res.send('hello world');
* });
*
* @param {function} handler
* @return {Route} for chaining
* @api public
*/
Route.prototype.all = function all() {
var handles = flatten(slice.call(arguments));
for (var i = 0; i < handles.length; i++) {
var handle = handles[i];
if (typeof handle !== 'function') {
var type = toString.call(handle);
var msg = 'Route.all() requires a callback function but got a ' + type
throw new TypeError(msg);
}
var layer = Layer('/', {}, handle);
layer.method = undefined;
this.methods._all = true;
this.stack.push(layer);
}
return this;
};
methods.forEach(function(method){
Route.prototype[method] = function(){
var handles = flatten(slice.call(arguments));
for (var i = 0; i < handles.length; i++) {
var handle = handles[i];
if (typeof handle !== 'function') {
var type = toString.call(handle);
var msg = 'Route.' + method + '() requires a callback function but got a ' + type
throw new Error(msg);
}
debug('%s %o', method, this.path)
var layer = Layer('/', {}, handle);
layer.method = method;
this.methods[method] = true;
this.stack.push(layer);
}
return this;
};
});

View File

@ -12,17 +12,20 @@
* @api private
*/
var Buffer = require('safe-buffer').Buffer
var contentDisposition = require('content-disposition');
var { METHODS } = require('node:http');
var contentType = require('content-type');
var deprecate = require('depd')('express');
var flatten = require('array-flatten');
var mime = require('send').mime;
var etag = require('etag');
var mime = require('mime-types')
var proxyaddr = require('proxy-addr');
var qs = require('qs');
var querystring = require('querystring');
/**
* A list of lowercased HTTP methods that are supported by Node.js.
* @api private
*/
exports.methods = METHODS.map((method) => method.toLowerCase());
/**
* Return strong ETag for `body`.
*
@ -45,31 +48,6 @@ exports.etag = createETagGenerator({ weak: false })
exports.wetag = createETagGenerator({ weak: true })
/**
* Check if `path` looks absolute.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
exports.isAbsolute = function(path){
if ('/' === path[0]) return true;
if (':' === path[1] && ('\\' === path[2] || '/' === path[2])) return true; // Windows device path
if ('\\\\' === path.substring(0, 2)) return true; // Microsoft Azure absolute path
};
/**
* Flatten the given `arr`.
*
* @param {Array} arr
* @return {Array}
* @api private
*/
exports.flatten = deprecate.function(flatten,
'utils.flatten: use array-flatten npm module instead');
/**
* Normalize the given `type`, for example "html" becomes "text/html".
*
@ -81,7 +59,7 @@ exports.flatten = deprecate.function(flatten,
exports.normalizeType = function(type){
return ~type.indexOf('/')
? acceptParams(type)
: { value: mime.lookup(type), params: {} };
: { value: (mime.lookup(type) || 'application/octet-stream'), params: {} }
};
/**
@ -92,27 +70,10 @@ exports.normalizeType = function(type){
* @api private
*/
exports.normalizeTypes = function(types){
var ret = [];
for (var i = 0; i < types.length; ++i) {
ret.push(exports.normalizeType(types[i]));
}
return ret;
exports.normalizeTypes = function(types) {
return types.map(exports.normalizeType);
};
/**
* Generate Content-Disposition header appropriate for the filename.
* non-ascii filenames are urlencoded and a filename* parameter is added
*
* @param {String} filename
* @return {String}
* @api private
*/
exports.contentDisposition = deprecate.function(contentDisposition,
'utils.contentDisposition: use content-disposition npm module instead');
/**
* Parse accept params `str` returning an
@ -124,16 +85,33 @@ exports.contentDisposition = deprecate.function(contentDisposition,
*/
function acceptParams (str) {
var parts = str.split(/ *; */);
var ret = { value: parts[0], quality: 1, params: {} }
var length = str.length;
var colonIndex = str.indexOf(';');
var index = colonIndex === -1 ? length : colonIndex;
var ret = { value: str.slice(0, index).trim(), quality: 1, params: {} };
for (var i = 1; i < parts.length; ++i) {
var pms = parts[i].split(/ *= */);
if ('q' === pms[0]) {
ret.quality = parseFloat(pms[1]);
} else {
ret.params[pms[0]] = pms[1];
while (index < length) {
var splitIndex = str.indexOf('=', index);
if (splitIndex === -1) break;
var colonIndex = str.indexOf(';', index);
var endIndex = colonIndex === -1 ? length : colonIndex;
if (splitIndex > endIndex) {
index = str.lastIndexOf(';', splitIndex - 1) + 1;
continue;
}
var key = str.slice(index, splitIndex).trim();
var value = str.slice(splitIndex + 1, endIndex).trim();
if (key === 'q') {
ret.quality = parseFloat(value);
} else {
ret.params[key] = value;
}
index = endIndex + 1;
}
return ret;
@ -192,7 +170,6 @@ exports.compileQueryParser = function compileQueryParser(val) {
fn = querystring.parse;
break;
case false:
fn = newObject;
break;
case 'extended':
fn = parseExtendedQueryString;
@ -290,14 +267,3 @@ function parseExtendedQueryString(str) {
allowPrototypes: true
});
}
/**
* Return new empty object.
*
* @return {Object}
* @api private
*/
function newObject() {
return {};
}

View File

@ -14,8 +14,8 @@
*/
var debug = require('debug')('express:view');
var path = require('path');
var fs = require('fs');
var path = require('node:path');
var fs = require('node:fs');
/**
* Module variables.
@ -131,8 +131,31 @@ View.prototype.lookup = function lookup(name) {
*/
View.prototype.render = function render(options, callback) {
var sync = true;
debug('render "%s"', this.path);
this.engine(this.path, options, callback);
// render, normalizing sync callbacks
this.engine(this.path, options, function onRender() {
if (!sync) {
return callback.apply(this, arguments);
}
// copy arguments
var args = new Array(arguments.length);
var cntx = this;
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
// force callback to be async
return process.nextTick(function renderTick() {
return callback.apply(cntx, args);
});
});
sync = false;
};
/**

View File

@ -1,7 +1,7 @@
{
"name": "express",
"description": "Fast, unopinionated, minimalist web framework",
"version": "4.19.2",
"version": "5.0.1",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
@ -14,7 +14,11 @@
],
"license": "MIT",
"repository": "expressjs/express",
"homepage": "http://expressjs.com/",
"homepage": "https://expressjs.com/",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
},
"keywords": [
"express",
"framework",
@ -28,58 +32,54 @@
"api"
],
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.8",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
"accepts": "^2.0.0",
"body-parser": "^2.1.0",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.0.0",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"merge-descriptors": "^2.0.0",
"mime-types": "^3.0.0",
"on-finished": "^2.4.1",
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.13.0",
"range-parser": "^1.2.1",
"router": "^2.1.0",
"send": "^1.1.0",
"serve-static": "^2.1.0",
"statuses": "^2.0.1",
"type-is": "^2.0.0",
"vary": "^1.1.2"
},
"devDependencies": {
"after": "0.8.2",
"connect-redis": "3.4.2",
"cookie-parser": "1.4.6",
"connect-redis": "^8.0.1",
"cookie-parser": "1.4.7",
"cookie-session": "2.0.0",
"ejs": "3.1.9",
"ejs": "^3.1.10",
"eslint": "8.47.0",
"express-session": "1.17.2",
"express-session": "^1.18.1",
"hbs": "4.2.0",
"marked": "0.7.0",
"marked": "^15.0.3",
"method-override": "3.0.0",
"mocha": "10.2.0",
"mocha": "^10.7.3",
"morgan": "1.10.0",
"nyc": "15.1.0",
"nyc": "^17.1.0",
"pbkdf2-password": "1.2.1",
"supertest": "6.3.0",
"supertest": "^6.3.0",
"vhost": "~3.0.2"
},
"engines": {
"node": ">= 0.10.0"
"node": ">= 18"
},
"files": [
"LICENSE",
@ -90,7 +90,7 @@
],
"scripts": {
"lint": "eslint .",
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
"test": "mocha --require test/support/env --reporter spec --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,10 +1,10 @@
'use strict'
var after = require('after');
var assert = require('assert')
var assert = require('node:assert')
var express = require('../')
, Route = express.Route
, methods = require('methods')
, methods = require('../lib/utils').methods
describe('Route', function(){
it('should work without handlers', function(done) {

View File

@ -3,11 +3,11 @@
var after = require('after');
var express = require('../')
, Router = express.Router
, methods = require('methods')
, assert = require('assert');
, methods = require('../lib/utils').methods
, assert = require('node:assert');
describe('Router', function(){
it('should return a function with router methods', function() {
describe('Router', function () {
it('should return a function with router methods', function () {
var router = new Router();
assert(typeof router === 'function')
@ -16,32 +16,32 @@ describe('Router', function(){
assert(typeof router.use === 'function')
});
it('should support .use of other routers', function(done){
it('should support .use of other routers', function (done) {
var router = new Router();
var another = new Router();
another.get('/bar', function(req, res){
another.get('/bar', function (req, res) {
res.end();
});
router.use('/foo', another);
router.handle({ url: '/foo/bar', method: 'GET' }, { end: done });
router.handle({ url: '/foo/bar', method: 'GET' }, { end: done }, function () { });
});
it('should support dynamic routes', function(done){
it('should support dynamic routes', function (done) {
var router = new Router();
var another = new Router();
another.get('/:bar', function(req, res){
another.get('/:bar', function (req, res) {
assert.strictEqual(req.params.bar, 'route')
res.end();
});
router.use('/:foo', another);
router.handle({ url: '/test/route', method: 'GET' }, { end: done });
router.handle({ url: '/test/route', method: 'GET' }, { end: done }, function () { });
});
it('should handle blank URL', function(done){
it('should handle blank URL', function (done) {
var router = new Router();
router.use(function (req, res) {
@ -88,10 +88,10 @@ describe('Router', function(){
})
})
it('should not stack overflow with many registered routes', function(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 handler = function (req, res) { res.end(new Error('wrong handler')) };
var router = new Router();
for (var i = 0; i < 6000; i++) {
@ -102,7 +102,7 @@ describe('Router', function(){
res.end();
});
router.handle({ url: '/', method: 'GET' }, { end: done });
router.handle({ url: '/', method: 'GET' }, { end: done }, function () { });
});
it('should not stack overflow with a large sync route stack', function (done) {
@ -127,7 +127,9 @@ describe('Router', function(){
res.end()
})
router.handle({ url: '/foo', method: 'GET' }, { end: done })
router.handle({ url: '/foo', method: 'GET' }, { end: done }, function (err) {
assert(!err, err);
});
})
it('should not stack overflow with a large sync middleware stack', function (done) {
@ -152,72 +154,74 @@ describe('Router', function(){
res.end()
})
router.handle({ url: '/', method: 'GET' }, { end: done })
router.handle({ url: '/', method: 'GET' }, { end: done }, function (err) {
assert(!err, err);
})
})
describe('.handle', function(){
it('should dispatch', function(done){
describe('.handle', function () {
it('should dispatch', function (done) {
var router = new Router();
router.route('/foo').get(function(req, res){
router.route('/foo').get(function (req, res) {
res.send('foo');
});
var res = {
send: function(val) {
send: function (val) {
assert.strictEqual(val, 'foo')
done();
}
}
router.handle({ url: '/foo', method: 'GET' }, res);
router.handle({ url: '/foo', method: 'GET' }, res, function () { });
})
})
describe('.multiple callbacks', function(){
it('should throw if a callback is null', function(){
describe('.multiple callbacks', function () {
it('should throw if a callback is null', function () {
assert.throws(function () {
var router = new Router();
router.route('/foo').all(null);
})
})
it('should throw if a callback is undefined', function(){
it('should throw if a callback is undefined', function () {
assert.throws(function () {
var router = new Router();
router.route('/foo').all(undefined);
})
})
it('should throw if a callback is not a function', function(){
it('should throw if a callback is not a function', function () {
assert.throws(function () {
var router = new Router();
router.route('/foo').all('not a function');
})
})
it('should not throw if all callbacks are functions', function(){
it('should not throw if all callbacks are functions', function () {
var router = new Router();
router.route('/foo').all(function(){}).all(function(){});
router.route('/foo').all(function () { }).all(function () { });
})
})
describe('error', function(){
it('should skip non error middleware', function(done){
describe('error', function () {
it('should skip non error middleware', function (done) {
var router = new Router();
router.get('/foo', function(req, res, next){
router.get('/foo', function (req, res, next) {
next(new Error('foo'));
});
router.get('/bar', function(req, res, next){
router.get('/bar', function (req, res, next) {
next(new Error('bar'));
});
router.use(function(req, res, next){
router.use(function (req, res, next) {
assert(false);
});
router.use(function(err, req, res, next){
router.use(function (err, req, res, next) {
assert.equal(err.message, 'foo');
done();
});
@ -225,59 +229,59 @@ describe('Router', function(){
router.handle({ url: '/foo', method: 'GET' }, {}, done);
});
it('should handle throwing inside routes with params', function(done) {
it('should handle throwing inside routes with params', function (done) {
var router = new Router();
router.get('/foo/:id', function () {
throw new Error('foo');
});
router.use(function(req, res, next){
router.use(function (req, res, next) {
assert(false);
});
router.use(function(err, req, res, next){
router.use(function (err, req, res, next) {
assert.equal(err.message, 'foo');
done();
});
router.handle({ url: '/foo/2', method: 'GET' }, {}, function() {});
router.handle({ url: '/foo/2', method: 'GET' }, {}, function () { });
});
it('should handle throwing in handler after async param', function(done) {
it('should handle throwing in handler after async param', function (done) {
var router = new Router();
router.param('user', function(req, res, next, val){
process.nextTick(function(){
router.param('user', function (req, res, next, val) {
process.nextTick(function () {
req.user = val;
next();
});
});
router.use('/:user', function(req, res, next){
router.use('/:user', function (req, res, next) {
throw new Error('oh no!');
});
router.use(function(err, req, res, next){
router.use(function (err, req, res, next) {
assert.equal(err.message, 'oh no!');
done();
});
router.handle({ url: '/bob', method: 'GET' }, {}, function() {});
router.handle({ url: '/bob', method: 'GET' }, {}, function () { });
});
it('should handle throwing inside error handlers', function(done) {
it('should handle throwing inside error handlers', function (done) {
var router = new Router();
router.use(function(req, res, next){
router.use(function (req, res, next) {
throw new Error('boom!');
});
router.use(function(err, req, res, next){
router.use(function (err, req, res, next) {
throw new Error('oops');
});
router.use(function(err, req, res, next){
router.use(function (err, req, res, next) {
assert.equal(err.message, 'oops');
done();
});
@ -408,73 +412,55 @@ describe('Router', function(){
});
})
describe('.all', function() {
it('should support using .all to capture all http verbs', function(done){
describe('.all', function () {
it('should support using .all to capture all http verbs', function (done) {
var router = new Router();
var count = 0;
router.all('/foo', function(){ count++; });
router.all('/foo', function () { count++; });
var url = '/foo?bar=baz';
methods.forEach(function testMethod(method) {
router.handle({ url: url, method: method }, {}, function() {});
router.handle({ url: url, method: method }, {}, function () { });
});
assert.equal(count, methods.length);
done();
})
it('should be called for any URL when "*"', function (done) {
var cb = after(4, done)
var router = new Router()
function no () {
throw new Error('should not be called')
}
router.all('*', function (req, res) {
res.end()
})
router.handle({ url: '/', method: 'GET' }, { end: cb }, no)
router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no)
router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no)
router.handle({ url: '*', method: 'GET' }, { end: cb }, no)
})
})
describe('.use', function() {
describe('.use', function () {
it('should require middleware', function () {
var router = new Router()
assert.throws(function () { router.use('/') }, /requires a middleware function/)
assert.throws(function () { router.use('/') }, /argument handler is required/)
})
it('should reject string as middleware', function () {
var router = new Router()
assert.throws(function () { router.use('/', 'foo') }, /requires a middleware function but got a string/)
assert.throws(function () { router.use('/', 'foo') }, /argument handler must be a function/)
})
it('should reject number as middleware', function () {
var router = new Router()
assert.throws(function () { router.use('/', 42) }, /requires a middleware function but got a number/)
assert.throws(function () { router.use('/', 42) }, /argument handler must be a function/)
})
it('should reject null as middleware', function () {
var router = new Router()
assert.throws(function () { router.use('/', null) }, /requires a middleware function but got a Null/)
assert.throws(function () { router.use('/', null) }, /argument handler must be a function/)
})
it('should reject Date as middleware', function () {
var router = new Router()
assert.throws(function () { router.use('/', new Date()) }, /requires a middleware function but got a Date/)
assert.throws(function () { router.use('/', new Date()) }, /argument handler must be a function/)
})
it('should be called for any URL', function (done) {
var cb = after(4, done)
var router = new Router()
function no () {
function no() {
throw new Error('should not be called')
}
@ -488,39 +474,49 @@ describe('Router', function(){
router.handle({ url: '*', method: 'GET' }, { end: cb }, no)
})
it('should accept array of middleware', function(done){
it('should accept array of middleware', function (done) {
var count = 0;
var router = new Router();
function fn1(req, res, next){
function fn1(req, res, next) {
assert.equal(++count, 1);
next();
}
function fn2(req, res, next){
function fn2(req, res, next) {
assert.equal(++count, 2);
next();
}
router.use([fn1, fn2], function(req, res){
router.use([fn1, fn2], function (req, res) {
assert.equal(++count, 3);
done();
});
router.handle({ url: '/foo', method: 'GET' }, {}, function(){});
router.handle({ url: '/foo', method: 'GET' }, {}, function () { });
})
})
describe('.param', function() {
it('should call param function when routing VERBS', function(done) {
describe('.param', function () {
it('should require function', function () {
var router = new Router();
assert.throws(router.param.bind(router, 'id'), /argument fn is required/);
});
it('should reject non-function', function () {
var router = new Router();
assert.throws(router.param.bind(router, 'id', 42), /argument fn must be a function/);
});
it('should call param function when routing VERBS', function (done) {
var router = new Router();
router.param('id', function(req, res, next, id) {
router.param('id', function (req, res, next, id) {
assert.equal(id, '123');
next();
});
router.get('/foo/:id/bar', function(req, res, next) {
router.get('/foo/:id/bar', function (req, res, next) {
assert.equal(req.params.id, '123');
next();
});
@ -528,15 +524,15 @@ describe('Router', function(){
router.handle({ url: '/foo/123/bar', method: 'get' }, {}, done);
});
it('should call param function when routing middleware', function(done) {
it('should call param function when routing middleware', function (done) {
var router = new Router();
router.param('id', function(req, res, next, id) {
router.param('id', function (req, res, next, id) {
assert.equal(id, '123');
next();
});
router.use('/foo/:id/bar', function(req, res, next) {
router.use('/foo/:id/bar', function (req, res, next) {
assert.equal(req.params.id, '123');
assert.equal(req.url, '/baz');
next();
@ -545,17 +541,17 @@ describe('Router', function(){
router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done);
});
it('should only call once per request', function(done) {
it('should only call once per request', function (done) {
var count = 0;
var req = { url: '/foo/bob/bar', method: 'get' };
var router = new Router();
var sub = new Router();
sub.get('/bar', function(req, res, next) {
sub.get('/bar', function (req, res, next) {
next();
});
router.param('user', function(req, res, next, user) {
router.param('user', function (req, res, next, user) {
count++;
req.user = user;
next();
@ -564,7 +560,7 @@ describe('Router', function(){
router.use('/foo/:user/', new Router());
router.use('/foo/:user/', sub);
router.handle(req, {}, function(err) {
router.handle(req, {}, function (err) {
if (err) return done(err);
assert.equal(count, 1);
assert.equal(req.user, 'bob');
@ -572,17 +568,17 @@ describe('Router', function(){
});
});
it('should call when values differ', function(done) {
it('should call when values differ', function (done) {
var count = 0;
var req = { url: '/foo/bob/bar', method: 'get' };
var router = new Router();
var sub = new Router();
sub.get('/bar', function(req, res, next) {
sub.get('/bar', function (req, res, next) {
next();
});
router.param('user', function(req, res, next, user) {
router.param('user', function (req, res, next, user) {
count++;
req.user = user;
next();
@ -591,7 +587,7 @@ describe('Router', function(){
router.use('/foo/:user/', new Router());
router.use('/:user/bob/', sub);
router.handle(req, {}, function(err) {
router.handle(req, {}, function (err) {
if (err) return done(err);
assert.equal(count, 2);
assert.equal(req.user, 'foo');
@ -600,8 +596,8 @@ describe('Router', function(){
});
});
describe('parallel requests', function() {
it('should not mix requests', function(done) {
describe('parallel requests', function () {
it('should not mix requests', function (done) {
var req1 = { url: '/foo/50/bar', method: 'get' };
var req2 = { url: '/foo/10/bar', method: 'get' };
var router = new Router();
@ -609,11 +605,11 @@ describe('Router', function(){
var cb = after(2, done)
sub.get('/bar', function(req, res, next) {
sub.get('/bar', function (req, res, next) {
next();
});
router.param('ms', function(req, res, next, ms) {
router.param('ms', function (req, res, next, ms) {
ms = parseInt(ms, 10);
req.ms = ms;
setTimeout(next, ms);
@ -622,14 +618,14 @@ describe('Router', function(){
router.use('/foo/:ms/', new Router());
router.use('/foo/:ms/', sub);
router.handle(req1, {}, function(err) {
router.handle(req1, {}, function (err) {
assert.ifError(err);
assert.equal(req1.ms, 50);
assert.equal(req1.originalUrl, '/foo/50/bar');
cb()
});
router.handle(req2, {}, function(err) {
router.handle(req2, {}, function (err) {
assert.ifError(err);
assert.equal(req2.ms, 10);
assert.equal(req2.originalUrl, '/foo/10/bar');

View File

@ -26,7 +26,7 @@ describe('app.all()', function(){
var app = express()
, n = 0;
app.all('/*', function(req, res, next){
app.all('/*splat', function(req, res, next){
if (n++) return done(new Error('DELETE called several times'));
next();
});

View File

@ -1,18 +0,0 @@
'use strict'
var express = require('../')
, request = require('supertest');
describe('app.del()', function(){
it('should alias app.delete()', function(done){
var app = express();
app.del('/tobi', function(req, res){
res.end('deleted tobi!');
});
request(app)
.del('/tobi')
.expect('deleted tobi!', done);
})
})

View File

@ -1,9 +1,9 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('../')
, fs = require('fs');
var path = require('path')
, fs = require('node:fs');
var path = require('node:path')
function render(path, options, fn) {
fs.readFile(path, 'utf8', function(err, str){

View File

@ -2,7 +2,7 @@
var express = require('../');
var request = require('supertest');
var assert = require('assert');
var assert = require('node:assert');
describe('HEAD', function(){
it('should default to GET', function(done){

View File

@ -1,6 +1,6 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('..')
var request = require('supertest')
@ -56,18 +56,6 @@ describe('app.mountpath', function(){
})
})
describe('app.router', function(){
it('should throw with notice', function(done){
var app = express()
try {
app.router;
} catch(err) {
done();
}
})
})
describe('app.path()', function(){
it('should return the canonical', function(){
var app = express()

View File

@ -1,6 +1,7 @@
'use strict'
var express = require('../')
var assert = require('node:assert')
describe('app.listen()', function(){
it('should wrap with an HTTP server', function(done){
@ -10,4 +11,17 @@ describe('app.listen()', function(){
server.close(done)
});
})
it('should callback on HTTP server errors', function (done) {
var app1 = express()
var app2 = express()
var server1 = app1.listen(0, function (err) {
assert(!err)
app2.listen(server1.address().port, function (err) {
assert(err.code === 'EADDRINUSE')
server1.close()
done()
})
})
})
})

View File

@ -1,14 +1,15 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('../')
describe('app', function(){
describe('.locals', function () {
it('should default object', function () {
it('should default object with null prototype', function () {
var app = express()
assert.ok(app.locals)
assert.strictEqual(typeof app.locals, 'object')
assert.strictEqual(Object.getPrototypeOf(app.locals), null)
})
describe('.settings', function () {

View File

@ -7,28 +7,28 @@ describe('OPTIONS', function(){
it('should default to the routes defined', function(done){
var app = express();
app.del('/', function(){});
app.post('/', function(){});
app.get('/users', function(req, res){});
app.put('/users', function(req, res){});
request(app)
.options('/users')
.expect('Allow', 'GET,HEAD,PUT')
.expect(200, 'GET,HEAD,PUT', done);
.expect('Allow', 'GET, HEAD, PUT')
.expect(200, 'GET, HEAD, PUT', done);
})
it('should only include each method once', function(done){
var app = express();
app.del('/', function(){});
app.delete('/', function(){});
app.get('/users', function(req, res){});
app.put('/users', function(req, res){});
app.get('/users', function(req, res){});
request(app)
.options('/users')
.expect('Allow', 'GET,HEAD,PUT')
.expect(200, 'GET,HEAD,PUT', done);
.expect('Allow', 'GET, HEAD, PUT')
.expect(200, 'GET, HEAD, PUT', done);
})
it('should not be affected by app.all', function(done){
@ -45,8 +45,8 @@ describe('OPTIONS', function(){
request(app)
.options('/users')
.expect('x-hit', '1')
.expect('Allow', 'GET,HEAD,PUT')
.expect(200, 'GET,HEAD,PUT', done);
.expect('Allow', 'GET, HEAD, PUT')
.expect(200, 'GET, HEAD, PUT', done);
})
it('should not respond if the path is not defined', function(done){
@ -69,8 +69,8 @@ describe('OPTIONS', function(){
request(app)
.options('/other')
.expect('Allow', 'GET,HEAD')
.expect(200, 'GET,HEAD', done);
.expect('Allow', 'GET, HEAD')
.expect(200, 'GET, HEAD', done);
})
describe('when error occurs in response handler', function () {

View File

@ -1,51 +1,9 @@
'use strict'
var assert = require('assert')
var express = require('../')
, request = require('supertest');
describe('app', function(){
describe('.param(fn)', function(){
it('should map app.param(name, ...) logic', function(done){
var app = express();
app.param(function(name, regexp){
if (Object.prototype.toString.call(regexp) === '[object RegExp]') { // See #1557
return function(req, res, next, val){
var captures;
if (captures = regexp.exec(String(val))) {
req.params[name] = captures[1];
next();
} else {
next('route');
}
}
}
})
app.param(':name', /^([a-zA-Z]+)$/);
app.get('/user/:name', function(req, res){
res.send(req.params.name);
});
request(app)
.get('/user/tj')
.expect(200, 'tj', function (err) {
if (err) return done(err)
request(app)
.get('/user/123')
.expect(404, done);
});
})
it('should fail if not given fn', function(){
var app = express();
assert.throws(app.param.bind(app, ':name', 'bob'))
})
})
describe('.param(names, fn)', function(){
it('should map the array', function(done){
var app = express();

View File

@ -1,8 +1,8 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('..');
var path = require('path')
var path = require('node:path')
var tmpl = require('./support/tmpl');
describe('app', function(){

View File

@ -10,7 +10,7 @@ describe('app', function(){
var app = express();
app.request.querystring = function(){
return require('url').parse(this.url).query;
return require('node:url').parse(this.url).query;
};
app.use(function(req, res){

View File

@ -61,4 +61,137 @@ describe('app.route', function(){
.get('/test')
.expect(404, done);
});
describe('promise support', function () {
it('should pass rejected promise value', function (done) {
var app = express()
var route = app.route('/foo')
route.all(function createError (req, res, next) {
return Promise.reject(new Error('boom!'))
})
route.all(function helloWorld (req, res) {
res.send('hello, world!')
})
route.all(function handleError (err, req, res, next) {
res.status(500)
res.send('caught: ' + err.message)
})
request(app)
.get('/foo')
.expect(500, 'caught: boom!', done)
})
it('should pass rejected promise without value', function (done) {
var app = express()
var route = app.route('/foo')
route.all(function createError (req, res, next) {
return Promise.reject()
})
route.all(function helloWorld (req, res) {
res.send('hello, world!')
})
route.all(function handleError (err, req, res, next) {
res.status(500)
res.send('caught: ' + err.message)
})
request(app)
.get('/foo')
.expect(500, 'caught: Rejected promise', done)
})
it('should ignore resolved promise', function (done) {
var app = express()
var route = app.route('/foo')
route.all(function createError (req, res, next) {
res.send('saw GET /foo')
return Promise.resolve('foo')
})
route.all(function () {
done(new Error('Unexpected route invoke'))
})
request(app)
.get('/foo')
.expect(200, 'saw GET /foo', done)
})
describe('error handling', function () {
it('should pass rejected promise value', function (done) {
var app = express()
var route = app.route('/foo')
route.all(function createError (req, res, next) {
return Promise.reject(new Error('boom!'))
})
route.all(function handleError (err, req, res, next) {
return Promise.reject(new Error('caught: ' + err.message))
})
route.all(function handleError (err, req, res, next) {
res.status(500)
res.send('caught again: ' + err.message)
})
request(app)
.get('/foo')
.expect(500, 'caught again: caught: boom!', done)
})
it('should pass rejected promise without value', function (done) {
var app = express()
var route = app.route('/foo')
route.all(function createError (req, res, next) {
return Promise.reject(new Error('boom!'))
})
route.all(function handleError (err, req, res, next) {
return Promise.reject()
})
route.all(function handleError (err, req, res, next) {
res.status(500)
res.send('caught again: ' + err.message)
})
request(app)
.get('/foo')
.expect(500, 'caught again: Rejected promise', done)
})
it('should ignore resolved promise', function (done) {
var app = express()
var route = app.route('/foo')
route.all(function createError (req, res, next) {
return Promise.reject(new Error('boom!'))
})
route.all(function handleError (err, req, res, next) {
res.status(500)
res.send('caught: ' + err.message)
return Promise.resolve('foo')
})
route.all(function () {
done(new Error('Unexpected route invoke'))
})
request(app)
.get('/foo')
.expect(500, 'caught: boom!', done)
})
})
})
});

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('../')
, request = require('supertest');
@ -51,7 +51,7 @@ describe('app', function(){
assert.ok(b)
assert.ok(c)
assert.ok(!d)
res.send(204);
res.sendStatus(204);
});
request(app)

View File

@ -1,7 +1,7 @@
'use strict'
var after = require('after');
var assert = require('assert')
var assert = require('node:assert')
var express = require('..');
var request = require('supertest');
@ -258,27 +258,27 @@ describe('app', function(){
describe('.use(path, middleware)', function(){
it('should require middleware', function () {
var app = express()
assert.throws(function () { app.use('/') }, /requires a middleware function/)
assert.throws(function () { app.use('/') }, 'TypeError: app.use() requires a middleware function')
})
it('should reject string as middleware', function () {
var app = express()
assert.throws(function () { app.use('/', 'foo') }, /requires a middleware function but got a string/)
assert.throws(function () { app.use('/', 'foo') }, /argument handler must be a function/)
})
it('should reject number as middleware', function () {
var app = express()
assert.throws(function () { app.use('/', 42) }, /requires a middleware function but got a number/)
assert.throws(function () { app.use('/', 42) }, /argument handler must be a function/)
})
it('should reject null as middleware', function () {
var app = express()
assert.throws(function () { app.use('/', null) }, /requires a middleware function but got a Null/)
assert.throws(function () { app.use('/', null) }, /argument handler must be a function/)
})
it('should reject Date as middleware', function () {
var app = express()
assert.throws(function () { app.use('/', new Date()) }, /requires a middleware function but got a Date/)
assert.throws(function () { app.use('/', new Date()) }, /argument handler must be a function/)
})
it('should strip path from req.url', function (done) {

View File

@ -1,6 +1,6 @@
'use strict'
var assert = require('assert');
var assert = require('node:assert');
var express = require('..');
describe('config', function () {

View File

@ -1,6 +1,6 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('../');
var request = require('supertest');
@ -79,9 +79,4 @@ describe('exports', function(){
.get('/')
.expect('bar', done);
})
it('should throw on old middlewares', function(){
assert.throws(function () { express.bodyParser() }, /Error:.*middleware.*bodyParser/)
assert.throws(function () { express.limit() }, /Error:.*middleware.*limit/)
})
})

View File

@ -1,15 +1,11 @@
'use strict'
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert')
var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
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())
@ -43,12 +39,13 @@ describe('express.json()', function () {
.expect(200, '{}', done)
})
// The old node error message modification in body parser is catching this
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)
.expect(400, '[entity.parse.failed] ' + parseError(' \n'), done)
})
it('should 400 when invalid content-length', function (done) {
@ -72,32 +69,6 @@ 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()
@ -341,7 +312,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/json')
.send('{"user":"tobi"}')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@ -373,7 +344,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/x-json')
.send('{"user":"tobi"}')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@ -528,13 +499,13 @@ describe('express.json()', function () {
})
})
describeAsyncHooks('async local storage', function () {
describe('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 = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@ -579,14 +550,14 @@ describe('express.json()', function () {
.end(done)
})
it('should presist store when unmatched content-type', function (done) {
it('should persist 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('{}')
.expect('')
.end(done)
})
@ -753,6 +724,7 @@ function createApp (options) {
app.use(express.json(options))
app.use(function (err, req, res, next) {
// console.log(err)
res.status(err.status || 500)
res.send(String(req.headers['x-error-property']
? err[req.headers['x-error-property']]
@ -780,11 +752,3 @@ function shouldContainInBody (str) {
'expected \'' + res.text + '\' to contain \'' + str + '\'')
}
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@ -1,15 +1,11 @@
'use strict'
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert')
var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
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()
@ -65,36 +61,6 @@ 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()
@ -236,7 +202,7 @@ describe('express.raw()', function () {
var test = request(this.app).post('/')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('000102', 'hex'))
test.expect(200, '{}', done)
test.expect(200, '', done)
})
})
@ -265,7 +231,7 @@ describe('express.raw()', function () {
var test = request(this.app).post('/')
test.set('Content-Type', 'application/x-foo')
test.write(Buffer.from('000102', 'hex'))
test.expect(200, '{}', done)
test.expect(200, '', done)
})
})
@ -358,13 +324,13 @@ describe('express.raw()', function () {
})
})
describeAsyncHooks('async local storage', function () {
describe('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 = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@ -420,7 +386,6 @@ describe('express.raw()', function () {
.send('buzz')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{}')
.end(done)
})
@ -545,11 +510,3 @@ function createApp (options) {
return app
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@ -1,9 +1,8 @@
'use strict'
var assert = require('assert')
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert')
var express = require('..')
var path = require('path')
var path = require('node:path')
var request = require('supertest')
var utils = require('./support/utils')
@ -41,7 +40,7 @@ describe('express.static()', function () {
it('should set Content-Type', function (done) {
request(this.app)
.get('/todo.txt')
.expect('Content-Type', 'text/plain; charset=UTF-8')
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect(200, done)
})
@ -486,7 +485,7 @@ describe('express.static()', function () {
request(this.app)
.get('/users')
.expect('Location', '/users/')
.expect(301, /<a href="\/users\/">/, done)
.expect(301, /\/users\//, done)
})
it('should redirect directories with query string', function (done) {
@ -508,7 +507,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,15 +1,11 @@
'use strict'
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert')
var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
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()
@ -61,32 +57,6 @@ 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()
@ -247,7 +217,7 @@ describe('express.text()', function () {
.post('/')
.set('Content-Type', 'text/plain')
.send('user is tobi')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@ -277,7 +247,7 @@ describe('express.text()', function () {
.post('/')
.set('Content-Type', 'text/xml')
.send('<user>tobi</user>')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@ -387,13 +357,13 @@ describe('express.text()', function () {
})
})
describeAsyncHooks('async local storage', function () {
describe('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 = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@ -445,7 +415,6 @@ describe('express.text()', function () {
.send('buzz')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{}')
.end(done)
})
@ -595,11 +564,3 @@ function createApp (options) {
return app
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@ -1,15 +1,11 @@
'use strict'
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert')
var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
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()
@ -62,32 +58,6 @@ 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()
@ -105,12 +75,12 @@ describe('express.urlencoded()', function () {
.expect(200, '{"user":"tobi"}', done)
})
it('should parse extended syntax', function (done) {
it('should not parse extended syntax', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('user[name][first]=Tobi')
.expect(200, '{"user":{"name":{"first":"Tobi"}}}', done)
.expect(200, '{"user[name][first]":"Tobi"}', done)
})
describe('with extended option', function () {
@ -212,7 +182,7 @@ describe('express.urlencoded()', function () {
it('should parse deep object', function (done) {
var str = 'foo'
for (var i = 0; i < 500; i++) {
for (var i = 0; i < 32; i++) {
str += '[p]'
}
@ -230,7 +200,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)
})
@ -473,7 +443,7 @@ describe('express.urlencoded()', function () {
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('user=tobi')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@ -505,7 +475,7 @@ describe('express.urlencoded()', function () {
.post('/')
.set('Content-Type', 'application/x-foo')
.send('user=tobi')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@ -632,13 +602,13 @@ describe('express.urlencoded()', function () {
})
})
describeAsyncHooks('async local storage', function () {
describe('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 = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@ -690,7 +660,6 @@ describe('express.urlencoded()', function () {
.send('buzz')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('{}')
.end(done)
})
@ -856,11 +825,3 @@ function expectKeyCount (count) {
assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count)
}
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@ -1,6 +1,6 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('../');
var request = require('supertest');

View File

@ -1,50 +0,0 @@
'use strict'
var express = require('../')
, request = require('supertest');
describe('req', function(){
describe('.acceptsCharset(type)', function(){
describe('when Accept-Charset is not present', function(){
it('should return true', function(done){
var app = express();
app.use(function(req, res, next){
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
});
request(app)
.get('/')
.expect('yes', done);
})
})
describe('when Accept-Charset is present', function () {
it('should return true', function (done) {
var app = express();
app.use(function(req, res, next){
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
});
request(app)
.get('/')
.set('Accept-Charset', 'foo, bar, utf-8')
.expect('yes', done);
})
it('should return false otherwise', function(done){
var app = express();
app.use(function(req, res, next){
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
});
request(app)
.get('/')
.set('Accept-Charset', 'foo, bar')
.expect('no', done);
})
})
})
})

View File

@ -1,39 +0,0 @@
'use strict'
var express = require('../')
, request = require('supertest');
describe('req', function(){
describe('.acceptsEncoding', function(){
it('should return encoding if accepted', function (done) {
var app = express();
app.get('/', function (req, res) {
res.send({
gzip: req.acceptsEncoding('gzip'),
deflate: req.acceptsEncoding('deflate')
})
})
request(app)
.get('/')
.set('Accept-Encoding', ' gzip, deflate')
.expect(200, { gzip: 'gzip', deflate: 'deflate' }, done)
})
it('should be false if encoding not accepted', function(done){
var app = express();
app.get('/', function (req, res) {
res.send({
bogus: req.acceptsEncoding('bogus')
})
})
request(app)
.get('/')
.set('Accept-Encoding', ' gzip, deflate')
.expect(200, { bogus: false }, done)
})
})
})

View File

@ -1,57 +0,0 @@
'use strict'
var express = require('../')
, request = require('supertest');
describe('req', function(){
describe('.acceptsLanguage', function(){
it('should return language if accepted', function (done) {
var app = express();
app.get('/', function (req, res) {
res.send({
'en-us': req.acceptsLanguage('en-us'),
en: req.acceptsLanguage('en')
})
})
request(app)
.get('/')
.set('Accept-Language', 'en;q=.5, en-us')
.expect(200, { 'en-us': 'en-us', en: 'en' }, done)
})
it('should be false if language not accepted', function(done){
var app = express();
app.get('/', function (req, res) {
res.send({
es: req.acceptsLanguage('es')
})
})
request(app)
.get('/')
.set('Accept-Language', 'en;q=.5, en-us')
.expect(200, { es: false }, done)
})
describe('when Accept-Language is not present', function(){
it('should always return language', function (done) {
var app = express();
app.get('/', function (req, res) {
res.send({
en: req.acceptsLanguage('en'),
es: req.acceptsLanguage('es'),
jp: req.acceptsLanguage('jp')
})
})
request(app)
.get('/')
.expect(200, { en: 'en', es: 'es', jp: 'jp' }, done)
})
})
})
})

View File

@ -46,5 +46,25 @@ describe('req', function(){
.get('/')
.expect(200, 'false', done);
})
it('should ignore "If-Modified-Since" when "If-None-Match" is present', function(done) {
var app = express();
const etag = '"FooBar"'
const now = Date.now()
app.disable('x-powered-by')
app.use(function(req, res) {
res.set('Etag', etag)
res.set('Last-Modified', new Date(now).toUTCString())
res.send(req.fresh);
});
request(app)
.get('/')
.set('If-Modified-Since', new Date(now - 1000).toUTCString)
.set('If-None-Match', etag)
.expect(304, done);
})
})
})

View File

@ -2,7 +2,7 @@
var express = require('../')
, request = require('supertest')
, assert = require('assert');
, assert = require('node:assert');
describe('req', function(){
describe('.get(field)', function(){

View File

@ -28,7 +28,7 @@ describe('req', function(){
request(app)
.post('/')
.set('Host', 'example.com:3000')
.expect('example.com', done);
.expect(200, 'example.com:3000', done);
})
it('should return undefined otherwise', function(done){
@ -67,7 +67,7 @@ describe('req', function(){
request(app)
.post('/')
.set('Host', '[::1]:3000')
.expect('[::1]', done);
.expect(200, '[::1]:3000', done);
})
describe('when "trust proxy" is enabled', function(){

View File

@ -1,61 +0,0 @@
'use strict'
var express = require('../')
, request = require('supertest')
describe('req', function(){
describe('.param(name, default)', function(){
it('should use the default value unless defined', function(done){
var app = express();
app.use(function(req, res){
res.end(req.param('name', 'tj'));
});
request(app)
.get('/')
.expect('tj', done);
})
})
describe('.param(name)', function(){
it('should check req.query', function(done){
var app = express();
app.use(function(req, res){
res.end(req.param('name'));
});
request(app)
.get('/?name=tj')
.expect('tj', done);
})
it('should check req.body', function(done){
var app = express();
app.use(express.json())
app.use(function(req, res){
res.end(req.param('name'));
});
request(app)
.post('/')
.send({ name: 'tj' })
.expect('tj', done);
})
it('should check req.params', function(done){
var app = express();
app.get('/user/:name', function(req, res){
res.end(req.param('filter') + req.param('name'));
});
request(app)
.get('/user/tj')
.expect('undefinedtj', done);
})
})
})

View File

@ -1,6 +1,6 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('../')
, request = require('supertest');
@ -14,12 +14,12 @@ describe('req', function(){
.expect(200, '{}', done);
});
it('should default to parse complex keys', function (done) {
it('should default to parse simple keys', function (done) {
var app = createApp();
request(app)
.get('/?user[name]=tj')
.expect(200, '{"user":{"name":"tj"}}', done);
.expect(200, '{"user[name]":"tj"}', done);
});
describe('when "query parser" is extended', function () {
@ -82,23 +82,6 @@ describe('req', function(){
});
});
describe('when "query parser fn" is missing', function () {
it('should act like "extended"', function (done) {
var app = express();
delete app.settings['query parser'];
delete app.settings['query parser fn'];
app.use(function (req, res) {
res.send(req.query);
});
request(app)
.get('/?user[name]=tj&user.name=tj')
.expect(200, '{"user":{"name":"tj"},"user.name":"tj"}', done);
});
});
describe('when "query parser" an unknown value', function () {
it('should throw', function () {
assert.throws(createApp.bind(null, 'bogus'),

View File

@ -8,7 +8,7 @@ describe('req', function(){
it('should be the executed Route', function(done){
var app = express();
app.get('/user/:id/:op?', function(req, res, next){
app.get('/user/:id{/:op}', function(req, res, next){
res.header('path-1', req.route.path)
next();
});
@ -20,7 +20,7 @@ describe('req', function(){
request(app)
.get('/user/12/edit')
.expect('path-1', '/user/:id/:op?')
.expect('path-1', '/user/:id{/:op}')
.expect('path-2', '/user/:id/edit')
.expect(200, done)
})

View File

@ -1,6 +1,6 @@
'use strict'
var assert = require('assert')
var assert = require('node:assert')
var express = require('..')
var request = require('supertest')

View File

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

View File

@ -33,35 +33,29 @@ describe('res', function(){
.expect(200, done)
})
it('should set expires when passed', function(done) {
var expiresAt = new Date()
it('should ignore maxAge', function(done){
var app = express();
app.use(function(req, res){
res.clearCookie('sid', { expires: expiresAt }).end();
res.clearCookie('sid', { path: '/admin', maxAge: 1000 }).end();
});
request(app)
.get('/')
.expect('Set-Cookie', 'sid=; Path=/; Expires=' + expiresAt.toUTCString() )
.expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT')
.expect(200, done)
})
it('should set both maxAge and expires when passed', function(done) {
var maxAgeInMs = 10000
var expiresAt = new Date()
var expectedExpires = new Date(expiresAt.getTime() + maxAgeInMs)
it('should ignore user supplied expires param', function(done){
var app = express();
app.use(function(req, res){
res.clearCookie('sid', { expires: expiresAt, maxAge: maxAgeInMs }).end();
res.clearCookie('sid', { path: '/admin', expires: new Date() }).end();
});
request(app)
.get('/')
// yes, this is the behavior. When we set a max-age, we also set expires to a date 10 sec ahead of expires
// even if we set max-age only, we will also set an expires 10 sec in the future
.expect('Set-Cookie', 'sid=; Max-Age=10; Path=/; Expires=' + expectedExpires.toUTCString())
.expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT')
.expect(200, done)
})
})

View File

@ -3,7 +3,6 @@
var express = require('../')
, request = require('supertest')
, cookieParser = require('cookie-parser')
var merge = require('utils-merge');
describe('res', function(){
describe('.cookie(name, object)', function(){
@ -130,7 +129,7 @@ describe('res', function(){
var app = express();
var options = { maxAge: 1000 };
var optionsCopy = merge({}, options);
var optionsCopy = { ...options };
app.use(function(req, res){
res.cookie('name', 'tobi', options)

View File

@ -1,20 +1,16 @@
'use strict'
var after = require('after');
var assert = require('assert')
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert')
var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
var express = require('..');
var path = require('path')
var path = require('node:path')
var request = require('supertest');
var utils = require('./support/utils')
var FIXTURES_PATH = path.join(__dirname, 'fixtures')
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('res', function(){
describe('.download(path)', function(){
it('should transfer as an attachment', function(done){
@ -26,7 +22,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'text/html; charset=UTF-8')
.expect('Content-Type', 'text/html; charset=utf-8')
.expect('Content-Disposition', 'attachment; filename="user.html"')
.expect(200, '<p>{{user.name}}</p>', done)
})
@ -69,7 +65,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'text/html; charset=UTF-8')
.expect('Content-Type', 'text/html; charset=utf-8')
.expect('Content-Disposition', 'attachment; filename="document"')
.expect(200, done)
})
@ -86,19 +82,19 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'text/html; charset=UTF-8')
.expect('Content-Type', 'text/html; charset=utf-8')
.expect('Content-Disposition', 'attachment; filename="user.html"')
.expect(200, cb);
})
describeAsyncHooks('async local storage', function () {
describe('async local storage', function () {
it('should presist store', function (done) {
var app = express()
var cb = after(2, done)
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@ -115,7 +111,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'text/plain; charset=UTF-8')
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect('Content-Disposition', 'attachment; filename="name.txt"')
.expect(200, 'tobi', cb)
})
@ -125,7 +121,7 @@ describe('res', function(){
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@ -369,7 +365,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'text/html; charset=UTF-8')
.expect('Content-Type', 'text/html; charset=utf-8')
.expect('Content-Disposition', 'attachment; filename="document"')
.expect(200, cb);
})
@ -388,7 +384,7 @@ describe('res', function(){
request(app)
.get('/')
.expect(200)
.expect('Content-Type', 'text/html; charset=UTF-8')
.expect('Content-Type', 'text/html; charset=utf-8')
.expect('Content-Disposition', 'attachment; filename="document"')
.end(cb)
})
@ -488,11 +484,3 @@ describe('res', function(){
})
})
})
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@ -3,7 +3,7 @@
var after = require('after')
var express = require('../')
, request = require('supertest')
, assert = require('assert');
, assert = require('node:assert');
var app1 = express();
@ -28,7 +28,8 @@ app1.use(function(req, res, next){
app1.use(function(err, req, res, next){
if (!err.types) throw err;
res.send(err.status, 'Supports: ' + err.types.join(', '));
res.status(err.status)
res.send('Supports: ' + err.types.join(', '))
})
var app2 = express();
@ -42,7 +43,8 @@ app2.use(function(req, res, next){
});
app2.use(function(err, req, res, next){
res.send(err.status, 'Supports: ' + err.types.join(', '));
res.status(err.status)
res.send('Supports: ' + err.types.join(', '))
})
var app3 = express();
@ -70,7 +72,8 @@ app4.get('/', function (req, res) {
});
app4.use(function(err, req, res, next){
res.send(err.status, 'Supports: ' + err.types.join(', '));
res.status(err.status)
res.send('Supports: ' + err.types.join(', '))
})
var app5 = express();
@ -103,7 +106,8 @@ describe('res', function(){
});
app.use(function(err, req, res, next){
res.send(err.status, 'Supports: ' + err.types.join(', '));
res.status(err.status)
res.send('Supports: ' + err.types.join(', '))
});
test(app);
@ -164,7 +168,8 @@ describe('res', function(){
});
router.use(function(err, req, res, next){
res.send(err.status, 'Supports: ' + err.types.join(', '));
res.status(err.status)
res.send('Supports: ' + err.types.join(', '))
})
app.use(router)

View File

@ -2,7 +2,7 @@
var express = require('../')
, request = require('supertest')
, assert = require('assert');
, assert = require('node:assert');
describe('res', function(){
describe('.json(object)', function(){
@ -183,47 +183,4 @@ describe('res', function(){
})
})
})
describe('.json(status, object)', function(){
it('should respond with json and set the .statusCode', function(done){
var app = express();
app.use(function(req, res){
res.json(201, { id: 1 });
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '{"id":1}', done)
})
})
describe('.json(object, status)', function(){
it('should respond with json and set the .statusCode for backwards compat', function(done){
var app = express();
app.use(function(req, res){
res.json({ id: 1 }, 201);
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '{"id":1}', done)
})
it('should use status as second number for backwards compat', function(done){
var app = express();
app.use(function(req, res){
res.json(200, 201);
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '200', done)
})
})
})

View File

@ -2,7 +2,7 @@
var express = require('../')
, request = require('supertest')
, assert = require('assert');
, assert = require('node:assert');
var utils = require('./support/utils');
describe('res', function(){
@ -328,49 +328,6 @@ describe('res', function(){
})
})
describe('.jsonp(status, object)', function(){
it('should respond with json and set the .statusCode', function(done){
var app = express();
app.use(function(req, res){
res.jsonp(201, { id: 1 });
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '{"id":1}', done)
})
})
describe('.jsonp(object, status)', function(){
it('should respond with json and set the .statusCode for backwards compat', function(done){
var app = express();
app.use(function(req, res){
res.jsonp({ id: 1 }, 201);
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '{"id":1}', done)
})
it('should use status as second number for backwards compat', function(done){
var app = express();
app.use(function(req, res){
res.jsonp(200, 201);
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '200', done)
})
})
it('should not override previous Content-Types', function(done){
var app = express();

View File

@ -43,5 +43,23 @@ describe('res', function(){
.expect('Link', '<http://api.example.com/users?page=2>; rel="next", <http://api.example.com/users?page=5>; rel="last", <http://api.example.com/users?page=1>; rel="prev"')
.expect(200, done);
})
it('should set multiple links for single rel', function (done) {
var app = express();
app.use(function (req, res) {
res.links({
next: 'http://api.example.com/users?page=2',
last: ['http://api.example.com/users?page=5', 'http://api.example.com/users?page=1']
});
res.end();
});
request(app)
.get('/')
.expect('Link', '<http://api.example.com/users?page=2>; rel="next", <http://api.example.com/users?page=5>; rel="last", <http://api.example.com/users?page=1>; rel="last"')
.expect(200, done);
})
})
})

View File

@ -2,8 +2,8 @@
var express = require('../')
, request = require('supertest')
, assert = require('assert')
, url = require('url');
, assert = require('node:assert')
, url = require('node:url');
describe('res', function(){
describe('.location(url)', function(){
@ -46,65 +46,19 @@ describe('res', function(){
.expect(200, done)
})
describe('when url is "back"', function () {
it('should set location from "Referer" header', function (done) {
var app = express()
it('should encode data uri1', function (done) {
var app = express()
app.use(function (req, res) {
res.location('data:text/javascript,export default () => { }').end();
});
app.use(function (req, res) {
res.location('back').end()
})
request(app)
request(app)
.get('/')
.set('Referer', '/some/page.html')
.expect('Location', '/some/page.html')
.expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')
.expect(200, done)
})
it('should set location from "Referrer" header', function (done) {
var app = express()
app.use(function (req, res) {
res.location('back').end()
})
request(app)
.get('/')
.set('Referrer', '/some/page.html')
.expect('Location', '/some/page.html')
.expect(200, done)
})
it('should prefer "Referrer" header', function (done) {
var app = express()
app.use(function (req, res) {
res.location('back').end()
})
request(app)
.get('/')
.set('Referer', '/some/page1.html')
.set('Referrer', '/some/page2.html')
.expect('Location', '/some/page2.html')
.expect(200, done)
})
it('should set the header to "/" without referrer', function (done) {
var app = express()
app.use(function (req, res) {
res.location('back').end()
})
request(app)
.get('/')
.expect('Location', '/')
.expect(200, done)
})
})
it('should encode data uri', function (done) {
it('should encode data uri2', function (done) {
var app = express()
app.use(function (req, res) {
res.location('data:text/javascript,export default () => { }').end();

View File

@ -61,21 +61,6 @@ describe('res', function(){
})
})
describe('.redirect(url, status)', function(){
it('should set the response status', function(done){
var app = express();
app.use(function(req, res){
res.redirect('http://google.com', 303);
});
request(app)
.get('/')
.expect('Location', 'http://google.com')
.expect(303, done)
})
})
describe('when the request method is HEAD', function(){
it('should ignore the body', function(done){
var app = express();
@ -106,7 +91,7 @@ describe('res', function(){
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', 'http://google.com')
.expect(302, '<p>Found. Redirecting to <a href="http://google.com">http://google.com</a></p>', done)
.expect(302, '<p>Found. Redirecting to http://google.com</p>', done)
})
it('should escape the url', function(done){
@ -122,9 +107,27 @@ describe('res', function(){
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', '%3Cla\'me%3E')
.expect(302, '<p>Found. Redirecting to <a href="%3Cla&#39;me%3E">%3Cla&#39;me%3E</a></p>', done)
.expect(302, '<p>Found. Redirecting to %3Cla&#39;me%3E</p>', done)
})
it('should not render evil javascript links in anchor href (prevent XSS)', function(done){
var app = express();
var xss = 'javascript:eval(document.body.innerHTML=`<p>XSS</p>`);';
var encodedXss = 'javascript:eval(document.body.innerHTML=%60%3Cp%3EXSS%3C/p%3E%60);';
app.use(function(req, res){
res.redirect(xss);
});
request(app)
.get('/')
.set('Host', 'http://example.com')
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', encodedXss)
.expect(302, '<p>Found. Redirecting to ' + encodedXss +'</p>', done);
});
it('should include the redirect type', function(done){
var app = express();
@ -137,7 +140,7 @@ describe('res', function(){
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', 'http://google.com')
.expect(301, '<p>Moved Permanently. Redirecting to <a href="http://google.com">http://google.com</a></p>', done);
.expect(301, '<p>Moved Permanently. Redirecting to http://google.com</p>', done);
})
})

View File

@ -1,7 +1,7 @@
'use strict'
var express = require('..');
var path = require('path')
var path = require('node:path')
var request = require('supertest');
var tmpl = require('./support/tmpl');

View File

@ -1,9 +1,8 @@
'use strict'
var assert = require('assert')
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert')
var express = require('..');
var methods = require('methods');
var methods = require('../lib/utils').methods;
var request = require('supertest');
var utils = require('./support/utils');
@ -53,63 +52,18 @@ describe('res', function(){
})
})
describe('.send(code)', function(){
it('should set .statusCode', function(done){
describe('.send(Number)', function(){
it('should send as application/json', function(done){
var app = express();
app.use(function(req, res){
res.send(201)
});
request(app)
.get('/')
.expect('Created')
.expect(201, done);
})
})
describe('.send(code, body)', function(){
it('should set .statusCode and body', function(done){
var app = express();
app.use(function(req, res){
res.send(201, 'Created :)');
});
request(app)
.get('/')
.expect('Created :)')
.expect(201, done);
})
})
describe('.send(body, code)', function(){
it('should be supported for backwards compat', function(done){
var app = express();
app.use(function(req, res){
res.send('Bad!', 400);
});
request(app)
.get('/')
.expect('Bad!')
.expect(400, done);
})
})
describe('.send(code, number)', function(){
it('should send number as json', function(done){
var app = express();
app.use(function(req, res){
res.send(200, 0.123);
res.send(1000);
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '0.123', done);
.expect(200, '1000', done)
})
})
@ -223,6 +177,19 @@ describe('res', function(){
.expect(200, 'hey', done);
})
it('should accept Uint8Array', function(done){
var app = express();
app.use(function(req, res){
const encodedHey = new TextEncoder().encode("hey");
res.set("Content-Type", "text/plain").send(encodedHey);
})
request(app)
.get("/")
.expect("Content-Type", "text/plain; charset=utf-8")
.expect(200, "hey", done);
})
it('should not override ETag', function (done) {
var app = express()
@ -463,7 +430,7 @@ describe('res', function(){
app.use(function (req, res) {
res.set('etag', '"asdf"');
res.send(200);
res.send('hello!');
});
app.enable('etag');
@ -514,7 +481,7 @@ describe('res', function(){
app.use(function (req, res) {
res.set('etag', '"asdf"');
res.send(200);
res.send('hello!');
});
request(app)

View File

@ -1,20 +1,16 @@
'use strict'
var after = require('after');
var asyncHooks = tryRequire('async_hooks')
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert')
var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
var express = require('../')
, request = require('supertest')
, assert = require('assert');
var onFinished = require('on-finished');
var path = require('path');
var path = require('node:path');
var fixtures = path.join(__dirname, 'fixtures');
var utils = require('./support/utils');
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('res', function(){
describe('.sendFile(path)', function () {
it('should error missing path', function (done) {
@ -82,6 +78,19 @@ describe('res', function(){
});
});
it('should disable the ETag function if requested', function (done) {
var app = createApp(path.resolve(fixtures, 'name.txt')).disable('etag');
request(app)
.get('/')
.expect(handleHeaders)
.expect(200, done);
function handleHeaders (res) {
assert(res.headers.etag === undefined);
}
});
it('should 404 for directory', function (done) {
var app = createApp(path.resolve(fixtures, 'blog'));
@ -267,14 +276,14 @@ describe('res', function(){
.expect(200, 'got 404 error', done)
})
describeAsyncHooks('async local storage', function () {
describe('async local storage', function () {
it('should presist store', function (done) {
var app = express()
var cb = after(2, done)
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@ -291,7 +300,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'text/plain; charset=UTF-8')
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect(200, 'tobi', cb)
})
@ -300,7 +309,7 @@ describe('res', function(){
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@ -890,507 +899,6 @@ describe('res', function(){
})
})
})
describe('.sendfile(path, fn)', function(){
it('should invoke the callback when complete', function(done){
var app = express();
var cb = after(2, done);
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', cb)
});
request(app)
.get('/')
.expect(200, cb);
})
it('should utilize the same options as express.static()', function(done){
var app = express();
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', { maxAge: 60000 });
});
request(app)
.get('/')
.expect('Cache-Control', 'public, max-age=60')
.end(done);
})
it('should invoke the callback when client aborts', function (done) {
var cb = after(2, done)
var app = express();
app.use(function (req, res) {
setImmediate(function () {
res.sendfile('test/fixtures/name.txt', function (err) {
assert.ok(err)
assert.strictEqual(err.code, 'ECONNABORTED')
cb()
});
});
test.req.abort()
});
var server = app.listen()
var test = request(server).get('/')
test.end(function (err) {
assert.ok(err)
server.close(cb)
})
})
it('should invoke the callback when client already aborted', function (done) {
var cb = after(2, done)
var app = express();
app.use(function (req, res) {
onFinished(res, function () {
res.sendfile('test/fixtures/name.txt', function (err) {
assert.ok(err)
assert.strictEqual(err.code, 'ECONNABORTED')
cb()
});
});
test.req.abort()
});
var server = app.listen()
var test = request(server).get('/')
test.end(function (err) {
assert.ok(err)
server.close(cb)
})
})
it('should invoke the callback without error when HEAD', function (done) {
var app = express();
var cb = after(2, done);
app.use(function (req, res) {
res.sendfile('test/fixtures/name.txt', cb);
});
request(app)
.head('/')
.expect(200, cb);
});
it('should invoke the callback without error when 304', function (done) {
var app = express();
var cb = after(3, done);
app.use(function (req, res) {
res.sendfile('test/fixtures/name.txt', cb);
});
request(app)
.get('/')
.expect('ETag', /^(?:W\/)?"[^"]+"$/)
.expect(200, 'tobi', function (err, res) {
if (err) return cb(err);
var etag = res.headers.etag;
request(app)
.get('/')
.set('If-None-Match', etag)
.expect(304, cb);
});
});
it('should invoke the callback on 404', function(done){
var app = express();
var calls = 0;
app.use(function(req, res){
res.sendfile('test/fixtures/nope.html', function(err){
assert.equal(calls++, 0);
assert(!res.headersSent);
res.send(err.message);
});
});
request(app)
.get('/')
.expect(200, /^ENOENT.*?, stat/, done);
})
it('should not override manual content-types', function(done){
var app = express();
app.use(function(req, res){
res.contentType('txt');
res.sendfile('test/fixtures/user.html');
});
request(app)
.get('/')
.expect('Content-Type', 'text/plain; charset=utf-8')
.end(done);
})
it('should invoke the callback on 403', function(done){
var app = express()
app.use(function(req, res){
res.sendfile('test/fixtures/foo/../user.html', function(err){
assert(!res.headersSent);
res.send(err.message);
});
});
request(app)
.get('/')
.expect('Forbidden')
.expect(200, done);
})
it('should invoke the callback on socket error', function(done){
var app = express()
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', function(err){
assert.ok(err)
assert.ok(!res.headersSent)
assert.strictEqual(err.message, 'broken!')
done();
});
req.socket.destroy(new Error('broken!'))
});
request(app)
.get('/')
.end(function(){});
})
describeAsyncHooks('async local storage', function () {
it('should presist store', function (done) {
var app = express()
var cb = after(2, done)
var store = { foo: 'bar' }
app.use(function (req, res, next) {
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
app.use(function (req, res) {
res.sendfile('test/fixtures/name.txt', function (err) {
if (err) return cb(err)
var local = req.asyncLocalStorage.getStore()
assert.strictEqual(local.foo, 'bar')
cb()
})
})
request(app)
.get('/')
.expect('Content-Type', 'text/plain; charset=UTF-8')
.expect(200, 'tobi', cb)
})
it('should presist store on error', function (done) {
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(function (req, res) {
res.sendfile('test/fixtures/does-not-exist', function (err) {
var local = req.asyncLocalStorage.getStore()
if (local) {
res.setHeader('x-store-foo', String(local.foo))
}
res.send(err ? 'got ' + err.status + ' error' : 'no error')
})
})
request(app)
.get('/')
.expect(200)
.expect('x-store-foo', 'bar')
.expect('got 404 error')
.end(done)
})
})
})
describe('.sendfile(path)', function(){
it('should not serve dotfiles', function(done){
var app = express();
app.use(function(req, res){
res.sendfile('test/fixtures/.name');
});
request(app)
.get('/')
.expect(404, done);
})
it('should accept dotfiles option', function(done){
var app = express();
app.use(function(req, res){
res.sendfile('test/fixtures/.name', { dotfiles: 'allow' });
});
request(app)
.get('/')
.expect(200)
.expect(utils.shouldHaveBody(Buffer.from('tobi')))
.end(done)
})
it('should accept headers option', function(done){
var app = express();
var headers = {
'x-success': 'sent',
'x-other': 'done'
};
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', { headers: headers });
});
request(app)
.get('/')
.expect('x-success', 'sent')
.expect('x-other', 'done')
.expect(200, done);
})
it('should ignore headers option on 404', function(done){
var app = express();
var headers = { 'x-success': 'sent' };
app.use(function(req, res){
res.sendfile('test/fixtures/user.nothing', { headers: headers });
});
request(app)
.get('/')
.expect(utils.shouldNotHaveHeader('X-Success'))
.expect(404, done);
})
it('should transfer a file', function (done) {
var app = express();
app.use(function (req, res) {
res.sendfile('test/fixtures/name.txt');
});
request(app)
.get('/')
.expect(200, 'tobi', done);
});
it('should transfer a directory index file', function (done) {
var app = express();
app.use(function (req, res) {
res.sendfile('test/fixtures/blog/');
});
request(app)
.get('/')
.expect(200, '<b>index</b>', done);
});
it('should 404 for directory without trailing slash', function (done) {
var app = express();
app.use(function (req, res) {
res.sendfile('test/fixtures/blog');
});
request(app)
.get('/')
.expect(404, done);
});
it('should transfer a file with urlencoded name', function (done) {
var app = express();
app.use(function (req, res) {
res.sendfile('test/fixtures/%25%20of%20dogs.txt');
});
request(app)
.get('/')
.expect(200, '20%', done);
});
it('should not error if the client aborts', function (done) {
var app = express();
var cb = after(2, done)
var error = null
app.use(function (req, res) {
setImmediate(function () {
res.sendfile(path.resolve(fixtures, 'name.txt'));
setTimeout(function () {
cb(error)
}, 10)
});
test.req.abort()
});
app.use(function (err, req, res, next) {
error = err
next(err)
});
var server = app.listen()
var test = request(server).get('/')
test.end(function (err) {
assert.ok(err)
server.close(cb)
})
})
describe('with an absolute path', function(){
it('should transfer the file', function(done){
var app = express();
app.use(function(req, res){
res.sendfile(path.join(__dirname, '/fixtures/user.html'))
});
request(app)
.get('/')
.expect('Content-Type', 'text/html; charset=UTF-8')
.expect(200, '<p>{{user.name}}</p>', done);
})
})
describe('with a relative path', function(){
it('should transfer the file', function(done){
var app = express();
app.use(function(req, res){
res.sendfile('test/fixtures/user.html');
});
request(app)
.get('/')
.expect('Content-Type', 'text/html; charset=UTF-8')
.expect(200, '<p>{{user.name}}</p>', done);
})
it('should serve relative to "root"', function(done){
var app = express();
app.use(function(req, res){
res.sendfile('user.html', { root: 'test/fixtures/' });
});
request(app)
.get('/')
.expect('Content-Type', 'text/html; charset=UTF-8')
.expect(200, '<p>{{user.name}}</p>', done);
})
it('should consider ../ malicious when "root" is not set', function(done){
var app = express();
app.use(function(req, res){
res.sendfile('test/fixtures/foo/../user.html');
});
request(app)
.get('/')
.expect(403, done);
})
it('should allow ../ when "root" is set', function(done){
var app = express();
app.use(function(req, res){
res.sendfile('foo/../user.html', { root: 'test/fixtures' });
});
request(app)
.get('/')
.expect(200, done);
})
it('should disallow requesting out of "root"', function(done){
var app = express();
app.use(function(req, res){
res.sendfile('foo/../../user.html', { root: 'test/fixtures' });
});
request(app)
.get('/')
.expect(403, done);
})
it('should next(404) when not found', function(done){
var app = express()
, calls = 0;
app.use(function(req, res){
res.sendfile('user.html');
});
app.use(function(req, res){
assert(0, 'this should not be called');
});
app.use(function(err, req, res, next){
++calls;
next(err);
});
request(app)
.get('/')
.expect(404, function (err) {
if (err) return done(err)
assert.strictEqual(calls, 1)
done()
})
})
describe('with non-GET', function(){
it('should still serve', function(done){
var app = express()
app.use(function(req, res){
res.sendfile(path.join(__dirname, '/fixtures/name.txt'))
});
request(app)
.get('/')
.expect('tobi', done);
})
})
})
})
describe('.sendfile(path, options)', function () {
it('should pass options to send module', function (done) {
var app = express()
app.use(function (req, res) {
res.sendfile(path.resolve(fixtures, 'name.txt'), { start: 0, end: 1 })
})
request(app)
.get('/')
.expect(200, 'to', done)
})
})
})
function createApp(path, options, fn) {
@ -1402,11 +910,3 @@ function createApp(path, options, fn) {
return app;
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@ -28,5 +28,17 @@ describe('res', function () {
.get('/')
.expect(599, '599', done);
})
it('should raise error for invalid status code', function (done) {
var app = express()
app.use(function (req, res) {
res.sendStatus(undefined).end()
})
request(app)
.get('/')
.expect(500, /TypeError: Invalid status code/, done)
})
})
})

View File

@ -1,55 +1,36 @@
'use strict'
var express = require('../')
var request = require('supertest')
var isIoJs = process.release
? process.release.name === 'io.js'
: ['v1.', 'v2.', 'v3.'].indexOf(process.version.slice(0, 3)) !== -1
const express = require('../.');
const request = require('supertest');
describe('res', function () {
describe('.status(code)', function () {
describe('when "code" is undefined', function () {
it('should raise error for invalid status code', function (done) {
it('should set the status code when valid', function (done) {
var app = express();
app.use(function (req, res) {
res.status(200).end();
});
request(app)
.get('/')
.expect(200, done);
});
describe('accept valid ranges', function() {
// not testing w/ 100, because that has specific meaning and behavior in Node as Expect: 100-continue
it('should set the response status code to 101', function (done) {
var app = express()
app.use(function (req, res) {
res.status(undefined).end()
res.status(101).end()
})
request(app)
.get('/')
.expect(500, /Invalid status code/, function (err) {
if (isIoJs) {
done(err ? null : new Error('expected error'))
} else {
done(err)
}
})
.expect(101, done)
})
})
describe('when "code" is null', function () {
it('should raise error for invalid status code', function (done) {
var app = express()
app.use(function (req, res) {
res.status(null).end()
})
request(app)
.get('/')
.expect(500, /Invalid status code/, function (err) {
if (isIoJs) {
done(err ? null : new Error('expected error'))
} else {
done(err)
}
})
})
})
describe('when "code" is 201', function () {
it('should set the response status code to 201', function (done) {
var app = express()
@ -61,9 +42,7 @@ describe('res', function () {
.get('/')
.expect(201, done)
})
})
describe('when "code" is 302', function () {
it('should set the response status code to 302', function (done) {
var app = express()
@ -75,9 +54,7 @@ describe('res', function () {
.get('/')
.expect(302, done)
})
})
describe('when "code" is 403', function () {
it('should set the response status code to 403', function (done) {
var app = express()
@ -89,9 +66,7 @@ describe('res', function () {
.get('/')
.expect(403, done)
})
})
describe('when "code" is 501', function () {
it('should set the response status code to 501', function (done) {
var app = express()
@ -103,100 +78,129 @@ describe('res', function () {
.get('/')
.expect(501, done)
})
})
describe('when "code" is "410"', function () {
it('should set the response status code to 410', function (done) {
it('should set the response status code to 700', function (done) {
var app = express()
app.use(function (req, res) {
res.status('410').end()
res.status(700).end()
})
request(app)
.get('/')
.expect(410, done)
.expect(700, done)
})
})
describe('when "code" is 410.1', function () {
it('should set the response status code to 410', function (done) {
it('should set the response status code to 800', function (done) {
var app = express()
app.use(function (req, res) {
res.status(410.1).end()
res.status(800).end()
})
request(app)
.get('/')
.expect(410, function (err) {
if (isIoJs) {
done(err ? null : new Error('expected error'))
} else {
done(err)
}
})
.expect(800, done)
})
})
describe('when "code" is 1000', function () {
it('should raise error for invalid status code', function (done) {
it('should set the response status code to 900', function (done) {
var app = express()
app.use(function (req, res) {
res.status(1000).end()
res.status(900).end()
})
request(app)
.get('/')
.expect(500, /Invalid status code/, function (err) {
if (isIoJs) {
done(err ? null : new Error('expected error'))
} else {
done(err)
}
})
.expect(900, done)
})
})
describe('when "code" is 99', function () {
it('should raise error for invalid status code', function (done) {
var app = express()
describe('invalid status codes', function () {
it('should raise error for status code below 100', function (done) {
var app = express();
app.use(function (req, res) {
res.status(99).end()
})
res.status(99).end();
});
request(app)
.get('/')
.expect(500, /Invalid status code/, function (err) {
if (isIoJs) {
done(err ? null : new Error('expected error'))
} else {
done(err)
}
})
})
})
.expect(500, /Invalid status code/, done);
});
describe('when "code" is -401', function () {
it('should raise error for invalid status code', function (done) {
var app = express()
it('should raise error for status code above 999', function (done) {
var app = express();
app.use(function (req, res) {
res.status(-401).end()
})
res.status(1000).end();
});
request(app)
.get('/')
.expect(500, /Invalid status code/, function (err) {
if (isIoJs) {
done(err ? null : new Error('expected error'))
} else {
done(err)
}
})
})
})
})
})
.expect(500, /Invalid status code/, done);
});
it('should raise error for non-integer status codes', function (done) {
var app = express();
app.use(function (req, res) {
res.status(200.1).end();
});
request(app)
.get('/')
.expect(500, /Invalid status code/, done);
});
it('should raise error for undefined status code', function (done) {
var app = express();
app.use(function (req, res) {
res.status(undefined).end();
});
request(app)
.get('/')
.expect(500, /Invalid status code/, done);
});
it('should raise error for null status code', function (done) {
var app = express();
app.use(function (req, res) {
res.status(null).end();
});
request(app)
.get('/')
.expect(500, /Invalid status code/, done);
});
it('should raise error for string status code', function (done) {
var app = express();
app.use(function (req, res) {
res.status("200").end();
});
request(app)
.get('/')
.expect(500, /Invalid status code/, done);
});
it('should raise error for NaN status code', function (done) {
var app = express();
app.use(function (req, res) {
res.status(NaN).end();
});
request(app)
.get('/')
.expect(500, /Invalid status code/, done);
});
});
});
});

View File

@ -14,7 +14,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'application/javascript; charset=utf-8')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.end(done)
})

View File

@ -6,7 +6,7 @@ var utils = require('./support/utils');
describe('res.vary()', function(){
describe('with no arguments', function(){
it('should not set Vary', function (done) {
it('should throw error', function (done) {
var app = express();
app.use(function (req, res) {
@ -16,8 +16,7 @@ describe('res.vary()', function(){
request(app)
.get('/')
.expect(utils.shouldNotHaveHeader('Vary'))
.expect(200, done);
.expect(500, /field.*required/, done)
})
})

View File

@ -1,4 +1,4 @@
var fs = require('fs');
var fs = require('node:fs');
var variableRegExp = /\$([0-9a-zA-Z\.]+)/g;

View File

@ -4,8 +4,7 @@
* @private
*/
var assert = require('assert');
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert');
/**
* Module exports.

View File

@ -1,7 +1,6 @@
'use strict'
var assert = require('assert');
var Buffer = require('safe-buffer').Buffer
var assert = require('node:assert');
var utils = require('../lib/utils');
describe('utils.etag(body, encoding)', function(){
@ -69,35 +68,3 @@ describe('utils.wetag(body, encoding)', function(){
'W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"')
})
})
describe('utils.isAbsolute()', function(){
it('should support windows', function(){
assert(utils.isAbsolute('c:\\'));
assert(utils.isAbsolute('c:/'));
assert(!utils.isAbsolute(':\\'));
})
it('should support windows unc', function(){
assert(utils.isAbsolute('\\\\foo\\bar'))
})
it('should support unices', function(){
assert(utils.isAbsolute('/foo/bar'));
assert(!utils.isAbsolute('foo/bar'));
})
})
describe('utils.flatten(arr)', function(){
it('should flatten an array', function(){
var arr = ['one', ['two', ['three', 'four'], 'five']];
var flat = utils.flatten(arr)
assert.strictEqual(flat.length, 5)
assert.strictEqual(flat[0], 'one')
assert.strictEqual(flat[1], 'two')
assert.strictEqual(flat[2], 'three')
assert.strictEqual(flat[3], 'four')
assert.strictEqual(flat[4], 'five')
assert.ok(flat.every(function (v) { return typeof v === 'string' }))
})
})