Compare commits

..

322 Commits

Author SHA1 Message Date
Ishita Singh
b4ab7d65d7 test: include edge case tests for res.type() (#7037) 2026-02-23 10:58:26 +01:00
Pavan Shinde
c4cc78bdf5 docs: fix README security policy link (#7029) 2026-02-21 22:15:11 -05:00
Dave Tashner
925a1dff1e fix: bump qs minimum to ^6.14.2 for CVE-2026-2391 (#7057)
qs versions before 6.14.2 have an arrayLimit bypass in comma parsing
that allows denial of service (GHSA-w7fw-mjwx-w883).

While the existing ^6.14.1 semver range allows 6.14.2 on fresh
installs, bumping the minimum ensures the vulnerable version cannot
be resolved.

Signed-off-by: davetashner <5702882+davetashner@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:11:08 -05:00
Murat Kirazkaya
9c85a25c02 Remove duplicate tests in res.location and res.jsonp (#6996)
* test: remove duplicated tests

* test: fix typo in data URI encoding test description
2026-02-14 12:25:36 -05:00
dependabot[bot]
1140301f6a build(deps): bump github/codeql-action from 4.31.9 to 4.32.0 (#7013)
* build(deps): bump github/codeql-action from 4.31.9 to 4.32.0

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.9 to 4.32.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](5d4e8d1aca...b20883b0cd)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: fix version tag comments

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Phillip Barta <barta.phillip@gmail.com>
2026-02-10 00:10:13 +01:00
dependabot[bot]
c76ed5ae05 build(deps): bump actions/setup-node from 6.1.0 to 6.2.0 (#7012)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](395ad32622...6044e13b5d)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 00:05:17 +01:00
dependabot[bot]
2d4192ebb3 build(deps): bump actions/checkout from 6.0.1 to 6.0.2 (#7011)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](8e8c483db8...de0fac2e45)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 00:04:00 +01:00
Sebastian Beltran
66404b347a docs: Add @GroophyLifefor to the triage team (#6995) 2026-02-01 13:04:08 +01:00
Viny Brun Kriesang
d12772393c fix: search example to support Redis v4+ and Express 4/5 (#6274)
* Fix Redis example to support Redis v4+ and Express 4/5

* update optional route syntax to /{:query} and refactor Redis initialization into dedicated function to guarantee that it is complete before server starts

---------

Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2026-01-31 22:12:23 -05:00
Ayoub Mabrouk
6b7ccfcf12 test: add test for normalizeType fallback when mime lookup fails (#6894)
Add test to verify that utils.normalizeType correctly defaults to
'application/octet-stream' when mime.lookup() returns null/undefined
for unknown file extensions. This covers the fallback behavior on
line 64 of lib/utils.js and ensures proper handling of unrecognized
MIME types.

Co-authored-by: bjohansebas <103585995+bjohansebas@users.noreply.github.com>
2026-01-31 21:53:38 -05:00
AkaHarshit
c9ecf7b658 feat: Allow passing null or undefined as the value for options in app.render (#6903)
* fix: allow null options in app.render

* fix: ensure options are initialized to an empty object in app.render

* docs: add history entry

---------

Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2026-01-31 21:51:17 -05:00
Sebastian Beltran
a479419b16 feat: do not modify the Content-Type twice when sending strings (#6991)
* fix: improve content-type handling in res.send method

* fix: ensure content-type is a string before setting charset in res.send

* fix: refactor content-type handling in res.send to use const and improve clarity

* Apply suggestion from @bjohansebas

* docs: update History.md
2026-01-19 09:56:53 -05:00
Sebastian Beltran
5a4568abfe chore: remove benchmarks directory (#6992) 2026-01-17 17:36:22 -05:00
sukdev24
912893c07c test: added unit tests for utils.compileETag to cover valid and invalid inputs (#6534)
* Added unit tests for utils.compileETag to cover valid and invalid inputs

* test: enhance compileETag tests for various input types

---------

Co-authored-by: sucem029 <sucem029@vippan-118.ad.liu.se>
Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2026-01-16 21:27:22 -05:00
Marcos Molina
ae265a90c7 docs: fix JSDoc for req.accepts() return value and parameter format (#6936)
* fixed request accept jsdoc

* reverted format

* reverted format

* updated jsdoc

* updated the rest of the documentation
2026-01-16 16:19:39 -05:00
Bernice Wu
9a3f7ff412 Polish HTML structure of the response in the res.redirect() function (#5167)
* structure the DOM body

* structure the DOM body

* test: add html title to redirect test

* fix: update HTML structure for include body and head tags

* docs: improve HTML structure in res.redirect() responses for better browser compatibility

---------

Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2026-01-16 10:29:01 -05:00
Sebastian Beltran
2cd372e34c docs: add @krzysdz to the triage team (#6482) 2026-01-12 10:28:54 +01:00
dependabot[bot]
04d3a49976 build(deps): bump actions/setup-node from 6.0.0 to 6.1.0 (#6962)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](2028fbc5c2...395ad32622)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 09:48:47 -05:00
dependabot[bot]
bc7d155f53 build(deps): bump actions/checkout from 6.0.0 to 6.0.1 (#6963)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](1af3b93b68...8e8c483db8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 09:47:41 -05:00
Gabriel Alves
00bb633ca6 deps: qs@^6.14.1 2026-01-07 15:46:37 +01:00
dependabot[bot]
3c0ad4e8dc build(deps): bump github/codeql-action from 4.31.6 to 4.31.9 (#6964)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.6 to 4.31.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](fe4161a26a...5d4e8d1aca)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 09:46:20 -05:00
dependabot[bot]
4ae96bdf5e build(deps): bump actions/upload-artifact from 5.0.0 to 6.0.0 (#6965)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](330a01c490...b7c566a772)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 09:45:47 -05:00
AbdelMonaam Aouini
6cd404eb28 fix: enhance req.acceptsCharsets method (#6088)
* fix: enhance req.acceptsCharsets method

* Update req.acceptsCharsets.js

---------

Co-authored-by: Monaam Aouini <abdelmonaem.aouini@mispay.co>
Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2026-01-07 09:41:34 -05:00
dependabot[bot]
3e81873b52 build(deps): bump actions/download-artifact from 6.0.0 to 7.0.0 (#6961)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](018cc2cf5b...37930b1c2a)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 09:38:53 -05:00
Jon Church
b5aae87594 doc: fix security.md link to point to security tab 2026-01-05 17:54:09 -05:00
Ulises Gascon
b8fc000f31 docs: use global Security policy
We should inherit https://github.com/expressjs/.github/blob/master/SECURITY.md directly.
2026-01-05 17:46:28 -05:00
Rick Markins
c2fb76e99f docs: add @rxmarbles to triagers (#6953) 2025-12-19 10:44:49 +01:00
ctcpip
9eb700151b 📝 add note to history 2025-12-09 09:32:11 -06:00
Ulises Gascon
dbac741a49 5.2.1 2025-12-01 15:27:35 -05:00
Ulises Gascon
697547cde6 Revert "sec: security patch for CVE-2024-51999"
This reverts commit 2f64f68c37.
2025-12-01 15:27:35 -05:00
Ulises Gascón
4007ad103b Release: 5.2.0 (#6920) 2025-12-01 17:17:31 +01:00
Chris de Almeida
2f64f68c37 sec: security patch for CVE-2024-51999 2025-12-01 17:15:17 +01:00
dependabot[bot]
ed0ba3f1dc build(deps): bump actions/checkout from 5.0.0 to 6.0.0 (#6928)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](08c6903cd8...1af3b93b68)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 13:13:45 +01:00
dependabot[bot]
8eace4603c build(deps): bump github/codeql-action from 4.31.2 to 4.31.6 (#6929)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.2 to 4.31.6.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](0499de31b9...fe4161a26a)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 13:12:52 +01:00
dependabot[bot]
30bae81027 build(deps): bump coverallsapp/github-action from 2.3.6 to 2.3.7 (#6930)
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.3.6 to 2.3.7.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](648a8eb78e...5cbfd81b66)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-version: 2.3.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 13:12:08 +01:00
Shivam Sharma
758d4355d4 deps: body-parser@^2.2.1 (#6922)
Includes the security patch for CVE-2025-13466
2025-11-26 15:19:57 +01:00
Sebastian Beltran
77bcd5274a docs: update emeritus triagers (#6890)
* docs: update emeritus triagers
---------

Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2025-11-25 11:32:32 +01:00
Sebastian Beltran
f33caf1f89 Nominate to @efekrskl for triage team (#6888)
* Nominate to @efekrskl for triage team

* Update Readme.md
2025-11-24 22:32:28 -05:00
TheMysterious
54af593b73 refactor: use cached slice in app.listen (#6897)
Signed-off-by: Tacit1 <tacitim5@gmail.com>
2025-11-23 18:52:16 -05:00
Phillip Barta
2551a7d8af docs: switch badges from badgen.net to shields.io (#6900) 2025-11-18 10:42:56 +01:00
dependabot[bot]
4453d83cca build(deps): bump actions/upload-artifact from 4.6.2 to 5.0.0 (#6868)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 10:46:37 +01:00
dependabot[bot]
db507669ca build(deps): bump github/codeql-action from 3.30.5 to 4.31.2 (#6869)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 10:45:51 +01:00
dependabot[bot]
374fc1a0f9 build(deps): bump actions/setup-node from 5.0.0 to 6.0.0 (#6870)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 10:45:07 +01:00
dependabot[bot]
1b196c8b82 build(deps): bump actions/download-artifact from 5.0.0 to 6.0.0 (#6871)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 10:44:12 +01:00
Phillip Barta
64e7373d69 ci: add node.js 25 to test matrix (#6843) 2025-10-16 13:51:39 +02:00
dependabot[bot]
e4fb370ad8 build(deps): bump actions/download-artifact from 4.3.0 to 5.0.0 (#6793)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.3.0 to 5.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](d3f86a106a...634f93cb29)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 14:44:02 +02:00
dependabot[bot]
60d4c16cc9 build(deps): bump actions/setup-node from 4.4.0 to 5.0.0 (#6794)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.4.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](49933ea528...a0853c2454)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 14:43:54 +02:00
dependabot[bot]
9e6760e186 build(deps): bump ossf/scorecard-action from 2.4.2 to 2.4.3 (#6795)
Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.2 to 2.4.3.
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](05b42c6244...4eaacf0543)

---
updated-dependencies:
- dependency-name: ossf/scorecard-action
  dependency-version: 2.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 14:43:45 +02:00
dependabot[bot]
ffa89f2ccf build(deps): bump github/codeql-action from 3.29.7 to 3.30.5 (#6796)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.7 to 3.30.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](51f77329af...3599b3baa1)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 14:40:08 +02:00
dependabot[bot]
b9b9f52b2f build(deps): bump actions/checkout from 4.2.2 to 5.0.0 (#6797)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](11bd71901b...08c6903cd8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 14:34:25 +02:00
Jean Burellier
9a7afb2886 chore: remove history.md from being packaged on publish (#6780) 2025-09-25 07:20:45 +02:00
dependabot[bot]
2eb42059f3 build(deps): bump github/codeql-action from 3.29.2 to 3.29.5 (#6675)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.2 to 3.29.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](181d5eefc2...51f77329af)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Co-authored-by: bjohansebas <103585995+bjohansebas@users.noreply.github.com>
2025-09-14 09:39:32 -05:00
Shivam Sharma
aa907945cd doc: update express app example (#6718) 2025-08-22 09:12:09 +02:00
Sebastian Beltran
89f198c6a5 lib: use req.socket over deprecated req.connection (#6705)
Signed-off-by: Sebastian Beltran <bjohansebas@gmail.com>
2025-08-21 10:05:29 -05:00
Shivam Sharma
d9a62f9833 chore: update git rules to ignore yarn.lock file (#6588)
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2025-08-20 15:18:36 +02:00
Shivam Sharma
8f21493cc5 lint: add --fix flag to automatic fix linting issue (#6644)
* lint: add --fix flag to automatic fix linting issue

* add dedicated lint script
2025-08-12 17:58:13 -05:00
dependabot[bot]
6616e39d4d build(deps-dev): bump cookie-session from 2.1.0 to 2.1.1 (#6678)
Bumps [cookie-session](https://github.com/expressjs/cookie-session) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/expressjs/cookie-session/releases)
- [Changelog](https://github.com/expressjs/cookie-session/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/cookie-session/compare/v2.1.0...v2.1.1)

---
updated-dependencies:
- dependency-name: cookie-session
  dependency-version: 2.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 20:24:48 -05:00
dependabot[bot]
ed64290e4a build(deps-dev): bump morgan from 1.10.0 to 1.10.1 (#6679)
Bumps [morgan](https://github.com/expressjs/morgan) from 1.10.0 to 1.10.1.
- [Release notes](https://github.com/expressjs/morgan/releases)
- [Changelog](https://github.com/expressjs/morgan/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/morgan/compare/1.10.0...1.10.1)

---
updated-dependencies:
- dependency-name: morgan
  dependency-version: 1.10.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 20:24:22 -05:00
shubham oulkar
b52ff7ca60 update contributing and COC links (#6601) 2025-07-30 08:41:53 -05:00
Jon Church
9420cd3f9b doc: fix the Contributing.md link (#6653) 2025-07-21 19:14:33 -04:00
Sebastian Beltran
ef5f2e13ef ci: run CI when the markdown changes (#6632) 2025-07-15 21:27:50 -05:00
Sebastian Beltran
c5b8d55a6a feat: add deprecation warnings for redirect arguments undefined (#6405) 2025-07-14 22:18:10 -05:00
dependabot[bot]
7a9311216a build(deps): bump github/codeql-action from 3.28.18 to 3.29.2 (#6618)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.18 to 3.29.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](ff0a06e83c...181d5eefc2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 10:27:41 +02:00
mountdisk
b0ed15b452 chore: fix typo (#6609)
Signed-off-by: mountdisk <mountdisk@icloud.com.>
2025-07-04 09:47:31 -05:00
Shivam Sharma
3910323d09 chore: use node protocol for node builtin module (#6520)
Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2025-07-02 21:33:10 -05:00
Shivam Sharma
98c85eb0dd chore: enforce explicit Buffer import and add lint rule (#6525)
Signed-off-by: Shivam Sharma <meshivam81@gmail.com>
2025-06-27 20:10:36 -05:00
dependabot[bot]
a039e49175 build(deps): bump ossf/scorecard-action from 2.4.1 to 2.4.2 (#6548)
Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.1 to 2.4.2.
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](f49aabe0b5...05b42c6244)

---
updated-dependencies:
- dependency-name: ossf/scorecard-action
  dependency-version: 2.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 23:36:52 +02:00
dependabot[bot]
ffc562c7d1 build(deps): bump github/codeql-action from 3.28.16 to 3.28.18 (#6549)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.16 to 3.28.18.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](28deaeda66...ff0a06e83c)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 23:36:20 +02:00
Sebastian Beltran
52872b84ca fix(docs): move documentation and charters to the discussions and .github … (#6427)
* docs: move documentation and charters to the discussions and .github repository

* Update Readme.md
2025-06-09 15:34:53 -05:00
kgarg1
b8ab46594d test: add coverage for app.listen() variants (#6476)
* test: add coverage for app.listen() variants

- verify alternate signatures (port+host+backlog)
- verify server.address() shape

* fix linter issue

---------

Co-authored-by: kuldeep <kuldeep@wanclouds.net>
2025-05-28 19:26:16 -05:00
Shivam Sharma
fedd60e642 ci: allow manual triggering of workflow (#6515) 2025-05-28 19:16:44 -05:00
Mert Şişmanoğlu
99a0bd3354 ci: disable credential persistence for checkout actions (#6522)
Signed-off-by: Mert Şişmanoğlu <mertssmnoglu@gmail.com>
2025-05-28 18:55:05 -05:00
Noritaka Kobayashi
dfd1851245 test: fix typos in test descriptions (#6535) 2025-05-27 10:36:50 +02:00
Jon Church
9f4dbe3a13 chore: wider range for query test skip (#6512) 2025-05-15 11:40:26 -05:00
Phillip Barta
9784321e89 ci: update codeql config (#6488) 2025-05-09 15:29:27 +02:00
Phillip Barta
ee1ef41bd3 ci: add node.js 24 to test matrix (#6504) 2025-05-07 21:08:25 -05:00
dependabot[bot]
1ca803dd55 build(deps): bump actions/download-artifact from 4.2.1 to 4.3.0 (#6496)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.2.1 to 4.3.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](95815c38cf...d3f86a106a)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 23:02:23 +02:00
dependabot[bot]
73555815b9 build(deps): bump actions/setup-node from 4.3.0 to 4.4.0 (#6497)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.3.0 to 4.4.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](cdca7365b2...49933ea528)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 4.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 23:02:09 +02:00
dependabot[bot]
a1161b4686 build(deps): bump github/codeql-action from 3.28.13 to 3.28.16 (#6498)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.13 to 3.28.16.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](1b549b9259...28deaeda66)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 23:01:58 +02:00
dufucun
f9954dd317 fix(test): remove duplicate word (#6456)
Signed-off-by: dufucun <dufuchun@sohu.com>
2025-04-16 11:18:38 -05:00
Ashish Sharma
5da5a11a49 increased code coverage of utils.js file (#6386)
Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2025-04-10 20:57:43 -05:00
Ayoub Mabrouk
3dc96995df Refactor: simplify acceptsLanguages implementation using spread operator (#6137)
Refactored `req.acceptsLanguages` to use the spread operator for passing arguments directly to `accept.languages`, eliminating the need for `.apply`. This approach improves readability and streamlines the function call.
2025-04-10 20:49:23 -05:00
dependabot[bot]
fa40ecfe76 build(deps): bump github/codeql-action from 3.28.11 to 3.28.13 (#6429) 2025-04-04 21:53:14 -05:00
Wes Todd
cd7d4397c3 5.1.0 2025-03-28 13:29:16 -05:00
Ulises Gascón
4c4f3ea105 fix(deps): serve-static@^2.2.0 (#6418)
* Update package.json

* Update History.md

---------

Co-authored-by: Wes Todd <wes@wesleytodd.com>
2025-03-27 19:46:06 -05:00
Mert Can Altin
cb4c56e9a7 fix(docs): remove @mertcanaltin from Triagers (#6408)
* doc: remove @mertcanaltin from Triagers

* move emeritus

---------

Co-authored-by: Mert Can Altin <mert.altin@trendyol.com>
Co-authored-by: Wes Todd <wes@wesleytodd.com>
2025-03-27 18:43:18 -05:00
Phillip9587
7b44e1d850 ci: use full SHAs for github action versions
Ref: https://github.com/expressjs/security-wg/issues/2
2025-03-27 15:46:29 +01:00
Ulises Gascón
eb6d12587a deps: router@^2.2.0 (#6417) 2025-03-27 02:47:40 +01:00
Ulises Gascón
f1a2dc884d deps: type-is@^2.0.1 (#6420) 2025-03-27 02:32:28 +01:00
Ulises Gascón
6b51e8ef97 deps: body-parser@^2.2.0 (#6419) 2025-03-27 02:31:57 +01:00
dependabot[bot]
1f311c59d4 build(deps-dev): bump cookie-session from 2.0.0 to 2.1.0 (#6399)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 20:08:24 -05:00
Wes Todd
9e97144222 feat(deps): finalhandler@2.1.0 (#6373)
* feat(deps): finalhandler@2.1.0

* Update History.md

Co-authored-by: Phillip Barta <barta.phillip@gmail.com>

---------

Co-authored-by: Phillip Barta <barta.phillip@gmail.com>
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2025-03-18 19:12:45 -05:00
dependabot[bot]
29d09803c1 build(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.1 (#6397)
Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](62b2cac7ed...f49aabe0b5)

---
updated-dependencies:
- dependency-name: ossf/scorecard-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-18 17:12:20 +01:00
dependabot[bot]
1d63162dbf build(deps): bump github/codeql-action from 3.24.7 to 3.28.11 (#6398)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.7 to 3.28.11.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3.24.7...6bb031afdd8eb862ea3fc1848194185e076637e5)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-18 17:11:59 +01:00
Sebastian Beltran
4a2175dfc9 fix dependabot config (#6392) 2025-03-16 17:44:57 -05:00
Ulises Gascón
0bb00e1906 ci: add dependabot (#5435)
Co-authored-by: Shivam Sharma <meshivam81@gmail.com>
Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
2025-03-11 22:50:59 +01:00
Wes Todd
1e359f57fc fix(deps): qs@^6.14.0 (#6374) 2025-03-11 22:49:58 +01:00
Ulises Gascón
9cbe2c2cbb docs: add @dpopp07 to the triage team (#6352) 2025-03-11 22:49:08 +01:00
Sebastian Beltran
35e15362ab fix(docs): clarify guidelines for becoming a committer (#6364)
* docs: clarify guidelines for becoming a committer

* Update Contributing.md

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

---------

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2025-03-05 09:11:29 -06:00
Sebastian Beltran
90e522ac90 fix(docs): update guidance for triager nominations (#6349)
* docs: update guidance for triager nominations

* Update Contributing.md

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

* Update Contributing.md

---------

Co-authored-by: Wes Todd <wes@wesleytodd.com>
2025-03-04 08:09:18 -06:00
Ulises Gascón
59703c2321 docs: update emeritus triagers (#6345) 2025-02-27 22:47:13 +01:00
Andrea Polverino
caa4f68ee8 feat: Extend res.links() to allow adding multiple links with the same rel (closes #2729) (#4885) 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
2a980ad160 merge-descriptors@1.0.3 (#5781)
* Allow patches for `merge-descriptors` dependency

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

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

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

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

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

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

* update History.md for clearCookie deprecation change

* add tests to codify deprecated behavior

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

---------

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

* doc: updated readme file for doc lint

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* repair readme

* added Emeritus area

* Add @carpasse to the triager team

* removed old collaborators

* add missing triagers

* lint

* Update Readme.md

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

* Update Readme.md

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

* dedent to fix ToC spacing

* fixup! dedent to fix ToC spacing

* us @ for jonchurch

* format names to use github handles first, single line

* added emeritus triagers

* edited title

* added emeritus team members

* added menu head

* edited emeritus

* Update Readme.md

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

* Update Readme.md

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

* edits to TC and anchors

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

* Update Readme.md

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

---------

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

---------

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

* docs: update reference

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

* docs: improve readability

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

* docs: add reference to the Threat Model

---------

Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-04-04 13:01:35 +02:00
Jon Church
6da57c7819 remove examples from the ignore 2024-03-27 09:57:44 -05:00
Jon Church
d546f93f2f add develop to branches 2024-03-27 09:57:44 -05:00
Jon Church
4771ba2bc3 crib fastify's ci concurrency logic
76674fdf46/.github/workflows/ci.yml (L18)
2024-03-27 09:57:44 -05:00
Jon Church
3ae704f67f update ci push trigger only to some branches, ignore examples, .md 2024-03-27 09:57:44 -05:00
Marco Ippolito
8b6d34963d Update Security.md
Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-03-27 09:57:09 -05:00
Marco Ippolito
36b8148110 Update Security.md
Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-03-27 09:57:09 -05:00
Marco Ippolito
6d98d2e110 Update Release-Process.md
Co-authored-by: Chris de Almeida <ctcpip@users.noreply.github.com>
2024-03-27 09:57:09 -05:00
marco-ippolito
51a76366e3 refactor: reword to pre-releases 2024-03-27 09:57:09 -05:00
Marco Ippolito
4e3f95c0ea Update Security.md
Co-authored-by: Wes Todd <wes@wesleytodd.com>
2024-03-27 09:57:09 -05:00
Marco Ippolito
88bd6d8e3a Update Release-Process.md
Co-authored-by: Wes Todd <wes@wesleytodd.com>
2024-03-27 09:57:09 -05:00
marco-ippolito
51595d402b feat: document beta releases expectations 2024-03-27 09:57:09 -05:00
Wes Todd
94669f9289 remove duplicate location test for data uri 2024-03-26 09:41:06 -05:00
Wes Todd
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
b28db2c12c 4.19.2 2024-03-25 09:29:41 -05:00
Wes Todd
0b746953c4 Improved fix for open redirect allow list bypass
Co-authored-by: Jon Church <me@jonchurch.com>
Co-authored-by: Blake Embrey <hello@blakeembrey.com>
2024-03-25 09:29:41 -05:00
Wes Todd
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
110 changed files with 2819 additions and 5157 deletions

View File

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

View File

@@ -1,8 +1,14 @@
root: true
env:
es2022: true
node: true
rules:
eol-last: error
eqeqeq: [error, allow-null]
indent: [error, 2, { MemberExpression: "off", SwitchCase: 1 }]
no-trailing-spaces: error
no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }]
no-restricted-globals:
- error
- name: Buffer
message: Use `import { Buffer } from "node:buffer"` instead of the global Buffer.

17
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
- package-ecosystem: npm
directory: /
schedule:
interval: monthly
time: "23:00"
timezone: Europe/London
open-pull-requests-limit: 10
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]

View File

@@ -1,218 +1,117 @@
name: ci
on:
- pull_request
- push
push:
branches:
- master
- develop
- '4.x'
- '5.x'
- '5.0'
paths-ignore:
- '*.md'
pull_request:
workflow_dispatch:
permissions:
contents: read
# Cancel in progress workflows
# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run
concurrency:
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
cancel-in-progress: true
jobs:
test:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
- name: Install dependencies
run: npm install --ignore-scripts --include=dev
- name: Run lint
run: npm run lint
test:
strategy:
fail-fast: false
matrix:
name:
- Node.js 0.10
- Node.js 0.12
- io.js 1.x
- io.js 2.x
- io.js 3.x
- Node.js 4.x
- Node.js 5.x
- Node.js 6.x
- Node.js 7.x
- Node.js 8.x
- Node.js 9.x
- Node.js 10.x
- Node.js 11.x
- Node.js 12.x
- Node.js 13.x
- Node.js 14.x
- Node.js 15.x
- Node.js 16.x
- Node.js 17.x
- Node.js 18.x
- Node.js 19.x
- Node.js 20.x
- Node.js 21.x
os: [ubuntu-latest, windows-latest]
node-version: [18, 19, 20, 21, 22, 23, 24, 25]
# Node.js release schedule: https://nodejs.org/en/about/releases/
include:
- name: Node.js 0.10
node-version: "0.10"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
- name: Node.js 0.12
node-version: "0.12"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
- name: io.js 1.x
node-version: "1.8"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
- name: io.js 2.x
node-version: "2.5"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
- name: io.js 3.x
node-version: "3.3"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.0
- name: Node.js 4.x
node-version: "4.9"
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
- name: Node.js 5.x
node-version: "5.12"
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
- name: Node.js 6.x
node-version: "6.17"
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2
- name: Node.js 7.x
node-version: "7.10"
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6
- name: Node.js 8.x
node-version: "8.17"
npm-i: mocha@7.2.0 nyc@14.1.1
- name: Node.js 9.x
node-version: "9.11"
npm-i: mocha@7.2.0 nyc@14.1.1
- name: Node.js 10.x
node-version: "10.24"
npm-i: mocha@8.4.0
- name: Node.js 11.x
node-version: "11.15"
npm-i: mocha@8.4.0
- name: Node.js 12.x
node-version: "12.22"
npm-i: mocha@9.2.2
- name: Node.js 13.x
node-version: "13.14"
npm-i: mocha@9.2.2
- name: Node.js 14.x
node-version: "14.20"
- name: Node.js 15.x
node-version: "15.14"
- name: Node.js 16.x
node-version: "16.20"
- name: Node.js 17.x
node-version: "17.9"
- name: Node.js 18.x
node-version: "18.19"
- name: Node.js 19.x
node-version: "19.9"
- name: Node.js 20.x
node-version: "20.11"
- name: Node.js 21.x
node-version: "21.6"
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Node.js ${{ matrix.node-version }}
shell: bash -eo pipefail -l {0}
run: |
nvm install --default ${{ matrix.node-version }}
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: ${{ matrix.node-version }}
- name: Configure npm
run: |
npm config set loglevel error
if [[ "$(npm config get package-lock)" == "true" ]]; then
npm config set package-lock false
else
npm config set shrinkwrap false
fi
- name: Configure npm loglevel
run: |
npm config set loglevel error
shell: bash
- name: Install npm module(s) ${{ matrix.npm-i }}
run: npm install --save-dev ${{ matrix.npm-i }}
if: matrix.npm-i != ''
- name: Install dependencies
run: npm install
- 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)"
echo "NPM version: $(npm -v)"
- name: Setup Node.js version-specific dependencies
shell: bash
run: |
# eslint for linting
# - remove on Node.js < 12
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
grep -E '^eslint(-|$)' | \
sort -r | \
xargs -n1 npm rm --silent --save-dev
fi
- name: Run tests
shell: bash
run: npm run test-ci
- name: Install Node.js dependencies
run: npm install
- name: List environment
id: list_env
shell: bash
run: |
echo "node@$(node -v)"
echo "npm@$(npm -v)"
npm -s ls ||:
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT"
- name: Run tests
shell: bash
run: |
npm run test-ci
cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov"
- name: Lint code
if: steps.list_env.outputs.eslint != ''
run: npm run lint
- name: Collect code coverage
run: |
mv ./coverage "./${{ matrix.name }}"
mkdir ./coverage
mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}"
- name: Upload code coverage
uses: actions/upload-artifact@v3
with:
name: coverage
path: ./coverage
retention-days: 1
- name: Upload code coverage
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
path: ./coverage/lcov.info
retention-days: 1
coverage:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- 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@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: ./coverage
pattern: coverage-node-*
- name: Merge coverage reports
shell: bash
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./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@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
with:
file: ./lcov.info

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

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

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

@@ -0,0 +1,101 @@
name: legacy
on:
push:
branches:
- master
- develop
- '4.x'
- '5.x'
- '5.0'
paths-ignore:
- '*.md'
pull_request:
paths-ignore:
- '*.md'
workflow_dispatch:
permissions:
contents: read
# Cancel in progress workflows
# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run
concurrency:
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
cancel-in-progress: true
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [16, 17]
# Node.js release schedule: https://nodejs.org/en/about/releases/
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: ${{ matrix.node-version }}
- name: Configure npm loglevel
run: |
npm config set loglevel error
shell: bash
- name: Install dependencies
run: npm install
- name: Output Node and NPM versions
run: |
echo "Node.js version: $(node -v)"
echo "NPM version: $(npm -v)"
- name: Run tests
shell: bash
run: npm run test-ci
- name: Upload code coverage
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
path: ./coverage/lcov.info
retention-days: 1
coverage:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install lcov
shell: bash
run: sudo apt-get -y install lcov
- name: Collect coverage reports
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: ./coverage
pattern: coverage-node-*
- name: Merge coverage reports
shell: bash
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
- name: Upload coverage report
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
with:
file: ./lcov.info

72
.github/workflows/scorecard.yml vendored Normal file
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
with:
sarif_file: results.sarif

5
.gitignore vendored
View File

@@ -1,9 +1,14 @@
# npm
node_modules
package-lock.json
npm-shrinkwrap.json
*.log
*.gz
# Yarn
yarn-error.log
yarn.lock
# Coveralls
.nyc_output
coverage

1
.npmrc Normal file
View File

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

View File

@@ -1,92 +0,0 @@
# Express Charter
## Section 0: Guiding Principles
The Express project is part of the OpenJS Foundation which operates
transparently, openly, collaboratively, and ethically.
Project proposals, timelines, and status must not merely be open, but
also easily visible to outsiders.
## Section 1: Scope
Express is a HTTP web server framework with a simple and expressive API
which is highly aligned with Node.js core. We aim to be the best in
class for writing performant, spec compliant, and powerful web servers
in Node.js. As one of the oldest and most popular web frameworks in
the ecosystem, we have an important place for new users and experts
alike.
### 1.1: In-scope
Express is made of many modules spread between three GitHub Orgs:
- [expressjs](http://github.com/expressjs/): Top level middleware and
libraries
- [pillarjs](http://github.com/pillarjs/): Components which make up
Express but can also be used for other web frameworks
- [jshttp](http://github.com/jshttp/): Low level HTTP libraries
### 1.2: Out-of-Scope
Section Intentionally Left Blank
## Section 2: Relationship with OpenJS Foundation CPC.
Technical leadership for the projects within the OpenJS Foundation is
delegated to the projects through their project charters by the OpenJS
Cross Project Council (CPC). In the case of the Express project, it is
delegated to the Express Technical Committee ("TC").
This Technical Committee is in charge of both the day-to-day operations
of the project, as well as its technical management. This charter can
be amended by the TC requiring at least two approvals and a minimum two
week comment period for other TC members or CPC members to object. Any
changes the CPC wishes to propose will be considered a priority but
will follow the same process.
### 2.1 Other Formal Project Relationships
Section Intentionally Left Blank
## Section 3: Express Governing Body
The Express project is managed by the Technical Committee ("TC").
Members can be added to the TC at any time. Any committer can nominate
another committer to the TC and the TC uses its standard consensus
seeking process to evaluate whether or not to add this new member.
Members who do not participate consistently at the level of a majority
of the other members are expected to resign.
## Section 4: Roles & Responsibilities
The Express TC manages all aspects of both the technical and community
parts of the project. Members of the TC should attend the regular
meetings when possible, and be available for discussion of time
sensitive or important issues.
### Section 4.1 Project Operations & Management
Section Intentionally Left Blank
### Section 4.2: Decision-making, Voting, and/or Elections
The Express TC uses a "consensus seeking" process for issues that are
escalated to the TC. The group tries to find a resolution that has no
open objections among TC members. If a consensus cannot be reached
that has no objections then a majority wins vote is called. It is also
expected that the majority of decisions made by the TC are via a
consensus seeking process and that voting is only used as a last-resort.
Resolution may involve returning the issue to committers with
suggestions on how to move forward towards a consensus. It is not
expected that a meeting of the TC will resolve all issues on its
agenda during that meeting and may prefer to continue the discussion
happening among the committers.
### Section 4.3: Other Project Roles
Section Intentionally Left Blank
## Section 5: Definitions
Section Intentionally Left Blank

View File

@@ -1,139 +0,0 @@
# Contributor Covenant Code of Conduct
As a member of the Open JS Foundation, Express has adopted the
[Contributor Covenant 2.0][cc-20-doc].
If an issue arises and you cannot resolve it directly with the parties
involved, you can report it to the Express project TC through the following
email: express-coc@lists.openjsf.org
In addition, the OpenJS Foundation maintains a Code of Conduct Panel (CoCP).
This is a foundation-wide team established to manage escalation when a reporter
believes that a report to a member project or the CPC has not been properly
handled. In order to escalate to the CoCP send an email to
coc-escalation@lists.openjsf.org.
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances
of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for
moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail
address, posting via an official social media account, or acting as an
appointed representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
express-coc@lists.openjsf.org. All complaints will be reviewed and
investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited
interaction with those enforcing the Code of Conduct, is allowed during this
period. Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
project community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant, version 2.0](cc-20-doc).
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
[cc-20-doc]: https://www.contributor-covenant.org/version/2/0/code_of_conduct/
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -1,51 +0,0 @@
# Express Collaborator Guide
## Website Issues
Open issues for the expressjs.com website in https://github.com/expressjs/expressjs.com.
## PRs and Code contributions
* Tests must pass.
* Follow the [JavaScript Standard Style](http://standardjs.com/) and `npm run lint`.
* If you fix a bug, add a test.
## Branches
Use the `master` branch for bug fixes or minor work that is intended for the
current release stream.
Use the correspondingly named branch, e.g. `5.0`, for anything intended for
a future release of Express.
## Steps for contributing
1. [Create an issue](https://github.com/expressjs/express/issues/new) for the
bug you want to fix or the feature that you want to add.
2. Create your own [fork](https://github.com/expressjs/express) on GitHub, then
checkout your fork.
3. Write your code in your local copy. It's good practice to create a branch for
each new issue you work on, although not compulsory.
4. To run the test suite, first install the dependencies by running `npm install`,
then run `npm test`.
5. Ensure your code is linted by running `npm run lint` -- fix any issue you
see listed.
6. If the tests pass, you can commit your changes to your fork and then create
a pull request from there. Make sure to reference your issue from the pull
request comments by including the issue number e.g. `#123`.
## Issues which are questions
We will typically close any vague issues or questions that are specific to some
app you are writing. Please double check the docs and other references before
being trigger happy with posting a question issue.
Things that will help get your question issue looked at:
* Full and runnable JS code.
* Clear description of the problem or unexpected behavior.
* Clear description of the expected result.
* Steps you have taken to debug it yourself.
If you post a question and do not outline the above items or make it easy for
us to understand and reproduce your issue, it will be closed.

View File

@@ -1,176 +0,0 @@
# Express.js Community Contributing Guide 1.0
The goal of this document is to create a contribution process that:
* Encourages new contributions.
* Encourages contributors to remain involved.
* Avoids unnecessary processes and bureaucracy whenever possible.
* Creates a transparent decision making process that makes it clear how
contributors can be involved in decision making.
## Vocabulary
* A **Contributor** is any individual creating or commenting on an issue or pull request.
* A **Committer** is a subset of contributors who have been given write access to the repository.
* A **Project Captain** is the lead maintainer of a repository.
* A **TC (Technical Committee)** is a group of committers representing the required technical
expertise to resolve rare disputes.
* A **Triager** is a subset of contributors who have been given triage access to the repository.
## Logging Issues
Log an issue for any question or problem you might have. When in doubt, log an issue, and
any additional policies about what to include will be provided in the responses. The only
exception is security disclosures which should be sent privately.
Committers may direct you to another repository, ask for additional clarifications, and
add appropriate metadata before the issue is addressed.
Please be courteous and respectful. Every participant is expected to follow the
project's Code of Conduct.
## Contributions
Any change to resources in this repository must be through pull requests. This applies to all changes
to documentation, code, binary files, etc. Even long term committers and TC members must use
pull requests.
No pull request can be merged without being reviewed.
For non-trivial contributions, pull requests should sit for at least 36 hours to ensure that
contributors in other timezones have time to review. Consideration should also be given to
weekends and other holiday periods to ensure active committers all have reasonable time to
become involved in the discussion and review process if they wish.
The default for each contribution is that it is accepted once no committer has an objection.
During a review, committers may also request that a specific contributor who is most versed in a
particular area gives a "LGTM" before the PR can be merged. There is no additional "sign off"
process for contributions to land. Once all issues brought by committers are addressed it can
be landed by any committer.
In the case of an objection being raised in a pull request by another committer, all involved
committers should seek to arrive at a consensus by way of addressing concerns being expressed
by discussion, compromise on the proposed change, or withdrawal of the proposed change.
If a contribution is controversial and committers cannot agree about how to get it to land
or if it should land then it should be escalated to the TC. TC members should regularly
discuss pending contributions in order to find a resolution. It is expected that only a
small minority of issues be brought to the TC for resolution and that discussion and
compromise among committers be the default resolution mechanism.
## Becoming a Triager
Anyone can become a triager! Read more about the process of being a triager in
[the triage process document](Triager-Guide.md).
[Open an issue in `expressjs/express` repo](https://github.com/expressjs/express/issues/new)
to request the triage role. State that you have read and agree to the
[Code of Conduct](Code-Of-Conduct.md) and details of the role.
Here is an example issue content you can copy and paste:
```
Title: Request triager role for <your GitHub username>
I have read and understood the project's Code of Conduct.
I also have read and understood the process and best practices around Express triaging.
I request for a triager role for the following GitHub organizations:
jshttp
pillarjs
express
```
Once you have opened your issue, a member of the TC will add you to the `triage` team in
the organizations requested. They will then close the issue.
Happy triaging!
## Becoming a Committer
All contributors who land a non-trivial contribution should be on-boarded in a timely manner,
and added as a committer, and be given write access to the repository.
Committers are expected to follow this policy and continue to send pull requests, go through
proper review, and have other committers merge their pull requests.
## TC Process
The TC uses a "consensus seeking" process for issues that are escalated to the TC.
The group tries to find a resolution that has no open objections among TC members.
If a consensus cannot be reached that has no objections then a majority wins vote
is called. It is also expected that the majority of decisions made by the TC are via
a consensus seeking process and that voting is only used as a last-resort.
Resolution may involve returning the issue to project captains with suggestions on
how to move forward towards a consensus. It is not expected that a meeting of the TC
will resolve all issues on its agenda during that meeting and may prefer to continue
the discussion happening among the project captains.
Members can be added to the TC at any time. Any TC member can nominate another committer
to the TC and the TC uses its standard consensus seeking process to evaluate whether or
not to add this new member. The TC will consist of a minimum of 3 active members and a
maximum of 10. If the TC should drop below 5 members the active TC members should nominate
someone new. If a TC member is stepping down, they are encouraged (but not required) to
nominate someone to take their place.
TC members will be added as admin's on the Github orgs, npm orgs, and other resources as
necessary to be effective in the role.
To remain "active" a TC member should have participation within the last 12 months and miss
no more than six consecutive TC meetings. Our goal is to increase participation, not punish
people for any lack of participation, this guideline should be only be used as such
(replace an inactive member with a new active one, for example). Members who do not meet this
are expected to step down. If A TC member does not step down, an issue can be opened in the
discussions repo to move them to inactive status. TC members who step down or are removed due
to inactivity will be moved into inactive status.
Inactive status members can become active members by self nomination if the TC is not already
larger than the maximum of 10. They will also be given preference if, while at max size, an
active member steps down.
## Project Captains
The Express TC can designate captains for individual projects/repos in the
organizations. These captains are responsible for being the primary
day-to-day maintainers of the repo on a technical and community front.
Repo captains are empowered with repo ownership and package publication rights.
When there are conflicts, especially on topics that effect the Express project
at large, captains are responsible to raise it up to the TC and drive
those conflicts to resolution. Captains are also responsible for making sure
community members follow the community guidelines, maintaining the repo
and the published package, as well as in providing user support.
Like TC members, Repo captains are a subset of committers.
To become a captain for a project the candidate is expected to participate in that
project for at least 6 months as a committer prior to the request. They should have
helped with code contributions as well as triaging issues. They are also required to
have 2FA enabled on both their GitHub and npm accounts. Any TC member or existing
captain on the repo can nominate another committer to the captain role, submit a PR to
this doc, under `Current Project Captains` section (maintaining the sort order) with
the project, their GitHub handle and npm username (if different). The PR will require
at least 2 approvals from TC members and 2 weeks hold time to allow for comment and/or
dissent. When the PR is merged, a TC member will add them to the proper GitHub/npm groups.
### Current Project Captains
- `expressjs/express`: @wesleytodd
- `expressjs/discussions`: @wesleytodd
- `expressjs/expressjs.com`: @crandmck
- `expressjs/body-parser`: @wesleytodd
- `expressjs/multer`: @LinusU
- `expressjs/cookie-parser`: @wesleytodd
- `expressjs/generator`: @wesleytodd
- `expressjs/statusboard`: @wesleytodd
- `pillarjs/path-to-regexp`: @blakeembrey
- `pillarjs/router`: @dougwilson, @wesleytodd
- `pillarjs/finalhandler`: @wesleytodd
- `pillarjs/request`: @wesleytodd
- `jshttp/http-errors`: @wesleytodd
- `jshttp/cookie`: @wesleytodd
- `jshttp/on-finished`: @wesleytodd
- `jshttp/forwarded`: @wesleytodd
- `jshttp/proxy-addr`: @wesleytodd

View File

@@ -1,3 +1,280 @@
# Unreleased Changes
## 🚀 Improvements
* Improve HTML structure in `res.redirect()` responses when HTML format is accepted by adding `<!DOCTYPE html>`, `<title>`, and `<body>` tags for better browser compatibility - by [@Bernice55231](https://github.com/Bernice55231) in [#5167](https://github.com/expressjs/express/pull/5167)
* When calling `app.render` with options set to null, the locals object is handled correctly, preventing unexpected errors and making the method behave the same as when options is omitted or an empty object is passed - by [AkaHarshit](https://github.com/AkaHarshit) in [#6903](https://github.com/expressjs/express/pull/6903)
```js
app.render('index', null, callback); // now works as expected
```
## ⚡ Performance
* Avoid duplicate Content-Type header processing in `res.send()` when sending string responses without an explicit Content-Type header - by [@bjohansebas](https://github.com/bjohansebas) in [#6991](https://github.com/expressjs/express/pull/6991)
5.2.1 / 2025-12-01
=======================
* Revert security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))
* The prior release (5.2.0) included an erroneous breaking change related to the extended query parser. There is no actual security vulnerability associated with this behavior (CVE-2024-51999 has been rejected). The change has been fully reverted in this release.
5.2.0 / 2025-12-01
========================
* Security fix for [CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999) ([GHSA-pj86-cfqh-vqx6](https://github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))
* deps: `body-parser@^2.2.1`
* A deprecation warning was added when using `res.redirect` with undefined arguments, Express now emits a warning to help detect calls that pass undefined as the status or URL and make them easier to fix.
5.1.0 / 2025-03-31
========================
* Add support for `Uint8Array` in `res.send()`
* Add support for ETag option in `res.sendFile()`
* Add support for multiple links with the same rel in `res.links()`
* Add funding field to package.json
* perf: use loop for acceptParams
* refactor: prefix built-in node module imports
* deps: remove `setprototypeof`
* deps: remove `safe-buffer`
* deps: remove `utils-merge`
* deps: remove `methods`
* deps: remove `depd`
* deps: `debug@^4.4.0`
* deps: `body-parser@^2.2.0`
* deps: `router@^2.2.0`
* deps: `content-type@^1.0.5`
* deps: `finalhandler@^2.1.0`
* deps: `qs@^6.14.0`
* deps: `server-static@2.2.0`
* deps: `type-is@2.0.1`
5.0.1 / 2024-10-08
==========
* Update `cookie` semver lock to address [CVE-2024-47764](https://nvd.nist.gov/vuln/detail/CVE-2024-47764)
5.0.0 / 2024-09-10
=========================
* remove:
- `path-is-absolute` dependency - use `path.isAbsolute` instead
* breaking:
* `res.status()` accepts only integers, and input must be greater than 99 and less than 1000
* will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range
* will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs
* deps: send@1.0.0
* `res.redirect('back')` and `res.location('back')` is no longer a supported magic string, explicitly use `req.get('Referrer') || '/'`.
* change:
- `res.clearCookie` will ignore user provided `maxAge` and `expires` options
* deps: cookie-signature@^1.2.1
* deps: debug@4.3.6
* deps: merge-descriptors@^2.0.0
* deps: serve-static@^2.1.0
* deps: qs@6.13.0
* deps: accepts@^2.0.0
* deps: mime-types@^3.0.0
- `application/javascript` => `text/javascript`
* deps: type-is@^2.0.0
* deps: content-disposition@^1.0.0
* deps: finalhandler@^2.0.0
* deps: fresh@^2.0.0
* deps: body-parser@^2.0.1
* deps: send@^1.1.0
5.0.0-beta.3 / 2024-03-25
=========================
This incorporates all changes after 4.19.1 up to 4.19.2.
5.0.0-beta.2 / 2024-03-20
=========================
This incorporates all changes after 4.17.2 up to 4.19.1.
5.0.0-beta.1 / 2022-02-14
=========================
This is the first Express 5.0 beta release, based off 4.17.2 and includes
changes from 5.0.0-alpha.8.
* change:
- Default "query parser" setting to `'simple'`
- Requires Node.js 4+
- Use `mime-types` for file to content type mapping
* deps: array-flatten@3.0.0
* deps: body-parser@2.0.0-beta.1
- `req.body` is no longer always initialized to `{}`
- `urlencoded` parser now defaults `extended` to `false`
- Use `on-finished` to determine when body read
* deps: router@2.0.0-beta.1
- Add new `?`, `*`, and `+` parameter modifiers
- Internalize private `router.process_params` method
- Matching group expressions are only RegExp syntax
- Named matching groups no longer available by position in `req.params`
- Regular expressions can only be used in a matching group
- Remove `debug` dependency
- Special `*` path segment behavior removed
- deps: array-flatten@3.0.0
- deps: parseurl@~1.3.3
- deps: path-to-regexp@3.2.0
- deps: setprototypeof@1.2.0
* deps: send@1.0.0-beta.1
- Change `dotfiles` option default to `'ignore'`
- Remove `hidden` option; use `dotfiles` option instead
- Use `mime-types` for file to content type mapping
- deps: debug@3.1.0
* deps: serve-static@2.0.0-beta.1
- Change `dotfiles` option default to `'ignore'`
- Remove `hidden` option; use `dotfiles` option instead
- Use `mime-types` for file to content type mapping
- Remove `express.static.mime` export; use `mime-types` package instead
- deps: send@1.0.0-beta.1
5.0.0-alpha.8 / 2020-03-25
==========================
This is the eighth Express 5.0 alpha release, based off 4.17.1 and includes
changes from 5.0.0-alpha.7.
5.0.0-alpha.7 / 2018-10-26
==========================
This is the seventh Express 5.0 alpha release, based off 4.16.4 and includes
changes from 5.0.0-alpha.6.
The major change with this alpha is the basic support for returned, rejected
Promises in the router.
* remove:
- `path-to-regexp` dependency
* deps: debug@3.1.0
- Add `DEBUG_HIDE_DATE` environment variable
- Change timer to per-namespace instead of global
- Change non-TTY date format
- Remove `DEBUG_FD` environment variable support
- Support 256 namespace colors
* deps: router@2.0.0-alpha.1
- Add basic support for returned, rejected Promises
- Fix JSDoc for `Router` constructor
- deps: debug@3.1.0
- deps: parseurl@~1.3.2
- deps: setprototypeof@1.1.0
- deps: utils-merge@1.0.1
5.0.0-alpha.6 / 2017-09-24
==========================
This is the sixth Express 5.0 alpha release, based off 4.15.5 and includes
changes from 5.0.0-alpha.5.
* remove:
- `res.redirect(url, status)` signature - use `res.redirect(status, url)`
- `res.send(status, body)` signature - use `res.status(status).send(body)`
* deps: router@~1.3.1
- deps: debug@2.6.8
5.0.0-alpha.5 / 2017-03-06
==========================
This is the fifth Express 5.0 alpha release, based off 4.15.2 and includes
changes from 5.0.0-alpha.4.
5.0.0-alpha.4 / 2017-03-01
==========================
This is the fourth Express 5.0 alpha release, based off 4.15.0 and includes
changes from 5.0.0-alpha.3.
* remove:
- Remove Express 3.x middleware error stubs
* deps: router@~1.3.0
- Add `next("router")` to exit from router
- Fix case where `router.use` skipped requests routes did not
- Skip routing when `req.url` is not set
- Use `%o` in path debug to tell types apart
- deps: debug@2.6.1
- deps: setprototypeof@1.0.3
- perf: add fast match path for `*` route
5.0.0-alpha.3 / 2017-01-28
==========================
This is the third Express 5.0 alpha release, based off 4.14.1 and includes
changes from 5.0.0-alpha.2.
* remove:
- `res.json(status, obj)` signature - use `res.status(status).json(obj)`
- `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)`
- `res.vary()` (no arguments) -- provide a field name as an argument
* deps: array-flatten@2.1.1
* deps: path-is-absolute@1.0.1
* deps: router@~1.1.5
- deps: array-flatten@2.0.1
- deps: methods@~1.1.2
- deps: parseurl@~1.3.1
- deps: setprototypeof@1.0.2
5.0.0-alpha.2 / 2015-07-06
==========================
This is the second Express 5.0 alpha release, based off 4.13.1 and includes
changes from 5.0.0-alpha.1.
* remove:
- `app.param(fn)`
- `req.param()` -- use `req.params`, `req.body`, or `req.query` instead
* change:
- `res.render` callback is always async, even for sync view engines
- The leading `:` character in `name` for `app.param(name, fn)` is no longer removed
- Use `router` module for routing
- Use `path-is-absolute` module for absolute path detection
5.0.0-alpha.1 / 2014-11-06
==========================
This is the first Express 5.0 alpha release, based off 4.10.1.
* remove:
- `app.del` - use `app.delete`
- `req.acceptsCharset` - use `req.acceptsCharsets`
- `req.acceptsEncoding` - use `req.acceptsEncodings`
- `req.acceptsLanguage` - use `req.acceptsLanguages`
- `res.json(obj, status)` signature - use `res.json(status, obj)`
- `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)`
- `res.send(body, status)` signature - use `res.send(status, body)`
- `res.send(status)` signature - use `res.sendStatus(status)`
- `res.sendfile` - use `res.sendFile` instead
- `express.query` middleware
* change:
- `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname
- `req.query` is now a getter instead of a plain property
* add:
- `app.router` is a reference to the base router
4.20.0 / 2024-09-10
==========
* deps: serve-static@0.16.0
* Remove link renderization in html while redirecting
* deps: send@0.19.0
* Remove link renderization in html while redirecting
* deps: body-parser@0.6.0
* add `depth` option to customize the depth level in the parser
* IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`)
* Remove link renderization in html while using `res.redirect`
* deps: path-to-regexp@0.1.10
- Adds support for named matching groups in the routes using a regex
- Adds backtracking protection to parameters without regexes defined
* deps: encodeurl@~2.0.0
- Removes encoding of `\`, `|`, and `^` to align better with URL spec
* Deprecate passing `options.maxAge` and `options.expires` to `res.clearCookie`
- Will be ignored in v5, clearCookie will set a cookie with an expires in the past to instruct clients to delete the cookie
4.19.2 / 2024-03-25
==========
* Improved fix for open redirect allow list bypass
4.19.1 / 2024-03-20
==========

View File

@@ -1,125 +0,0 @@
# README guidelines
Every module in the expressjs, pillarjs, and jshttp organizations should have
a README file named `README.md`. The purpose of the README is to:
- Explain the purpose of the module and how to use it.
- Act as a landing page (both on GitHub and npmjs.com) for the module to help
people find it via search. Middleware module READMEs are also incorporated
into https://expressjs.com/en/resources/middleware.html.
- Encourage community contributions and participation.
Use the [README template](https://github.com/expressjs/express/wiki/README-template)
to quickly create a new README file.
## Top-level items
**Badges** (optional): At the very top (with no subheading), include any
applicable badges, such as npm version/downloads, build status, test coverage,
and so on. Badges should resolve properly (not display a broken image).
Possible badges include:
- npm version: `[![NPM Version][npm-image]][npm-url]`
- npm downloads: `[![NPM Downloads][downloads-image]][downloads-url]`
- Build status: `[![Build Status][travis-image]][travis-url]`
- Test coverage: `[![Test Coverage][coveralls-image]][coveralls-url]`
- Tips: `[![Gratipay][gratipay-image]][gratipay-url]`
**Summary**: Following badges, provide a one- or two-sentence description of
what the module does. This should be the same as the npmjs.org blurb (which
comes from the description property of `package.json`). Since npm doesn't
handle markdown for the blurb, avoid using markdown in the summary sentence.
**TOC** (Optional): For longer READMEs, provide a table of contents that has
a relative link to each section. A tool such as
[doctoc](https://www.npmjs.com/package/doctoc) makes it very easy to generate
a TOC.
## Overview
Optionally, include a section of one or two paragraphs with more high-level
information on what the module does, what problems it solves, why one would
use it and how. Don't just repeat what's in the summary.
## Installation
Required. This section is typically just:
```sh
$ npm install module-name
```
But include any other steps or requirements.
NOTE: Use the `sh` code block to make the shell command display properly on
the website.
## Basic use
- Provide a general description of how to use the module with code sample.
Include any important caveats or restrictions.
- Explain the most common use cases.
- Optional: a simple "hello world" type example (where applicable). This
example is in addition to the more comprehensive [example section](#examples)
later.
## API
Provide complete API documentation.
Formatting conventions: Each function is listed in a 3rd-level heading (`###`),
like this:
```
### Function_name(arg, options [, optional_arg] ... )
```
**Options objects**
For arguments that are objects (for example, options object), describe the
properties in a table, as follows. This matches the formatting used in the
[Express API docs](https://expressjs.com/en/4x/api.html).
|Property | Description | Type | Default|
|----------|-----------|------------|-------------|
|Name of the property in `monospace`. | Brief description | String, Number, Boolean, etc. | If applicable.|
If all the properties are required (i.e. there are no defaults), then you
can omit the default column.
Instead of very lengthy descriptions, link out to subsequent paragraphs for
more detailed explanation of specific cases (e.g. "When this property is set
to 'foobar', xyz happens; see &lt;link to following section &gt;.)
If there are options properties that are themselves options, use additional
tables. See [`trust proxy` and `etag` properties](https://expressjs.com/en/4x/api.html#app.settings.table).
## Examples
Every README should have at least one example; ideally more. For code samples,
be sure to use the `js` code block, for proper display in the website, e.g.:
```js
var csurf = require('csurf')
...
```
## Tests
What tests are included.
How to run them.
The convention for running tests is `npm test`. All our projects should follow
this convention.
## Contributors
Names of module "owners" (lead developers) and other developers who have
contributed.
## License
Link to the license, with a short description of what it is, e.g. "MIT" or
whatever. Ideally, avoid putting the license text directly in the README; link
to it instead.

222
Readme.md
View File

@@ -1,20 +1,48 @@
[![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].**
## Table of contents
- [Table of contents](#table-of-contents)
- [Installation](#installation)
- [Features](#features)
- [Docs \& Community](#docs--community)
- [Quick Start](#quick-start)
- [Philosophy](#philosophy)
- [Examples](#examples)
- [Contributing](#contributing)
- [Security Issues](#security-issues)
- [Running Tests](#running-tests)
- [Current project team members](#current-project-team-members)
- [TC (Technical Committee)](#tc-technical-committee)
- [TC emeriti members](#tc-emeriti-members)
- [Triagers](#triagers)
- [Emeritus Triagers](#emeritus-triagers)
- [License](#license)
[![NPM Version][npm-version-image]][npm-url]
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
[![Test Coverage][coveralls-image]][coveralls-url]
[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
[![NPM Version][npm-version-image]][npm-url]
[![NPM Install Size][npm-install-size-image]][npm-install-size-url]
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
```js
const express = require('express')
import express from 'express'
const app = express()
app.get('/', function (req, res) {
app.get('/', (req, res) => {
res.send('Hello World')
})
app.listen(3000)
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000')
})
```
## Installation
@@ -23,7 +51,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).
@@ -31,11 +59,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
@@ -50,14 +78,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
@@ -65,26 +90,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
@@ -96,71 +121,158 @@ $ 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,
from code for bug fixes and enhancements, to additions and fixes to documentation, additional
tests, triaging incoming pull requests and issues, and more!
See the [Contributing Guide](Contributing.md) for more technical details on contributing.
See the [Contributing Guide] for more technical details on contributing.
### Security Issues
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](https://github.com/expressjs/express/security/policy).
### Running Tests
To run the test suite, first install the dependencies, then run `npm test`:
To run the test suite, first install the dependencies:
```console
$ npm install
$ npm test
```bash
npm install
```
## People
Then run `npm test`:
```bash
npm test
```
## Current project team members
For information about the governance of the express.js project, see [GOVERNANCE.md](https://github.com/expressjs/discussions/blob/HEAD/docs/GOVERNANCE.md).
The original author of Express is [TJ Holowaychuk](https://github.com/tj)
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson)
[List of all contributors](https://github.com/expressjs/express/graphs/contributors)
### TC (Technical Committee)
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
* [wesleytodd](https://github.com/wesleytodd) - **Wes Todd**
* [LinusU](https://github.com/LinusU) - **Linus Unnebäck**
* [blakeembrey](https://github.com/blakeembrey) - **Blake Embrey**
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
* [crandmck](https://github.com/crandmck) - **Rand McKinney**
* [ctcpip](https://github.com/ctcpip) - **Chris de Almeida**
<details>
<summary>TC emeriti members</summary>
#### TC emeriti members
* [dougwilson](https://github.com/dougwilson) - **Douglas Wilson**
* [hacksparrow](https://github.com/hacksparrow) - **Hage Yaapa**
* [jonathanong](https://github.com/jonathanong) - **jongleberry**
* [niftylettuce](https://github.com/niftylettuce) - **niftylettuce**
* [troygoode](https://github.com/troygoode) - **Troy Goode**
</details>
### Triagers
* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair**
* [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran**
* [carpasse](https://github.com/carpasse) - **Carlos Serrano**
* [CBID2](https://github.com/CBID2) - **Christine Belzie**
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him)
* [Phillip9587](https://github.com/Phillip9587) - **Phillip Barta**
* [efekrskl](https://github.com/efekrskl) - **Efe Karasakal**
* [rxmarbles](https://github.com/rxmarbles) - **Rick Markins** (he/him)
* [krzysdz](https://github.com/krzysdz)
* [GroophyLifefor](https://github.com/GroophyLifefor) - **Murat Kirazkaya**
<details>
<summary>Triagers emeriti members</summary>
#### Emeritus Triagers
* [AuggieH](https://github.com/AuggieH) - **Auggie Hudak**
* [G-Rath](https://github.com/G-Rath) - **Gareth Jones**
* [MohammadXroid](https://github.com/MohammadXroid) - **Mohammad Ayashi**
* [NawafSwe](https://github.com/NawafSwe) - **Nawaf Alsharqi**
* [NotMoni](https://github.com/NotMoni) - **Moni**
* [VigneshMurugan](https://github.com/VigneshMurugan) - **Vignesh Murugan**
* [davidmashe](https://github.com/davidmashe) - **David Ashe**
* [digitaIfabric](https://github.com/digitaIfabric) - **David**
* [e-l-i-s-e](https://github.com/e-l-i-s-e) - **Elise Bonner**
* [fed135](https://github.com/fed135) - **Frederic Charette**
* [firmanJS](https://github.com/firmanJS) - **Firman Abdul Hakim**
* [getspooky](https://github.com/getspooky) - **Yasser Ameur**
* [ghinks](https://github.com/ghinks) - **Glenn**
* [ghousemohamed](https://github.com/ghousemohamed) - **Ghouse Mohamed**
* [gireeshpunathil](https://github.com/gireeshpunathil) - **Gireesh Punathil**
* [jake32321](https://github.com/jake32321) - **Jake Reed**
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
* [lekanikotun](https://github.com/lekanikotun) - **Troy Goode**
* [marsonya](https://github.com/marsonya) - **Lekan Ikotun**
* [mastermatt](https://github.com/mastermatt) - **Matt R. Wilson**
* [maxakuru](https://github.com/maxakuru) - **Max Edell**
* [mlrawlings](https://github.com/mlrawlings) - **Michael Rawlings**
* [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov**
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
* [tarunyadav1](https://github.com/tarunyadav1) - **Tarun yadav**
* [tunniclm](https://github.com/tunniclm) - **Mike Tunnicliffe**
* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**
* [0ss](https://github.com/0ss) - **Salah**
* [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him)
* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)
* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**
* [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin**
* [dpopp07](https://github.com/dpopp07) - **Dustin Popp**
* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
* [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi**
</details>
## License
[MIT](LICENSE)
[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-image]: https://img.shields.io/coverallsCoverage/github/expressjs/express?branch=master
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/express/master?label=linux
[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/express/ci.yml?branch=master&label=ci
[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml
[npm-downloads-image]: https://badgen.net/npm/dm/express
[npm-downloads-image]: https://img.shields.io/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
[npm-version-image]: https://img.shields.io/npm/v/express
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/express
[Code of Conduct]: https://github.com/expressjs/.github/blob/HEAD/CODE_OF_CONDUCT.md
[Contributing Guide]: https://github.com/expressjs/.github/blob/HEAD/CONTRIBUTING.md

View File

@@ -1,192 +0,0 @@
# Express Release Process
This document contains the technical aspects of the Express release process. The
intended audience is those who have been authorized by the Express Technical
Committee (TC) to create, promote and sign official release builds for Express,
as npm packages hosted on https://npmjs.com/package/express.
## Who can make releases?
Release authorization is given by the Express TC. Once authorized, an individual
must have the following access permissions:
### 1. Github release access
The individual making the release will need to be a member of the
expressjs/express team with Write permission level so they are able to tag the
release commit and push changes to the expressjs/express repository
(see Steps 4 and 5).
### 2. npmjs.com release access
The individual making the release will need to be made an owner on the
`express` package on npmjs.com so they are able to publish the release
(see Step 6).
## How to publish a release
Before publishing, the following preconditions should be met:
- A release proposal issue or tracking pull request (see "Proposal branch"
below) will exist documenting:
- the proposed changes
- the type of release: patch, minor or major
- the version number (according to semantic versioning - http://semver.org)
- The proposed changes should be complete.
There are two main release flows: patch and non-patch.
The patch flow is for making **patch releases**. As per semantic versioning,
patch releases are for simple changes, eg: typo fixes, patch dependency updates,
and simple/low-risk bug fixes. Every other type of change is made via the
non-patch flow.
### Branch terminology
"Master branch"
- There is a branch in git used for the current major version of Express, named
`master`.
- This branch contains the completed commits for the next patch release of the
current major version.
- Releases for the current major version are published from this branch.
"Version branch"
- For any given major version of Express (current, previous or next) there is
a branch in git for that release named `<major-version>.x` (eg: `4.x`).
- This branch points to the commit of the latest tag for the given major version.
"Release branch"
- For any given major version of Express, there is a branch used for publishing
releases.
- For the current major version of Express, the release branch is the
"Master branch" named `master`.
- For all other major versions of Express, the release branch is the
"Version branch" named `<major-version>.x`.
"Proposal branch"
- A branch in git representing a proposed new release of Express. This can be a
minor or major release, named `<major-version>.0` for a major release,
`<major-version>.<minor-version>` for a minor release.
- A tracking pull request should exist to document the proposed release,
targeted at the appropriate release branch. Prior to opening the tracking
pull request the content of the release may have be discussed in an issue.
- This branch contains the commits accepted so far that implement the proposal
in the tracking pull request.
### Patch flow
In the patch flow, simple changes are committed to the release branch which
acts as an ever-present branch for the next patch release of the associated
major version of Express.
The release branch is usually kept in a state where it is ready to release.
Releases are made when sufficient time or change has been made to warrant it.
This is usually proposed and decided using a github issue.
### Non-patch flow
In the non-patch flow, changes are committed to a temporary proposal branch
created specifically for that release proposal. The branch is based on the
most recent release of the major version of Express that the release targets.
Releases are made when all the changes on a proposal branch are complete and
approved. This is done by merging the proposal branch into the release branch
(using a fast-forward merge), tagging it with the new version number and
publishing the release package to npmjs.com.
### Flow
Below is a detailed description of the steps to publish a release.
#### Step 1. Check the release is ready to publish
Check any relevant information to ensure the release is ready, eg: any
milestone, label, issue or tracking pull request for the release. The release
is ready when all proposed code, tests and documentation updates are complete
(either merged, closed or re-targeted to another release).
#### Step 2. (Non-patch flow only) Merge the proposal branch into the release branch
In the patch flow: skip this step.
In the non-patch flow:
```sh
$ git checkout <release-branch>
$ git merge --ff-only <proposal-branch>
```
<release-branch> - see "Release branch" of "Branches" above.
<proposal-branch> - see "Proposal branch" of "Non-patch flow" above.
**NOTE:** You may need to rebase the proposal branch to allow a fast-forward
merge. Using a fast-forward merge keeps the history clean as it does
not introduce merge commits.
### Step 3. Update the History.md and package.json to the new version number
The changes so far for the release should already be documented under the
"unreleased" section at the top of the History.md file, as per the usual
development practice. Change "unreleased" to the new release version / date.
Example diff fragment:
```diff
-unreleased
-==========
+4.13.3 / 2015-08-02
+===================
```
The version property in the package.json should already contain the version of
the previous release. Change it to the new release version.
Commit these changes together under a single commit with the message set to
the new release version (eg: `4.13.3`):
```sh
$ git checkout <release-branch>
<..edit files..>
$ git add History.md package.json
$ git commit -m '<version-number>'
```
### Step 4. Identify and tag the release commit with the new release version
Create a lightweight tag (rather than an annotated tag) named after the new
release version (eg: `4.13.3`).
```sh
$ git tag <version-number>
```
### Step 5. Push the release branch changes and tag to github
The branch and tag should be pushed directly to the main repository
(https://github.com/expressjs/express).
```sh
$ git push origin <release-branch>
$ git push origin <version-number>
```
### Step 6. Publish to npmjs.com
Ensure your local working copy is completely clean (no extra or changed files).
You can use `git status` for this purpose.
```sh
$ npm login <npm-username>
$ npm publish
```
**NOTE:** The version number to publish will be picked up automatically from
package.json.
### Step 7. Update documentation website
The documentation website https://expressjs.com/ documents the current release version in various places. For a new release:
1. Change the value of `current_version` in https://github.com/expressjs/expressjs.com/blob/gh-pages/_data/express.yml to match the latest version number.
2. Add a new section to the change log. For example, for a 4.x release, https://github.com/expressjs/expressjs.com/blob/gh-pages/en/changelog/4x.md,

View File

@@ -1,46 +0,0 @@
# Security Policies and Procedures
This document outlines security procedures and general policies for the Express
project.
* [Reporting a Bug](#reporting-a-bug)
* [Disclosure Policy](#disclosure-policy)
* [Comments on this Policy](#comments-on-this-policy)
## Reporting a Bug
The Express team and community take all security bugs in Express seriously.
Thank you for improving the security of Express. We appreciate your efforts and
responsible disclosure and will make every effort to acknowledge your
contributions.
Report security bugs by emailing the lead maintainer in the Readme.md file.
To ensure the timely response to your report, please ensure that the entirety
of the report is contained within the email body and not solely behind a web
link or an attachment.
The lead maintainer will acknowledge your email within 48 hours, and will send a
more detailed response within 48 hours indicating the next steps in handling
your report. After the initial reply to your report, the security team will
endeavor to keep you informed of the progress towards a fix and full
announcement, and may ask for additional information or guidance.
Report security bugs in third-party modules to the person or team maintaining
the module.
## Disclosure Policy
When the security team receives a security bug report, they will assign it to a
primary handler. This person will coordinate the fix and release process,
involving the following steps:
* Confirm the problem and determine the affected versions.
* Audit code to find any potential similar problems.
* Prepare fixes for all releases still under maintenance. These fixes will be
released as fast as possible to npm.
## Comments on this Policy
If you have suggestions on how this process could be improved please submit a
pull request.

View File

@@ -1,63 +0,0 @@
# Express Triager Guide
## Issue Triage Process
When a new issue or pull request is opened the issue will be labeled with `needs triage`.
If a triage team member is available they can help make sure all the required information
is provided. Depending on the issue or PR there are several next labels they can add for further
classification:
* `needs triage`: This can be kept if the triager is unsure which next steps to take
* `awaiting more info`: If more info has been requested from the author, apply this label.
* `question`: User questions that do not appear to be bugs or enhancements.
* `discuss`: Topics for discussion. Might end in an `enhancement` or `question` label.
* `bug`: Issues that present a reasonable conviction there is a reproducible bug.
* `enhancement`: Issues that are found to be a reasonable candidate feature additions.
In all cases, issues may be closed by maintainers if they don't receive a timely response when
further information is sought, or when additional questions are asked.
## Approaches and Best Practices for getting into triage contributions
Review the organization's [StatusBoard](https://expressjs.github.io/statusboard/),
pay special attention to these columns: stars, watchers, open issues, and contributors.
This gives you a general idea about the criticality and health of the repository.
Pick a few projects based on that criteria, your interests, and skills (existing or aspiring).
Review the project's contribution guideline if present. In a nutshell,
commit to the community's standards and values. Review the
documentation, for most of the projects it is just the README.md, and
make sure you understand the key APIs, semantics, configurations, and use cases.
It might be helpful to write your own test apps to re-affirm your
understanding of the key functions. This may identify some gaps in
documentation, record those as they might be good PR's to open.
Skim through the issue backlog; identify low hanging issues and mostly new ones.
From those, attempt to recreate issues based on the OP description and
ask questions if required. No question is a bad question!
## Removal of Triage Role
There are a few cases where members can be removed as triagers:
- Breaking the CoC or project contributor guidelines
- Abuse or misuse of the role as deemed by the TC
- Lack of participation for more than 6 months
If any of these happen we will discuss as a part of the triage portion of the regular TC meetings.
If you have questions feel free to reach out to any of the TC members.
## Other Helpful Hints:
- Everyone is welcome to attend the [Express Technical Committee Meetings](https://github.com/expressjs/discussions#expressjs-tc-meetings), and as a triager, it might help to get a better idea of what's happening with the project.
- When exploring the module's functionality there are a few helpful steps:
- Turn on `DEBUG=*` (see https://www.npmjs.com/package/debug) to get detailed log information
- It is also a good idea to do live debugging to follow the control flow, try using `node --inspect`
- It is a good idea to make at least one pass of reading through the entire source
- When reviewing the list of open issues there are some common types and suggested actions:
- New/unattended issues or simple questions: A good place to start
- Hard bugs & ongoing discussions: always feel free to chime in and help
- Issues that imply gaps in the documentation: open PRs with changes or help the user to do so
- For recurring issues, it is helpful to create functional examples to demonstrate (publish as gists or a repo)
- Review and identify the maintainers. If necessary, at-mention one or more of them if you are unsure what to do
- Make sure all your interactions are professional, welcoming, and respectful to the parties involved.

View File

@@ -1,112 +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"
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

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

View File

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

View File

@@ -1,20 +0,0 @@
var express = require('..');
var app = express();
// number of middleware
var n = parseInt(process.env.MW || '1', 10);
console.log(' %s middleware', n);
while (n--) {
app.use(function(req, res, next){
next();
});
}
app.use(function(req, res){
res.send('Hello World')
});
app.listen(3333);

View File

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

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,35 +12,51 @@
*/
var express = require('../..');
var path = require('path');
var path = require('node:path');
var redis = require('redis');
var db = redis.createClient();
// npm install redis
var app = express();
app.use(express.static(path.join(__dirname, 'public')));
// populate search
// npm install redis
db.sadd('ferret', 'tobi');
db.sadd('ferret', 'loki');
db.sadd('ferret', 'jane');
db.sadd('cat', 'manny');
db.sadd('cat', 'luna');
/**
* Redis Initialization
*/
async function initializeRedis() {
try {
// connect to Redis
await db.connect();
// populate search
await db.sAdd('ferret', 'tobi');
await db.sAdd('ferret', 'loki');
await db.sAdd('ferret', 'jane');
await db.sAdd('cat', 'manny');
await db.sAdd('cat', 'luna');
} catch (err) {
console.error('Error initializing Redis:', err);
process.exit(1);
}
}
/**
* GET search for :query.
*/
app.get('/search/:query?', function(req, res){
var query = req.params.query;
db.smembers(query, function(err, vals){
if (err) return res.send(500);
res.send(vals);
});
app.get('/search/{:query}', function (req, res, next) {
var query = req.params.query || '';
db.sMembers(query)
.then((vals) => res.send(vals))
.catch((err) => {
console.error(`Redis error for query "${query}":`, err);
next(err);
});
});
/**
@@ -54,8 +70,14 @@ app.get('/client.js', function(req, res){
res.sendFile(path.join(__dirname, 'client.js'));
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
/**
* Start the Server
*/
(async () => {
await initializeRedis();
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
})();

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
@@ -549,8 +523,7 @@ app.render = function render(name, options, callback) {
var cache = this.cache;
var done = callback;
var engines = this.engines;
var opts = options;
var renderOptions = {};
var opts = options || {};
var view;
// support callback function as second arg
@@ -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 = 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');
@@ -84,16 +83,13 @@ req.header = function header(name) {
};
/**
* To do: update docs.
*
* Check if the given `type(s)` is acceptable, returning
* the best match when true, otherwise `undefined`, in which
* the best match when true, otherwise `false`, in which
* case you should respond with 406 "Not Acceptable".
*
* The `type` value may be a single MIME type string
* such as "application/json", an extension name
* such as "json", a comma-delimited list such as "json, html, text/plain",
* an argument list such as `"json", "html", "text/plain"`,
* such as "json", an argument list such as `"json", "html", "text/plain"`,
* or an array `["json", "html", "text/plain"]`. When a list
* or array is given, the _best_ match, if any is returned.
*
@@ -108,7 +104,7 @@ req.header = function header(name) {
* // => "html"
* req.accepts('text/html');
* // => "text/html"
* req.accepts('json, text');
* req.accepts('json', 'text');
* // => "json"
* req.accepts('application/json');
* // => "application/json"
@@ -116,12 +112,11 @@ req.header = function header(name) {
* // Accept: text/*, application/json
* req.accepts('image/png');
* req.accepts('png');
* // => undefined
* // => false
*
* // Accept: text/*;q=.5, application/json
* req.accepts(['html', 'json']);
* req.accepts('html', 'json');
* req.accepts('html, json');
* // => "json"
*
* @param {String|Array} type(s)
@@ -147,26 +142,37 @@ req.acceptsEncodings = function(){
return accept.encodings.apply(accept, arguments);
};
req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
'req.acceptsEncoding: Use acceptsEncodings instead');
/**
* Check if the given `charset`s are acceptable,
* otherwise you should respond with 406 "Not Acceptable".
* Checks if the specified `charset`s are acceptable based on the request's `Accept-Charset` header.
* Returns the best matching charset or an array of acceptable charsets.
*
* @param {String} ...charset
* @return {String|Array}
* The `charset` argument(s) can be:
* - A single charset string (e.g., "utf-8")
* - Multiple charset strings as arguments (e.g., `"utf-8", "iso-8859-1"`)
* - A comma-delimited list of charsets (e.g., `"utf-8, iso-8859-1"`)
*
* Examples:
*
* // Accept-Charset: utf-8, iso-8859-1
* req.acceptsCharsets('utf-8');
* // => "utf-8"
*
* req.acceptsCharsets('utf-8', 'iso-8859-1');
* // => "utf-8"
*
* req.acceptsCharsets('utf-8, utf-16');
* // => "utf-8"
*
* @param {...String} charsets - The charset(s) to check against the `Accept-Charset` header.
* @return {String|Array} - The best matching charset, or an array of acceptable charsets.
* @public
*/
req.acceptsCharsets = function(){
var accept = accepts(this);
return accept.charsets.apply(accept, arguments);
req.acceptsCharsets = function(...charsets) {
const accept = accepts(this);
return accept.charsets(...charsets);
};
req.acceptsCharset = deprecate.function(req.acceptsCharsets,
'req.acceptsCharset: Use acceptsCharsets instead');
/**
* Check if the given `lang`s are acceptable,
* otherwise you should respond with 406 "Not Acceptable".
@@ -176,14 +182,10 @@ req.acceptsCharset = deprecate.function(req.acceptsCharsets,
* @public
*/
req.acceptsLanguages = function(){
var accept = accepts(this);
return accept.languages.apply(accept, arguments);
req.acceptsLanguages = function(...languages) {
return accepts(this).languages(...languages);
};
req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
'req.acceptsLanguage: Use acceptsLanguages instead');
/**
* Parse Range header field, capping to the given `size`.
*
@@ -216,38 +218,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"
@@ -304,12 +295,12 @@ req.is = function is(types) {
*/
defineGetter(req, 'protocol', function protocol(){
var proto = this.connection.encrypted
var proto = this.socket.encrypted
? 'https'
: 'http';
var trust = this.app.get('trust proxy fn');
if (!trust(this.connection.remoteAddress, 0)) {
if (!trust(this.socket.remoteAddress, 0)) {
return proto;
}
@@ -414,7 +405,7 @@ defineGetter(req, 'path', function path() {
});
/**
* Parse the "Host" header field to a hostname.
* Parse the "Host" header field to a host.
*
* When the "trust proxy" setting trusts the socket
* address, the "X-Forwarded-Host" header field will
@@ -424,18 +415,35 @@ defineGetter(req, 'path', function path() {
* @public
*/
defineGetter(req, 'hostname', function hostname(){
defineGetter(req, 'host', function host(){
var trust = this.app.get('trust proxy fn');
var host = this.get('X-Forwarded-Host');
var val = this.get('X-Forwarded-Host');
if (!host || !trust(this.connection.remoteAddress, 0)) {
host = this.get('Host');
} else if (host.indexOf(',') !== -1) {
if (!val || !trust(this.socket.remoteAddress, 0)) {
val = this.get('Host');
} else if (val.indexOf(',') !== -1) {
// Note: X-Forwarded-Host is normally only ever a
// single value, but this is to be safe.
host = host.substring(0, host.indexOf(',')).trimRight()
val = val.substring(0, val.indexOf(',')).trimRight()
}
return val || undefined;
});
/**
* Parse the "Host" header field to a hostname.
*
* When the "trust proxy" setting trusts the socket
* address, the "X-Forwarded-Host" header field will
* be trusted.
*
* @return {String}
* @api public
*/
defineGetter(req, 'hostname', function hostname(){
var host = this.host;
if (!host) return;
// IPv6 literal support
@@ -449,15 +457,9 @@ defineGetter(req, 'hostname', function hostname(){
: host;
});
// TODO: change req.host to return host in next major
defineGetter(req, 'host', deprecate.function(function host(){
return this.hostname;
}, 'req.host: Use req.hostname instead'));
/**
* Check if the request is fresh, aka
* Last-Modified and/or the ETag
* Last-Modified or the ETag
* still match.
*
* @return {Boolean}

View File

@@ -12,18 +12,17 @@
* @private
*/
var Buffer = require('safe-buffer').Buffer
var contentDisposition = require('content-disposition');
var createError = require('http-errors')
var deprecate = require('depd')('express');
var encodeUrl = require('encodeurl');
var escapeHtml = require('escape-html');
var http = require('http');
var isAbsolute = require('./utils').isAbsolute;
var http = require('node:http');
var onFinished = require('on-finished');
var path = require('path');
var mime = require('mime-types')
var path = require('node:path');
var pathIsAbsolute = require('node:path').isAbsolute;
var statuses = require('statuses')
var merge = require('utils-merge');
var sign = require('cookie-signature').sign;
var normalizeType = require('./utils').normalizeType;
var normalizeTypes = require('./utils').normalizeTypes;
@@ -31,10 +30,9 @@ var setCharset = require('./utils').setCharset;
var cookie = require('cookie');
var send = require('send');
var extname = path.extname;
var mime = send.mime;
var resolve = path.resolve;
var vary = require('vary');
var urlParse = require('url').parse;
const { Buffer } = require('node:buffer');
/**
* Response prototype.
@@ -51,24 +49,28 @@ var res = Object.create(http.ServerResponse.prototype)
module.exports = res
/**
* Module variables.
* @private
*/
var charsetRegExp = /;\s*charset\s*=/;
/**
* Set status `code`.
* Set the HTTP status code for the response.
*
* @param {Number} code
* @return {ServerResponse}
* Expects an integer value between 100 and 999 inclusive.
* Throws an error if the provided status code is not an integer or if it's outside the allowable range.
*
* @param {number} code - The HTTP status code to set.
* @return {ServerResponse} - Returns itself for chaining methods.
* @throws {TypeError} If `code` is not an integer.
* @throws {RangeError} If `code` is outside the range 100 to 999.
* @public
*/
res.status = function status(code) {
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;
};
@@ -80,7 +82,11 @@ res.status = function status(code) {
*
* res.links({
* next: 'http://api.example.com/users?page=2',
* last: 'http://api.example.com/users?page=5'
* last: 'http://api.example.com/users?page=5',
* pages: [
* 'http://api.example.com/users?page=1',
* 'http://api.example.com/users?page=2'
* ]
* });
*
* @param {Object} links
@@ -88,11 +94,18 @@ res.status = function status(code) {
* @public
*/
res.links = function(links){
res.links = function(links) {
var link = this.get('Link') || '';
if (link) link += ', ';
return this.set('Link', link + Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
return this.set('Link', link + Object.keys(links).map(function(rel) {
// Allow multiple links if links[rel] is an array
if (Array.isArray(links[rel])) {
return links[rel].map(function (singleLink) {
return `<${singleLink}>; rel="${rel}"`;
}).join(', ');
} else {
return `<${links[rel]}>; rel="${rel}"`;
}
}).join(', '));
};
@@ -113,40 +126,19 @@ res.send = function send(body) {
var chunk = body;
var encoding;
var req = this.req;
var type;
// settings
var app = this.app;
// allow status / body
if (arguments.length === 2) {
// res.send(body, status) backwards compat
if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
deprecate('res.send(body, status): Use res.status(status).send(body) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.send(status, body): Use res.status(status).send(body) instead');
this.statusCode = arguments[0];
chunk = arguments[1];
}
}
// disambiguate res.send(status) and res.send(status, num)
if (typeof chunk === 'number' && arguments.length === 1) {
// res.send(status) will set status message as text string
if (!this.get('Content-Type')) {
this.type('txt');
}
deprecate('res.send(status): Use res.sendStatus(status) instead');
this.statusCode = chunk;
chunk = statuses.message[chunk]
}
switch (typeof chunk) {
// string defaulting to html
case 'string':
if (!this.get('Content-Type')) {
encoding = 'utf8';
const type = this.get('Content-Type');
if (typeof type === 'string') {
this.set('Content-Type', setCharset(type, 'utf-8'));
} else {
this.type('html');
}
break;
@@ -155,7 +147,7 @@ res.send = function send(body) {
case 'object':
if (chunk === null) {
chunk = '';
} else if (Buffer.isBuffer(chunk)) {
} else if (ArrayBuffer.isView(chunk)) {
if (!this.get('Content-Type')) {
this.type('bin');
}
@@ -165,17 +157,6 @@ res.send = function send(body) {
break;
}
// write strings in utf-8
if (typeof chunk === 'string') {
encoding = 'utf8';
type = this.get('Content-Type');
// reflect this in content-type
if (typeof type === 'string') {
this.set('Content-Type', setCharset(type, 'utf-8'));
}
}
// determine if ETag should be generated
var etagFn = app.get('etag fn')
var generateETag = !this.get('ETag') && typeof etagFn === 'function'
@@ -208,7 +189,7 @@ res.send = function send(body) {
}
// freshness
if (req.fresh) this.statusCode = 304;
if (req.fresh) this.status(304);
// strip irrelevant headers
if (204 === this.statusCode || 304 === this.statusCode) {
@@ -249,27 +230,12 @@ res.send = function send(body) {
*/
res.json = function json(obj) {
var val = obj;
// allow status / body
if (arguments.length === 2) {
// res.json(body, status) backwards compat
if (typeof arguments[1] === 'number') {
deprecate('res.json(obj, status): Use res.status(status).json(obj) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.json(status, obj): Use res.status(status).json(obj) instead');
this.statusCode = arguments[0];
val = arguments[1];
}
}
// settings
var app = this.app;
var escape = app.get('json escape')
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = stringify(val, replacer, spaces, escape)
var body = stringify(obj, replacer, spaces, escape)
// content-type
if (!this.get('Content-Type')) {
@@ -292,27 +258,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
@@ -370,7 +321,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);
@@ -438,12 +389,15 @@ res.sendFile = function sendFile(path, options, callback) {
opts = {};
}
if (!opts.root && !isAbsolute(path)) {
if (!opts.root && !pathIsAbsolute(path)) {
throw new TypeError('path must be absolute or specify root to res.sendFile');
}
// create file stream
var pathname = encodeURI(path);
// wire application etag option to send
opts.etag = this.app.enabled('etag');
var file = send(req, pathname, opts);
// transfer
@@ -458,78 +412,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.
*
@@ -600,8 +482,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:
*
@@ -619,7 +503,7 @@ res.download = function download (path, filename, options, callback) {
res.contentType =
res.type = function contentType(type) {
var ct = type.indexOf('/') === -1
? mime.lookup(type)
? (mime.contentType(type) || 'application/octet-stream')
: type;
return this.set('Content-Type', ct);
@@ -768,6 +652,9 @@ res.append = function append(field, val) {
*
* Aliased as `res.header()`.
*
* When the set header is "Content-Type", the type is expanded to include
* the charset if not present using `mime.contentType()`.
*
* @param {String|Object} field
* @param {String|Array} val
* @return {ServerResponse} for chaining
@@ -786,10 +673,7 @@ res.header = function header(field, val) {
if (Array.isArray(value)) {
throw new TypeError('Content-Type cannot be set to an Array');
}
if (!charsetRegExp.test(value)) {
var charset = mime.charsets.lookup(value.split(';')[0]);
if (charset) value += '; charset=' + charset.toLowerCase();
}
value = mime.contentType(value)
}
this.setHeader(field, value);
@@ -823,7 +707,10 @@ res.get = function(field){
*/
res.clearCookie = function clearCookie(name, options) {
var opts = merge({ expires: new Date(1), path: '/' }, options);
// Force cookie expiration by setting expires to the past
const opts = { path: '/', ...options, expires: new Date(1)};
// ensure maxAge is not passed
delete opts.maxAge
return this.cookie(name, '', opts);
};
@@ -853,7 +740,7 @@ res.clearCookie = function clearCookie(name, options) {
*/
res.cookie = function (name, value, options) {
var opts = merge({}, options);
var opts = { ...options };
var secret = this.req.secret;
var signed = opts.signed;
@@ -905,42 +792,13 @@ res.cookie = function (name, value, options) {
*/
res.location = function location(url) {
var loc = String(url);
// "back" is an alias for the referrer
if (url === 'back') {
loc = this.req.get('Referrer') || '/';
}
var lowerLoc = loc.toLowerCase();
var encodedUrl = encodeUrl(loc);
if (lowerLoc.indexOf('https://') === 0 || lowerLoc.indexOf('http://') === 0) {
try {
var parsedUrl = urlParse(loc);
var parsedEncodedUrl = urlParse(encodedUrl);
// Because this can encode the host, check that we did not change the host
if (parsedUrl.host !== parsedEncodedUrl.host) {
// If the host changes after encodeUrl, return the original url
return this.set('Location', loc);
}
} catch (e) {
// If parse fails, return the original url
return this.set('Location', loc);
}
}
// set location
return this.set('Location', encodedUrl);
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');
@@ -958,13 +816,20 @@ res.redirect = function redirect(url) {
// allow status / url
if (arguments.length === 2) {
if (typeof arguments[0] === 'number') {
status = arguments[0];
address = arguments[1];
} else {
deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
status = arguments[1];
}
status = arguments[0]
address = arguments[1]
}
if (!address) {
deprecate('Provide a url argument');
}
if (typeof address !== 'string') {
deprecate('Url must be a string');
}
if (typeof status !== 'number') {
deprecate('Status must be a number');
}
// Set location header
@@ -978,7 +843,8 @@ 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 = '<!DOCTYPE html><head><title>' + statuses.message[status] + '</title></head>'
+ '<body><p>' + statuses.message[status] + '. Redirecting to ' + u + '</p></body>'
},
default: function(){
@@ -987,7 +853,7 @@ res.redirect = function redirect(url) {
});
// Respond
this.statusCode = status;
this.status(status);
this.set('Content-Length', Buffer.byteLength(body));
if (this.req.method === 'HEAD') {
@@ -1007,12 +873,6 @@ res.redirect = function redirect(url) {
*/
res.vary = function(field){
// checks for back-compat
if (!field || (Array.isArray(field) && !field.length)) {
deprecate('res.vary(): Provide a field name');
return this;
}
vary(this, field);
return this;

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,16 +12,21 @@
* @api private
*/
var Buffer = require('safe-buffer').Buffer
var contentDisposition = require('content-disposition');
var { METHODS } = require('node:http');
var contentType = require('content-type');
var deprecate = require('depd')('express');
var flatten = require('array-flatten');
var mime = require('send').mime;
var etag = require('etag');
var mime = require('mime-types')
var proxyaddr = require('proxy-addr');
var qs = require('qs');
var querystring = require('querystring');
var querystring = require('node:querystring');
const { Buffer } = require('node:buffer');
/**
* A list of lowercased HTTP methods that are supported by Node.js.
* @api private
*/
exports.methods = METHODS.map((method) => method.toLowerCase());
/**
* Return strong ETag for `body`.
@@ -45,31 +50,6 @@ exports.etag = createETagGenerator({ weak: false })
exports.wetag = createETagGenerator({ weak: true })
/**
* Check if `path` looks absolute.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
exports.isAbsolute = function(path){
if ('/' === path[0]) return true;
if (':' === path[1] && ('\\' === path[2] || '/' === path[2])) return true; // Windows device path
if ('\\\\' === path.substring(0, 2)) return true; // Microsoft Azure absolute path
};
/**
* Flatten the given `arr`.
*
* @param {Array} arr
* @return {Array}
* @api private
*/
exports.flatten = deprecate.function(flatten,
'utils.flatten: use array-flatten npm module instead');
/**
* Normalize the given `type`, for example "html" becomes "text/html".
*
@@ -81,7 +61,7 @@ exports.flatten = deprecate.function(flatten,
exports.normalizeType = function(type){
return ~type.indexOf('/')
? acceptParams(type)
: { value: mime.lookup(type), params: {} };
: { value: (mime.lookup(type) || 'application/octet-stream'), params: {} }
};
/**
@@ -92,27 +72,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 +87,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 +172,6 @@ exports.compileQueryParser = function compileQueryParser(val) {
fn = querystring.parse;
break;
case false:
fn = newObject;
break;
case 'extended':
fn = parseExtendedQueryString;
@@ -290,14 +269,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.1",
"version": "5.2.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,71 +32,68 @@
"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": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"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.2.1",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"depd": "^2.0.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.1.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.14.2",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",
"serve-static": "^2.2.0",
"statuses": "^2.0.1",
"type-is": "^2.0.1",
"vary": "^1.1.2"
},
"devDependencies": {
"after": "0.8.2",
"connect-redis": "3.4.2",
"cookie-parser": "1.4.6",
"cookie-session": "2.0.0",
"ejs": "3.1.9",
"connect-redis": "^8.0.1",
"cookie-parser": "1.4.7",
"cookie-session": "2.1.1",
"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",
"morgan": "1.10.0",
"nyc": "15.1.0",
"mocha": "^10.7.3",
"morgan": "1.10.1",
"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",
"History.md",
"Readme.md",
"index.js",
"lib/"
],
"scripts": {
"lint": "eslint .",
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
"test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
"test-cov": "nyc --reporter=html --reporter=text npm test",
"lint:fix": "eslint . --fix",
"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,45 @@ 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()
})
})
})
it('accepts port + hostname + backlog + callback', function (done) {
const app = express();
const server = app.listen(0, '127.0.0.1', 5, function () {
const { address, port } = server.address();
assert.strictEqual(address, '127.0.0.1');
assert(Number.isInteger(port) && port > 0);
// backlog isnt directly inspectable, but if no error was thrown
// we know it was accepted.
server.close(done);
});
});
it('accepts just a callback (no args)', function (done) {
const app = express();
// same as app.listen(0, done)
const server = app.listen();
server.close(done);
});
it('server.address() gives a { address, port, family } object', function (done) {
const app = express();
const server = app.listen(0, () => {
const addr = server.address();
assert(addr && typeof addr === 'object');
assert.strictEqual(typeof addr.address, 'string');
assert(Number.isInteger(addr.port) && addr.port > 0);
assert(typeof addr.family === 'string');
server.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(){
@@ -331,6 +331,24 @@ describe('app', function(){
})
})
it('should accept null or undefined options', function (done) {
var app = createApp()
app.set('views', path.join(__dirname, 'fixtures'))
app.locals.user = { name: 'tobi' }
app.render('user.tmpl', null, function (err, str) {
if (err) return done(err);
assert.strictEqual(str, '<p>tobi</p>')
app.render('user.tmpl', undefined, function (err2, str2) {
if (err2) return done(err2);
assert.strictEqual(str2, '<p>tobi</p>')
done()
})
})
})
describe('caching', function(){
it('should cache with cache option', function(done){
var app = express();

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,12 @@
'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
const { Buffer } = require('node:buffer');
var express = require('..')
var request = require('supertest')
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('express.json()', function () {
it('should parse JSON', function (done) {
request(createApp())
@@ -43,12 +40,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 +70,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 +313,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/json')
.send('{"user":"tobi"}')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@@ -373,7 +345,7 @@ describe('express.json()', function () {
.post('/')
.set('Content-Type', 'application/x-json')
.send('{"user":"tobi"}')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@@ -528,13 +500,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)
})
@@ -568,7 +540,7 @@ describe('express.json()', function () {
this.app = app
})
it('should presist store', function (done) {
it('should persist store', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/json')
@@ -579,18 +551,18 @@ 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)
})
it('should presist store when inflated', function (done) {
it('should persist store when inflated', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/json')
@@ -601,7 +573,7 @@ describe('express.json()', function () {
test.end(done)
})
it('should presist store when inflate error', function (done) {
it('should persist store when inflate error', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/json')
@@ -611,7 +583,7 @@ describe('express.json()', function () {
test.end(done)
})
it('should presist store when parse error', function (done) {
it('should persist store when parse error', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/json')
@@ -621,7 +593,7 @@ describe('express.json()', function () {
.end(done)
})
it('should presist store when limit exceeded', function (done) {
it('should persist store when limit exceeded', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/json')
@@ -753,6 +725,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 +753,3 @@ function shouldContainInBody (str) {
'expected \'' + res.text + '\' to contain \'' + str + '\'')
}
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@@ -1,14 +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
const { Buffer } = require('node:buffer');
describe('express.raw()', function () {
before(function () {
@@ -65,36 +62,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 +203,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 +232,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 +325,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)
})
@@ -402,7 +369,7 @@ describe('express.raw()', function () {
this.app = app
})
it('should presist store', function (done) {
it('should persist store', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/octet-stream')
@@ -413,18 +380,17 @@ describe('express.raw()', 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('{}')
.end(done)
})
it('should presist store when inflated', function (done) {
it('should persist store when inflated', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/octet-stream')
@@ -435,7 +401,7 @@ describe('express.raw()', function () {
test.end(done)
})
it('should presist store when inflate error', function (done) {
it('should persist store when inflate error', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/octet-stream')
@@ -445,7 +411,7 @@ describe('express.raw()', function () {
test.end(done)
})
it('should presist store when limit exceeded', function (done) {
it('should persist store when limit exceeded', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/octet-stream')
@@ -545,11 +511,3 @@ function createApp (options) {
return app
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@@ -1,9 +1,10 @@
'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')
const { Buffer } = require('node:buffer');
var request = require('supertest')
var utils = require('./support/utils')
@@ -41,7 +42,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 +487,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 +509,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
const { Buffer } = require('node:buffer');
var express = require('..')
var request = require('supertest')
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('express.text()', function () {
before(function () {
this.app = createApp()
@@ -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)
})
@@ -427,7 +397,7 @@ describe('express.text()', function () {
this.app = app
})
it('should presist store', function (done) {
it('should persist store', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'text/plain')
@@ -438,18 +408,17 @@ describe('express.text()', 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('{}')
.end(done)
})
it('should presist store when inflated', function (done) {
it('should persist store when inflated', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'text/plain')
@@ -460,7 +429,7 @@ describe('express.text()', function () {
test.end(done)
})
it('should presist store when inflate error', function (done) {
it('should persist store when inflate error', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'text/plain')
@@ -470,7 +439,7 @@ describe('express.text()', function () {
test.end(done)
})
it('should presist store when limit exceeded', function (done) {
it('should persist store when limit exceeded', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'text/plain')
@@ -595,11 +564,3 @@ function createApp (options) {
return app
}
function tryRequire (name) {
try {
return require(name)
} catch (e) {
return {}
}
}

View File

@@ -1,15 +1,12 @@
'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
const { Buffer } = require('node:buffer');
var express = require('..')
var request = require('supertest')
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip
describe('express.urlencoded()', function () {
before(function () {
this.app = createApp()
@@ -62,32 +59,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 +76,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 +183,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 +201,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 +444,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 +476,7 @@ describe('express.urlencoded()', function () {
.post('/')
.set('Content-Type', 'application/x-foo')
.send('user=tobi')
.expect(200, '{}', done)
.expect(200, '', done)
})
})
@@ -632,13 +603,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)
})
@@ -672,7 +643,7 @@ describe('express.urlencoded()', function () {
this.app = app
})
it('should presist store', function (done) {
it('should persist store', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
@@ -683,18 +654,17 @@ describe('express.urlencoded()', 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('{}')
.end(done)
})
it('should presist store when inflated', function (done) {
it('should persist store when inflated', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/x-www-form-urlencoded')
@@ -705,7 +675,7 @@ describe('express.urlencoded()', function () {
test.end(done)
})
it('should presist store when inflate error', function (done) {
it('should persist store when inflate error', function (done) {
var test = request(this.app).post('/')
test.set('Content-Encoding', 'gzip')
test.set('Content-Type', 'application/x-www-form-urlencoded')
@@ -715,7 +685,7 @@ describe('express.urlencoded()', function () {
test.end(done)
})
it('should presist store when limit exceeded', function (done) {
it('should persist store when limit exceeded', function (done) {
request(this.app)
.post('/')
.set('Content-Type', 'application/x-www-form-urlencoded')
@@ -856,11 +826,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

@@ -45,6 +45,19 @@ describe('req', function(){
.set('Accept-Charset', 'foo, bar')
.expect('no', done);
})
it('should return the best matching charset from multiple inputs', function (done) {
var app = express();
app.use(function(req, res, next){
res.end(req.acceptsCharsets('utf-8', 'iso-8859-1'));
});
request(app)
.get('/')
.set('Accept-Charset', 'iso-8859-1, utf-8')
.expect('iso-8859-1', 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

@@ -39,7 +39,7 @@ describe('req', function(){
app.enable('trust proxy');
app.use(function(req, res){
req.connection.encrypted = true;
req.socket.encrypted = true;
res.end(req.protocol);
});

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')
@@ -108,7 +108,7 @@ function shouldHaveHeaderValues (key, values) {
return function (res) {
var headers = res.headers[key.toLowerCase()]
assert.ok(headers, 'should have header "' + key + '"')
assert.strictEqual(headers.length, values.length, 'should have ' + values.length + ' occurances of "' + key + '"')
assert.strictEqual(headers.length, values.length, 'should have ' + values.length + ' occurrences of "' + key + '"')
for (var i = 0; i < values.length; i++) {
assert.strictEqual(headers[i], values[i])
}

View File

@@ -1,6 +1,7 @@
'use strict'
var Buffer = require('safe-buffer').Buffer
const { Buffer } = require('node:buffer');
var express = require('../')
, request = require('supertest');

View File

@@ -32,5 +32,31 @@ describe('res', function(){
.expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT')
.expect(200, done)
})
it('should ignore maxAge', function(done){
var app = express();
app.use(function(req, res){
res.clearCookie('sid', { path: '/admin', maxAge: 1000 }).end();
});
request(app)
.get('/')
.expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT')
.expect(200, done)
})
it('should ignore user supplied expires param', function(done){
var app = express();
app.use(function(req, res){
res.clearCookie('sid', { path: '/admin', expires: new Date() }).end();
});
request(app)
.get('/')
.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,17 @@
'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
const { Buffer } = require('node:buffer');
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 +23,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 +66,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 +83,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 () {
it('should presist store', function (done) {
describe('async local storage', function () {
it('should persist 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,17 +112,17 @@ 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)
})
it('should presist store on error', function (done) {
it('should persist 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 = new AsyncLocalStorage()
req.asyncLocalStorage.run(store, next)
})
@@ -369,7 +366,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 +385,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 +485,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)
@@ -232,7 +237,7 @@ function test(app) {
})
describe('when no match is made', function(){
it('should should respond with 406 not acceptable', function(done){
it('should respond with 406 not acceptable', function(done){
request(app)
.get('/')
.set('Accept', 'foo/bar')

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(){
@@ -327,61 +327,4 @@ 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();
app.get('/', function(req, res){
res.type('application/vnd.example+json');
res.jsonp({ hello: 'world' });
});
request(app)
.get('/')
.expect('content-type', 'application/vnd.example+json; charset=utf-8')
.expect(200, '{"hello":"world"}', done)
})
})

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,7 +2,8 @@
var express = require('../')
, request = require('supertest')
, url = require('url');
, assert = require('node:assert')
, url = require('node:url');
describe('res', function(){
describe('.location(url)', function(){
@@ -45,106 +46,66 @@ describe('res', function(){
.expect(200, done)
})
it('should not encode bad "url"', function (done) {
it('should encode data uri', function (done) {
var app = express()
app.use(function (req, res) {
// This is here to show a basic check one might do which
// would pass but then the location header would still be bad
if (url.parse(req.query.q).host !== 'google.com') {
res.status(400).end('Bad url');
}
res.location(req.query.q).end();
res.location('data:text/javascript,export default () => { }').end();
});
request(app)
.get('/?q=http://google.com' + encodeURIComponent('\\@apple.com'))
.expect(200)
.expect('Location', 'http://google.com\\@apple.com')
.end(function (err) {
if (err) {
throw err;
}
.get('/')
.expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')
.expect(200, done)
})
// This ensures that our protocol check is case insensitive
request(app)
.get('/?q=HTTP://google.com' + encodeURIComponent('\\@apple.com'))
.expect(200)
.expect('Location', 'HTTP://google.com\\@apple.com')
.end(done)
});
});
it('should not touch already-encoded sequences in "url"', function (done) {
it('should consistently handle non-string input: boolean', function (done) {
var app = express()
app.use(function (req, res) {
res.location('https://google.com?q=%A710').end()
})
res.location(true).end();
});
request(app)
.get('/')
.expect('Location', 'https://google.com?q=%A710')
.expect(200, done)
})
describe('when url is "back"', function () {
it('should set location from "Referer" header', function (done) {
var app = express()
app.use(function (req, res) {
res.location('back').end()
})
request(app)
.get('/')
.set('Referer', '/some/page.html')
.expect('Location', '/some/page.html')
.expect('Location', 'true')
.expect(200, done)
})
});
it('should set location from "Referrer" header', function (done) {
var app = express()
it('should consistently handle non-string inputs: object', function (done) {
var app = express()
app.use(function (req, res) {
res.location({}).end();
});
app.use(function (req, res) {
res.location('back').end()
})
request(app)
request(app)
.get('/')
.set('Referrer', '/some/page.html')
.expect('Location', '/some/page.html')
.expect('Location', '[object%20Object]')
.expect(200, done)
})
});
it('should prefer "Referrer" header', function (done) {
var app = express()
it('should consistently handle non-string inputs: array', function (done) {
var app = express()
app.use(function (req, res) {
res.location([]).end();
});
app.use(function (req, res) {
res.location('back').end()
})
request(app)
request(app)
.get('/')
.set('Referer', '/some/page1.html')
.set('Referrer', '/some/page2.html')
.expect('Location', '/some/page2.html')
.expect('Location', '')
.expect(200, done)
})
});
it('should set the header to "/" without referrer', function (done) {
var app = express()
it('should consistently handle empty string input', function (done) {
var app = express()
app.use(function (req, res) {
res.location('').end();
});
app.use(function (req, res) {
res.location('back').end()
})
request(app)
request(app)
.get('/')
.expect('Location', '/')
.expect('Location', '')
.expect(200, done)
})
})
});
if (typeof URL !== 'undefined') {
it('should accept an instance of URL', function (done) {
@@ -161,4 +122,183 @@ describe('res', function(){
});
}
})
describe('location header encoding', function() {
function createRedirectServerForDomain (domain) {
var app = express();
app.use(function (req, res) {
var host = url.parse(req.query.q, false, true).host;
// This is here to show a basic check one might do which
// would pass but then the location header would still be bad
if (host !== domain) {
res.status(400).end('Bad host: ' + host + ' !== ' + domain);
}
res.location(req.query.q).end();
});
return app;
}
function testRequestedRedirect (app, inputUrl, expected, expectedHost, done) {
return request(app)
// Encode uri because old supertest does not and is required
// to test older node versions. New supertest doesn't re-encode
// so this works in both.
.get('/?q=' + encodeURIComponent(inputUrl))
.expect('') // No body.
.expect(200)
.expect('Location', expected)
.end(function (err, res) {
if (err) {
console.log('headers:', res.headers)
console.error('error', res.error, err);
return done(err, res);
}
// Parse the hosts from the input URL and the Location header
var inputHost = url.parse(inputUrl, false, true).host;
var locationHost = url.parse(res.headers['location'], false, true).host;
assert.strictEqual(locationHost, expectedHost);
// Assert that the hosts are the same
if (inputHost !== locationHost) {
return done(new Error('Hosts do not match: ' + inputHost + " !== " + locationHost));
}
return done(null, res);
});
}
it('should not touch already-encoded sequences in "url"', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'https://google.com?q=%A710',
'https://google.com?q=%A710',
'google.com',
done
);
});
it('should consistently handle relative urls', function (done) {
var app = createRedirectServerForDomain(null);
testRequestedRedirect(
app,
'/foo/bar',
'/foo/bar',
null,
done
);
});
it('should not encode urls in such a way that they can bypass redirect allow lists', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'http://google.com\\@apple.com',
'http://google.com\\@apple.com',
'google.com',
done
);
});
it('should not be case sensitive', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'HTTP://google.com\\@apple.com',
'HTTP://google.com\\@apple.com',
'google.com',
done
);
});
it('should work with https', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'https://google.com\\@apple.com',
'https://google.com\\@apple.com',
'google.com',
done
);
});
it('should correctly encode schemaless paths', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'//google.com\\@apple.com/',
'//google.com\\@apple.com/',
'google.com',
done
);
});
it('should keep backslashes in the path', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'https://google.com/foo\\bar\\baz',
'https://google.com/foo\\bar\\baz',
'google.com',
done
);
});
it('should escape header splitting for old node versions', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'http://google.com\\@apple.com/%0d%0afoo:%20bar',
'http://google.com\\@apple.com/%0d%0afoo:%20bar',
'google.com',
done
);
});
it('should encode unicode correctly', function (done) {
var app = createRedirectServerForDomain(null);
testRequestedRedirect(
app,
'/%e2%98%83',
'/%e2%98%83',
null,
done
);
});
it('should encode unicode correctly even with a bad host', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'http://google.com\\@apple.com/%e2%98%83',
'http://google.com\\@apple.com/%e2%98%83',
'google.com',
done
);
});
it('should work correctly despite using deprecated url.parse', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'https://google.com\'.bb.com/1.html',
'https://google.com\'.bb.com/1.html',
'google.com',
done
);
});
it('should encode file uri path', function (done) {
var app = createRedirectServerForDomain('');
testRequestedRedirect(
app,
'file:///etc\\passwd',
'file:///etc\\passwd',
'',
done
);
});
});
})

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, '<!DOCTYPE html><head><title>Found</title></head><body><p>Found. Redirecting to http://google.com</p></body>', 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, '<!DOCTYPE html><head><title>Found</title></head><body><p>Found. Redirecting to %3Cla&#39;me%3E</p></body>', 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, '<!DOCTYPE html><head><title>Found</title></head><body><p>Found. Redirecting to ' + encodedXss +'</p></body>', 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, '<!DOCTYPE html><head><title>Moved Permanently</title></head><body><p>Moved Permanently. Redirecting to http://google.com</p></body>', done);
})
})

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