Compare commits

...

28 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
23 changed files with 257 additions and 248 deletions

View File

@@ -27,11 +27,11 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
@@ -53,12 +53,12 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: ${{ matrix.node-version }}
@@ -80,7 +80,7 @@ jobs:
run: npm run test-ci
- name: Upload code coverage
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
path: ./coverage/lcov.info
@@ -93,7 +93,7 @@ jobs:
contents: read
checks: write
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
@@ -102,7 +102,7 @@ jobs:
run: sudo apt-get -y install lcov
- name: Collect coverage reports
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: ./coverage
pattern: coverage-node-*

View File

@@ -39,13 +39,13 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
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@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
with:
languages: ${{ matrix.language }}
config: |
@@ -71,4 +71,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0

View File

@@ -37,12 +37,12 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: ${{ matrix.node-version }}
@@ -64,7 +64,7 @@ jobs:
run: npm run test-ci
- name: Upload code coverage
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
path: ./coverage/lcov.info
@@ -77,7 +77,7 @@ jobs:
contents: read
checks: write
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
@@ -86,7 +86,7 @@ jobs:
run: sudo apt-get -y install lcov
- name: Collect coverage reports
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: ./coverage
pattern: coverage-node-*

View File

@@ -32,7 +32,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
@@ -59,7 +59,7 @@ jobs:
# 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@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: SARIF file
path: results.sarif
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
with:
sarif_file: results.sarif

View File

@@ -1,7 +1,24 @@
# 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
========================

View File

@@ -154,7 +154,7 @@ 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
@@ -212,7 +212,9 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
* [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>

View File

@@ -1,56 +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 `express-security@lists.openjsf.org`.
To ensure the timely response to your report, please ensure that the entirety
of the report is contained within the email body and not solely behind a web
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.
## Pre-release Versions
Alpha and Beta releases are unstable and **not suitable for production use**.
Vulnerabilities found in pre-releases should be reported according to the [Reporting a Bug](#reporting-a-bug) section.
Due to the unstable nature of the branch it is not guaranteed that any fixes will be released in the next pre-release.
## Disclosure Policy
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.
## The Express Threat Model
We are currently working on a new version of the security model, the most updated version can be found [here](https://github.com/expressjs/security-wg/blob/main/docs/ThreatModel.md)
## Comments on this Policy
If you have suggestions on how this process could be improved please submit a
pull request.

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

@@ -16,31 +16,47 @@ 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, next){
var query = req.params.query;
db.smembers(query, function(err, vals){
if (err) return next(err);
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

@@ -523,7 +523,7 @@ app.render = function render(name, options, callback) {
var cache = this.cache;
var done = callback;
var engines = this.engines;
var opts = options;
var opts = options || {};
var view;
// support callback function as second arg

View File

@@ -83,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.
*
@@ -107,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"
@@ -115,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,17 +143,34 @@ req.acceptsEncodings = function(){
};
/**
* 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);
};
/**

View File

@@ -126,7 +126,6 @@ res.send = function send(body) {
var chunk = body;
var encoding;
var req = this.req;
var type;
// settings
var app = this.app;
@@ -134,7 +133,12 @@ res.send = function send(body) {
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;
@@ -153,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'
@@ -850,7 +843,8 @@ res.redirect = function redirect(url) {
html: function(){
var u = escapeHtml(address);
body = '<p>' + statuses.message[status] + '. Redirecting to ' + u + '</p>'
body = '<!DOCTYPE html><head><title>' + statuses.message[status] + '</title></head>'
+ '<body><p>' + statuses.message[status] + '. Redirecting to ' + u + '</p></body>'
},
default: function(){

View File

@@ -52,7 +52,7 @@
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.14.0",
"qs": "^6.14.2",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",

View File

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

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

@@ -327,18 +327,4 @@ describe('res', function(){
})
})
})
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

@@ -46,19 +46,7 @@ describe('res', function(){
.expect(200, done)
})
it('should encode data uri1', function (done) {
var app = express()
app.use(function (req, res) {
res.location('data:text/javascript,export default () => { }').end();
});
request(app)
.get('/')
.expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')
.expect(200, done)
})
it('should encode data uri2', function (done) {
it('should encode data uri', function (done) {
var app = express()
app.use(function (req, res) {
res.location('data:text/javascript,export default () => { }').end();

View File

@@ -91,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 http://google.com</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){
@@ -107,7 +107,7 @@ describe('res', function(){
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', '%3Cla\'me%3E')
.expect(302, '<p>Found. Redirecting to %3Cla&#39;me%3E</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){
@@ -125,7 +125,7 @@ describe('res', function(){
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', encodedXss)
.expect(302, '<p>Found. Redirecting to ' + encodedXss +'</p>', done);
.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){
@@ -140,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 http://google.com</p>', done);
.expect(301, '<!DOCTYPE html><head><title>Moved Permanently</title></head><body><p>Moved Permanently. Redirecting to http://google.com</p></body>', done);
})
})

View File

@@ -42,5 +42,74 @@ describe('res', function(){
.get('/')
.expect('Content-Type', 'application/vnd.amazon.ebook', done);
})
describe('edge cases', function(){
it('should handle empty string gracefully', function(done){
var app = express();
app.use(function(req, res){
res.type('').end('test');
});
request(app)
.get('/')
.expect('Content-Type', 'application/octet-stream')
.end(done);
})
it('should handle file extension with dots', function(done){
var app = express();
app.use(function(req, res){
res.type('.json').end('{"test": true}');
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.end(done);
})
it('should handle multiple file extensions', function(done){
var app = express();
app.use(function(req, res){
res.type('file.tar.gz').end('compressed');
});
request(app)
.get('/')
.expect('Content-Type', 'application/gzip')
.end(done);
})
it('should handle uppercase extensions', function(done){
var app = express();
app.use(function(req, res){
res.type('FILE.JSON').end('{"test": true}');
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.end(done);
})
it('should handle extension with special characters', function(done){
var app = express();
app.use(function(req, res){
res.type('file@test.json').end('{"test": true}');
});
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.end(done);
})
})
})
})

View File

@@ -35,8 +35,15 @@ describe('utils.normalizeType acceptParams method', () => {
params: {} // No parameters are added since "invalid" has no "="
});
});
});
it('should default to application/octet-stream when mime lookup fails', () => {
const result = utils.normalizeType('unknown-extension-xyz');
assert.deepEqual(result, {
value: 'application/octet-stream',
params: {}
});
});
});
describe('utils.setCharset(type, charset)', function () {
it('should do anything without type', function () {
@@ -81,3 +88,28 @@ describe('utils.wetag(body, encoding)', function(){
'W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"')
})
})
describe('utils.compileETag()', function () {
it('should return generateETag for true', function () {
const fn = utils.compileETag(true);
assert.strictEqual(fn('express!'), utils.wetag('express!'));
});
it('should return undefined for false', function () {
assert.strictEqual(utils.compileETag(false), undefined);
});
it('should return generateETag for string values "strong" and "weak"', function () {
assert.strictEqual(utils.compileETag('strong')("express"), utils.etag("express"));
assert.strictEqual(utils.compileETag('weak')("express"), utils.wetag("express"));
});
it('should throw for unknown string values', function () {
assert.throws(() => utils.compileETag('foo'), TypeError);
});
it('should throw for unsupported types like arrays and objects', function () {
assert.throws(() => utils.compileETag([]), TypeError);
assert.throws(() => utils.compileETag({}), TypeError);
});
});