mirror of
https://github.com/expressjs/express.git
synced 2026-02-27 19:20:15 +00:00
Compare commits
29 Commits
req.is
...
defineProp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07b2eb3de5 | ||
|
|
9eb700151b | ||
|
|
dbac741a49 | ||
|
|
697547cde6 | ||
|
|
4007ad103b | ||
|
|
2f64f68c37 | ||
|
|
ed0ba3f1dc | ||
|
|
8eace4603c | ||
|
|
30bae81027 | ||
|
|
758d4355d4 | ||
|
|
77bcd5274a | ||
|
|
f33caf1f89 | ||
|
|
54af593b73 | ||
|
|
2551a7d8af | ||
|
|
4453d83cca | ||
|
|
db507669ca | ||
|
|
374fc1a0f9 | ||
|
|
1b196c8b82 | ||
|
|
64e7373d69 | ||
|
|
e4fb370ad8 | ||
|
|
60d4c16cc9 | ||
|
|
9e6760e186 | ||
|
|
ffa89f2ccf | ||
|
|
b9b9f52b2f | ||
|
|
9a7afb2886 | ||
|
|
2eb42059f3 | ||
|
|
aa907945cd | ||
|
|
89f198c6a5 | ||
|
|
d9a62f9833 |
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@@ -27,11 +27,11 @@ jobs:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
@@ -46,19 +46,19 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node-version: [18, 19, 20, 21, 22, 23, 24]
|
||||
node-version: [18, 19, 20, 21, 22, 23, 24, 25]
|
||||
# Node.js release schedule: https://nodejs.org/en/about/releases/
|
||||
|
||||
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
run: sudo apt-get -y install lcov
|
||||
|
||||
- name: Collect coverage reports
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
with:
|
||||
path: ./coverage
|
||||
pattern: coverage-node-*
|
||||
@@ -112,6 +112,6 @@ jobs:
|
||||
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6
|
||||
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
|
||||
with:
|
||||
file: ./lcov.info
|
||||
|
||||
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -39,13 +39,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/init@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
|
||||
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@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/analyze@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
|
||||
|
||||
12
.github/workflows/legacy.yml
vendored
12
.github/workflows/legacy.yml
vendored
@@ -37,12 +37,12 @@ jobs:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
run: sudo apt-get -y install lcov
|
||||
|
||||
- name: Collect coverage reports
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
with:
|
||||
path: ./coverage
|
||||
pattern: coverage-node-*
|
||||
@@ -96,6 +96,6 @@ jobs:
|
||||
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6
|
||||
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
|
||||
with:
|
||||
file: ./lcov.info
|
||||
|
||||
8
.github/workflows/scorecard.yml
vendored
8
.github/workflows/scorecard.yml
vendored
@@ -32,12 +32,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@@ -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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.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@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,6 +5,10 @@ npm-shrinkwrap.json
|
||||
*.log
|
||||
*.gz
|
||||
|
||||
# Yarn
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
|
||||
# Coveralls
|
||||
.nyc_output
|
||||
coverage
|
||||
|
||||
13
History.md
13
History.md
@@ -1,3 +1,16 @@
|
||||
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
|
||||
========================
|
||||
|
||||
|
||||
21
Readme.md
21
Readme.md
@@ -40,7 +40,9 @@ app.get('/', (req, res) => {
|
||||
res.send('Hello World')
|
||||
})
|
||||
|
||||
app.listen(3000)
|
||||
app.listen(3000, () => {
|
||||
console.log('Server is running on http://localhost:3000')
|
||||
})
|
||||
```
|
||||
|
||||
## Installation
|
||||
@@ -206,13 +208,11 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
|
||||
* [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran**
|
||||
* [carpasse](https://github.com/carpasse) - **Carlos Serrano**
|
||||
* [CBID2](https://github.com/CBID2) - **Christine Belzie**
|
||||
* [dpopp07](https://github.com/dpopp07) - **Dustin Popp**
|
||||
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
|
||||
* [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi**
|
||||
* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him)
|
||||
* [Phillip9587](https://github.com/Phillip9587) - **Phillip Barta**
|
||||
* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
|
||||
* [rxmarbles](https://github.com/rxmarbles) **Rick Markins** (He/him)
|
||||
* [efekrskl](https://github.com/efekrskl) - **Efe Karasakal**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Triagers emeriti members</summary>
|
||||
@@ -251,6 +251,9 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
|
||||
* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)
|
||||
* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**
|
||||
* [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>
|
||||
|
||||
@@ -259,14 +262,14 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[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=CI
|
||||
[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-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
|
||||
|
||||
@@ -597,7 +597,7 @@ app.render = function render(name, options, callback) {
|
||||
|
||||
app.listen = function listen() {
|
||||
var server = http.createServer(this)
|
||||
var args = Array.prototype.slice.call(arguments)
|
||||
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)
|
||||
|
||||
554
lib/request.js
554
lib/request.js
@@ -17,7 +17,7 @@ var accepts = require('accepts');
|
||||
var isIP = require('node:net').isIP;
|
||||
var typeis = require('type-is');
|
||||
var http = require('node:http');
|
||||
var fresh = require('fresh');
|
||||
var freshModule = require('fresh');
|
||||
var parseRange = require('range-parser');
|
||||
var parse = require('parseurl');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
@@ -204,29 +204,6 @@ req.range = function range(size, options) {
|
||||
return parseRange(size, range, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the query string of `req.url`.
|
||||
*
|
||||
* This uses the "query parser" setting to parse the raw
|
||||
* string into an object.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'query', function query(){
|
||||
var queryparse = this.app.get('query parser fn');
|
||||
|
||||
if (!queryparse) {
|
||||
// parsing is disabled
|
||||
return Object.create(null);
|
||||
}
|
||||
|
||||
var querystring = parse(this).query;
|
||||
|
||||
return queryparse(querystring);
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the incoming request contains the "Content-Type"
|
||||
* header field, and it contains the given mime `type`.
|
||||
@@ -268,247 +245,296 @@ req.is = function is(types) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the protocol string "http" or "https"
|
||||
* when requested with TLS. When the "trust proxy"
|
||||
* setting trusts the socket address, the
|
||||
* "X-Forwarded-Proto" header field will be trusted
|
||||
* and used if present.
|
||||
*
|
||||
* If you're running behind a reverse proxy that
|
||||
* supplies https for you this may be enabled.
|
||||
*
|
||||
* @return {String}
|
||||
* @public
|
||||
* Define getter properties on the request prototype.
|
||||
*/
|
||||
|
||||
defineGetter(req, 'protocol', function protocol(){
|
||||
var proto = this.connection.encrypted
|
||||
? 'https'
|
||||
: 'http';
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
|
||||
if (!trust(this.connection.remoteAddress, 0)) {
|
||||
return proto;
|
||||
}
|
||||
|
||||
// Note: X-Forwarded-Proto is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
var header = this.get('X-Forwarded-Proto') || proto
|
||||
var index = header.indexOf(',')
|
||||
|
||||
return index !== -1
|
||||
? header.substring(0, index).trim()
|
||||
: header.trim()
|
||||
});
|
||||
|
||||
/**
|
||||
* Short-hand for:
|
||||
*
|
||||
* req.protocol === 'https'
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'secure', function secure(){
|
||||
return this.protocol === 'https';
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the remote address from the trusted proxy.
|
||||
*
|
||||
* The is the remote address on the socket unless
|
||||
* "trust proxy" is set.
|
||||
*
|
||||
* @return {String}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'ip', function ip(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
return proxyaddr(this, trust);
|
||||
});
|
||||
|
||||
/**
|
||||
* When "trust proxy" is set, trusted proxy addresses + client.
|
||||
*
|
||||
* For example if the value were "client, proxy1, proxy2"
|
||||
* you would receive the array `["client", "proxy1", "proxy2"]`
|
||||
* where "proxy2" is the furthest down-stream and "proxy1" and
|
||||
* "proxy2" were trusted.
|
||||
*
|
||||
* @return {Array}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'ips', function ips() {
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var addrs = proxyaddr.all(this, trust);
|
||||
|
||||
// reverse the order (to farthest -> closest)
|
||||
// and remove socket address
|
||||
addrs.reverse().pop()
|
||||
|
||||
return addrs
|
||||
});
|
||||
|
||||
/**
|
||||
* Return subdomains as an array.
|
||||
*
|
||||
* Subdomains are the dot-separated parts of the host before the main domain of
|
||||
* the app. By default, the domain of the app is assumed to be the last two
|
||||
* parts of the host. This can be changed by setting "subdomain offset".
|
||||
*
|
||||
* For example, if the domain is "tobi.ferrets.example.com":
|
||||
* If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
|
||||
* If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
|
||||
*
|
||||
* @return {Array}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'subdomains', function subdomains() {
|
||||
var hostname = this.hostname;
|
||||
|
||||
if (!hostname) return [];
|
||||
|
||||
var offset = this.app.get('subdomain offset');
|
||||
var subdomains = !isIP(hostname)
|
||||
? hostname.split('.').reverse()
|
||||
: [hostname];
|
||||
|
||||
return subdomains.slice(offset);
|
||||
});
|
||||
|
||||
/**
|
||||
* Short-hand for `url.parse(req.url).pathname`.
|
||||
*
|
||||
* @return {String}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'path', function path() {
|
||||
return parse(this).pathname;
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse the "Host" header field to a host.
|
||||
*
|
||||
* When the "trust proxy" setting trusts the socket
|
||||
* address, the "X-Forwarded-Host" header field will
|
||||
* be trusted.
|
||||
*
|
||||
* @return {String}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'host', function host(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var val = this.get('X-Forwarded-Host');
|
||||
|
||||
if (!val || !trust(this.connection.remoteAddress, 0)) {
|
||||
val = this.get('Host');
|
||||
} else if (val.indexOf(',') !== -1) {
|
||||
// Note: X-Forwarded-Host is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
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
|
||||
var offset = host[0] === '['
|
||||
? host.indexOf(']') + 1
|
||||
: 0;
|
||||
var index = host.indexOf(':', offset);
|
||||
|
||||
return index !== -1
|
||||
? host.substring(0, index)
|
||||
: host;
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the request is fresh, aka
|
||||
* Last-Modified or the ETag
|
||||
* still match.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'fresh', function(){
|
||||
var method = this.method;
|
||||
var res = this.res
|
||||
var status = res.statusCode
|
||||
|
||||
// GET or HEAD for weak freshness validation only
|
||||
if ('GET' !== method && 'HEAD' !== method) return false;
|
||||
|
||||
// 2xx or 304 as per rfc2616 14.26
|
||||
if ((status >= 200 && status < 300) || 304 === status) {
|
||||
return fresh(this.headers, {
|
||||
'etag': res.get('ETag'),
|
||||
'last-modified': res.get('Last-Modified')
|
||||
})
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the request is stale, aka
|
||||
* "Last-Modified" and / or the "ETag" for the
|
||||
* resource has changed.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'stale', function stale(){
|
||||
return !this.fresh;
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the request was an _XMLHttpRequest_.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'xhr', function xhr(){
|
||||
var val = this.get('X-Requested-With') || '';
|
||||
return val.toLowerCase() === 'xmlhttprequest';
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function for creating a getter on an object.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @param {String} name
|
||||
* @param {Function} getter
|
||||
* @private
|
||||
*/
|
||||
function defineGetter(obj, name, getter) {
|
||||
Object.defineProperty(obj, name, {
|
||||
Object.defineProperties(req, {
|
||||
/**
|
||||
* Parse the query string of `req.url`.
|
||||
*
|
||||
* This uses the "query parser" setting to parse the raw
|
||||
* string into an object.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
query: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: getter
|
||||
});
|
||||
}
|
||||
get: function query() {
|
||||
var queryparse = this.app.get('query parser fn');
|
||||
|
||||
if (!queryparse) {
|
||||
// parsing is disabled
|
||||
return Object.create(null);
|
||||
}
|
||||
|
||||
var querystring = parse(this).query;
|
||||
|
||||
return queryparse(querystring);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the protocol string "http" or "https"
|
||||
* when requested with TLS. When the "trust proxy"
|
||||
* setting trusts the socket address, the
|
||||
* "X-Forwarded-Proto" header field will be trusted
|
||||
* and used if present.
|
||||
*
|
||||
* If you're running behind a reverse proxy that
|
||||
* supplies https for you this may be enabled.
|
||||
*
|
||||
* @return {String}
|
||||
* @public
|
||||
*/
|
||||
protocol: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function protocol() {
|
||||
var proto = this.socket.encrypted
|
||||
? 'https'
|
||||
: 'http';
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
|
||||
if (!trust(this.socket.remoteAddress, 0)) {
|
||||
return proto;
|
||||
}
|
||||
|
||||
// Note: X-Forwarded-Proto is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
var header = this.get('X-Forwarded-Proto') || proto
|
||||
var index = header.indexOf(',')
|
||||
|
||||
return index !== -1
|
||||
? header.substring(0, index).trim()
|
||||
: header.trim()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Short-hand for:
|
||||
*
|
||||
* req.protocol === 'https'
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
secure: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function secure() {
|
||||
return this.protocol === 'https';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the remote address from the trusted proxy.
|
||||
*
|
||||
* The is the remote address on the socket unless
|
||||
* "trust proxy" is set.
|
||||
*
|
||||
* @return {String}
|
||||
* @public
|
||||
*/
|
||||
ip: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function ip() {
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
return proxyaddr(this, trust);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When "trust proxy" is set, trusted proxy addresses + client.
|
||||
*
|
||||
* For example if the value were "client, proxy1, proxy2"
|
||||
* you would receive the array `["client", "proxy1", "proxy2"]`
|
||||
* where "proxy2" is the furthest down-stream and "proxy1" and
|
||||
* "proxy2" were trusted.
|
||||
*
|
||||
* @return {Array}
|
||||
* @public
|
||||
*/
|
||||
ips: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function ips() {
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var addrs = proxyaddr.all(this, trust);
|
||||
|
||||
// reverse the order (to farthest -> closest)
|
||||
// and remove socket address
|
||||
addrs.reverse().pop()
|
||||
|
||||
return addrs
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return subdomains as an array.
|
||||
*
|
||||
* Subdomains are the dot-separated parts of the host before the main domain of
|
||||
* the app. By default, the domain of the app is assumed to be the last two
|
||||
* parts of the host. This can be changed by setting "subdomain offset".
|
||||
*
|
||||
* For example, if the domain is "tobi.ferrets.example.com":
|
||||
* If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
|
||||
* If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
|
||||
*
|
||||
* @return {Array}
|
||||
* @public
|
||||
*/
|
||||
subdomains: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function subdomains() {
|
||||
var hostname = this.hostname;
|
||||
|
||||
if (!hostname) return [];
|
||||
|
||||
var offset = this.app.get('subdomain offset');
|
||||
var subdomains = !isIP(hostname)
|
||||
? hostname.split('.').reverse()
|
||||
: [hostname];
|
||||
|
||||
return subdomains.slice(offset);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Short-hand for `url.parse(req.url).pathname`.
|
||||
*
|
||||
* @return {String}
|
||||
* @public
|
||||
*/
|
||||
path: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function path() {
|
||||
return parse(this).pathname;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the "Host" header field to a host.
|
||||
*
|
||||
* When the "trust proxy" setting trusts the socket
|
||||
* address, the "X-Forwarded-Host" header field will
|
||||
* be trusted.
|
||||
*
|
||||
* @return {String}
|
||||
* @public
|
||||
*/
|
||||
host: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function host() {
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var val = this.get('X-Forwarded-Host');
|
||||
|
||||
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.
|
||||
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
|
||||
*/
|
||||
hostname: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function hostname() {
|
||||
var host = this.host;
|
||||
|
||||
if (!host) return;
|
||||
|
||||
// IPv6 literal support
|
||||
var offset = host[0] === '['
|
||||
? host.indexOf(']') + 1
|
||||
: 0;
|
||||
var index = host.indexOf(':', offset);
|
||||
|
||||
return index !== -1
|
||||
? host.substring(0, index)
|
||||
: host;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the request is fresh, aka
|
||||
* Last-Modified or the ETag
|
||||
* still match.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
fresh: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function fresh() {
|
||||
var method = this.method;
|
||||
var res = this.res
|
||||
var status = res.statusCode
|
||||
|
||||
// GET or HEAD for weak freshness validation only
|
||||
if ('GET' !== method && 'HEAD' !== method) return false;
|
||||
|
||||
// 2xx or 304 as per rfc2616 14.26
|
||||
if ((status >= 200 && status < 300) || 304 === status) {
|
||||
return freshModule(this.headers, {
|
||||
'etag': res.get('ETag'),
|
||||
'last-modified': res.get('Last-Modified')
|
||||
})
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the request is stale, aka
|
||||
* "Last-Modified" and / or the "ETag" for the
|
||||
* resource has changed.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
stale: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function stale() {
|
||||
return !this.fresh;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the request was an _XMLHttpRequest_.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
xhr: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function xhr() {
|
||||
var val = this.get('X-Requested-With') || '';
|
||||
return val.toLowerCase() === 'xmlhttprequest';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Fast, unopinionated, minimalist web framework",
|
||||
"version": "5.1.0",
|
||||
"version": "5.2.1",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
|
||||
@@ -33,7 +33,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.0",
|
||||
"body-parser": "^2.2.1",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "^1.0.5",
|
||||
"cookie": "^0.7.1",
|
||||
@@ -84,7 +84,6 @@
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"History.md",
|
||||
"Readme.md",
|
||||
"index.js",
|
||||
"lib/"
|
||||
@@ -92,7 +91,7 @@
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"test": "mocha --require test/support/env --exit --reporter spec --check-leaks test/ test/acceptance/",
|
||||
"test": "mocha --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
|
||||
"test-ci": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=lcovonly --reporter=text npm test",
|
||||
"test-cov": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=html --reporter=text npm test",
|
||||
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
var after = require('after')
|
||||
|
||||
describe('req.is()', function () {
|
||||
describe('when given a mime type', function () {
|
||||
@@ -28,24 +27,10 @@ describe('req.is()', function () {
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('application/json')
|
||||
.send('{}')
|
||||
.expect(200, 'false', done)
|
||||
})
|
||||
|
||||
it('should return false when none in list matches', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.json(req.is(['image/jpeg', 'text/html']))
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('application/json')
|
||||
.send('{}')
|
||||
.expect(200, 'false', done)
|
||||
.post('/')
|
||||
.type('application/json')
|
||||
.send('{}')
|
||||
.expect(200, 'false', done)
|
||||
})
|
||||
|
||||
it('should ignore charset', function (done) {
|
||||
@@ -56,15 +41,15 @@ describe('req.is()', function () {
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('application/json; charset=UTF-8')
|
||||
.send('{}')
|
||||
.expect(200, '"application/json"', done)
|
||||
.post('/')
|
||||
.type('application/json; charset=UTF-8')
|
||||
.send('{}')
|
||||
.expect(200, '"application/json"', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when content-type is not present', function(){
|
||||
it('should return false for single type', function (done) {
|
||||
it('should return false', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res) {
|
||||
@@ -76,19 +61,6 @@ describe('req.is()', function () {
|
||||
.send('{}')
|
||||
.expect(200, 'false', done)
|
||||
})
|
||||
|
||||
it('should return false for multiple types', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.json(req.is(['application/json', 'image/jpeg']))
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.send('{}')
|
||||
.expect(200, 'false', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when given an extension', function(){
|
||||
@@ -105,27 +77,6 @@ describe('req.is()', function () {
|
||||
.send('{}')
|
||||
.expect(200, '"json"', done)
|
||||
})
|
||||
|
||||
it('should lookup the first matching extension from list', function (done) {
|
||||
var app = express()
|
||||
var cb = after(2, done)
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.json(req.is(['json', 'html']))
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('application/json')
|
||||
.send('{}')
|
||||
.expect(200, '"json"', cb)
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('text/html')
|
||||
.send('{}')
|
||||
.expect(200, '"html"', cb)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when given */subtype', function(){
|
||||
@@ -215,31 +166,4 @@ describe('req.is()', function () {
|
||||
.expect(200, '"application/json"', done)
|
||||
})
|
||||
})
|
||||
|
||||
it('should match wildcards in list and return full type or false', function (done){
|
||||
var app = express()
|
||||
var cb = after(3, done)
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.json(req.is(['application/*', '*/jpeg']))
|
||||
})
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('image/jpeg')
|
||||
.send('{}')
|
||||
.expect(200, '"image/jpeg"', cb)
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('text/html')
|
||||
.send('{}')
|
||||
.expect(200, 'false', cb)
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('application/json')
|
||||
.send('{}')
|
||||
.expect(200, '"application/json"', cb)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user