mirror of
https://github.com/zebrajr/express.git
synced 2025-12-06 00:19:48 +01:00
Merge tag '4.19.0' into 5.x
4.19.0
This commit is contained in:
commit
e9f9aaeebd
91
.github/workflows/ci.yml
vendored
91
.github/workflows/ci.yml
vendored
|
|
@ -23,6 +23,13 @@ jobs:
|
||||||
- Node.js 12.x
|
- Node.js 12.x
|
||||||
- Node.js 13.x
|
- Node.js 13.x
|
||||||
- Node.js 14.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
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- name: Node.js 4.0
|
- name: Node.js 4.0
|
||||||
|
|
@ -39,7 +46,7 @@ jobs:
|
||||||
|
|
||||||
- name: Node.js 6.x
|
- name: Node.js 6.x
|
||||||
node-version: "6.17"
|
node-version: "6.17"
|
||||||
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6
|
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2
|
||||||
|
|
||||||
- name: Node.js 7.x
|
- name: Node.js 7.x
|
||||||
node-version: "7.10"
|
node-version: "7.10"
|
||||||
|
|
@ -47,11 +54,11 @@ jobs:
|
||||||
|
|
||||||
- name: Node.js 8.x
|
- name: Node.js 8.x
|
||||||
node-version: "8.17"
|
node-version: "8.17"
|
||||||
npm-i: mocha@7.2.0
|
npm-i: mocha@7.2.0 nyc@14.1.1
|
||||||
|
|
||||||
- name: Node.js 9.x
|
- name: Node.js 9.x
|
||||||
node-version: "9.11"
|
node-version: "9.11"
|
||||||
npm-i: mocha@7.2.0
|
npm-i: mocha@7.2.0 nyc@14.1.1
|
||||||
|
|
||||||
- name: Node.js 10.x
|
- name: Node.js 10.x
|
||||||
node-version: "10.24"
|
node-version: "10.24"
|
||||||
|
|
@ -63,15 +70,38 @@ jobs:
|
||||||
|
|
||||||
- name: Node.js 12.x
|
- name: Node.js 12.x
|
||||||
node-version: "12.22"
|
node-version: "12.22"
|
||||||
|
npm-i: mocha@9.2.2
|
||||||
|
|
||||||
- name: Node.js 13.x
|
- name: Node.js 13.x
|
||||||
node-version: "13.14"
|
node-version: "13.14"
|
||||||
|
npm-i: mocha@9.2.2
|
||||||
|
|
||||||
- name: Node.js 14.x
|
- name: Node.js 14.x
|
||||||
node-version: "14.19"
|
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"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Node.js ${{ matrix.node-version }}
|
- name: Install Node.js ${{ matrix.node-version }}
|
||||||
shell: bash -eo pipefail -l {0}
|
shell: bash -eo pipefail -l {0}
|
||||||
|
|
@ -82,7 +112,11 @@ jobs:
|
||||||
- name: Configure npm
|
- name: Configure npm
|
||||||
run: |
|
run: |
|
||||||
npm config set loglevel error
|
npm config set loglevel error
|
||||||
npm config set shrinkwrap false
|
if [[ "$(npm config get package-lock)" == "true" ]]; then
|
||||||
|
npm config set package-lock false
|
||||||
|
else
|
||||||
|
npm config set shrinkwrap false
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Install npm module(s) ${{ matrix.npm-i }}
|
- name: Install npm module(s) ${{ matrix.npm-i }}
|
||||||
run: npm install --save-dev ${{ matrix.npm-i }}
|
run: npm install --save-dev ${{ matrix.npm-i }}
|
||||||
|
|
@ -95,8 +129,8 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# eslint for linting
|
# eslint for linting
|
||||||
# - remove on Node.js < 10
|
# - remove on Node.js < 12
|
||||||
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then
|
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then
|
||||||
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
|
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
|
||||||
grep -E '^eslint(-|$)' | \
|
grep -E '^eslint(-|$)' | \
|
||||||
sort -r | \
|
sort -r | \
|
||||||
|
|
@ -113,29 +147,52 @@ jobs:
|
||||||
echo "node@$(node -v)"
|
echo "node@$(node -v)"
|
||||||
echo "npm@$(npm -v)"
|
echo "npm@$(npm -v)"
|
||||||
npm -s ls ||:
|
npm -s ls ||:
|
||||||
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print "::set-output name=" $2 "::" $3 }'
|
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: npm run test-ci
|
run: |
|
||||||
|
npm run test-ci
|
||||||
|
cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov"
|
||||||
|
|
||||||
- name: Lint code
|
- name: Lint code
|
||||||
if: steps.list_env.outputs.eslint != ''
|
if: steps.list_env.outputs.eslint != ''
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
|
|
||||||
- name: Collect code coverage
|
- name: Collect code coverage
|
||||||
uses: coverallsapp/github-action@master
|
run: |
|
||||||
|
mv ./coverage "./${{ matrix.name }}"
|
||||||
|
mkdir ./coverage
|
||||||
|
mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}"
|
||||||
|
|
||||||
|
- name: Upload code coverage
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
name: coverage
|
||||||
flag-name: run-${{ matrix.test_number }}
|
path: ./coverage
|
||||||
parallel: true
|
retention-days: 1
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
needs: test
|
needs: test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Upload code coverage
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- 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: Merge coverage reports
|
||||||
|
shell: bash
|
||||||
|
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./coverage/lcov.info
|
||||||
|
|
||||||
|
- name: Upload coverage report
|
||||||
uses: coverallsapp/github-action@master
|
uses: coverallsapp/github-action@master
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.github_token }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
parallel-finished: true
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ also easily visible to outsiders.
|
||||||
|
|
||||||
## Section 1: Scope
|
## Section 1: Scope
|
||||||
|
|
||||||
Express is a http web server framework with a simple and expressive API
|
Express is a HTTP web server framework with a simple and expressive API
|
||||||
which is highly aligned with Node.js core. We aim to be the best in
|
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
|
class for writing performant, spec compliant, and powerful web servers
|
||||||
in Node.js. As one of the oldest and most popular web frameworks in
|
in Node.js. As one of the oldest and most popular web frameworks in
|
||||||
|
|
@ -24,7 +24,7 @@ Express is made of many modules spread between three GitHub Orgs:
|
||||||
libraries
|
libraries
|
||||||
- [pillarjs](http://github.com/pillarjs/): Components which make up
|
- [pillarjs](http://github.com/pillarjs/): Components which make up
|
||||||
Express but can also be used for other web frameworks
|
Express but can also be used for other web frameworks
|
||||||
- [jshttp](http://github.com/jshttp/): Low level http libraries
|
- [jshttp](http://github.com/jshttp/): Low level HTTP libraries
|
||||||
|
|
||||||
### 1.2: Out-of-Scope
|
### 1.2: Out-of-Scope
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ contributors can be involved in decision making.
|
||||||
|
|
||||||
* A **Contributor** is any individual creating or commenting on an issue or pull request.
|
* A **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 **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
|
* A **TC (Technical Committee)** is a group of committers representing the required technical
|
||||||
expertise to resolve rare disputes.
|
expertise to resolve rare disputes.
|
||||||
* A **Triager** is a subset of contributors who have been given triage access to the repository.
|
* A **Triager** is a subset of contributors who have been given triage access to the repository.
|
||||||
|
|
@ -102,12 +103,74 @@ If a consensus cannot be reached that has no objections then a majority wins vot
|
||||||
is called. It is also expected that the majority of decisions made by the TC are via
|
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.
|
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
|
Resolution may involve returning the issue to project captains with suggestions on
|
||||||
move forward towards a consensus. It is not expected that a meeting of the TC
|
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
|
will resolve all issues on its agenda during that meeting and may prefer to continue
|
||||||
the discussion happening among the committers.
|
the discussion happening among the project captains.
|
||||||
|
|
||||||
Members can be added to the TC at any time. Any committer can nominate another committer
|
Members can be added to the TC at any time. Any TC member can nominate another committer
|
||||||
to the TC and the TC uses its standard consensus seeking process to evaluate whether or
|
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
|
not to add this new member. The TC will consist of a minimum of 3 active members and a
|
||||||
a majority of the other members are expected to resign.
|
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
|
||||||
|
|
||||||
|
|
|
||||||
90
History.md
90
History.md
|
|
@ -162,6 +162,86 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
||||||
* add:
|
* add:
|
||||||
- `app.router` is a reference to the base router
|
- `app.router` is a reference to the base router
|
||||||
|
|
||||||
|
4.18.3 / 2024-03-20
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Prevent open redirect allow list bypass due to encodeurl
|
||||||
|
* deps: cookie@0.6.0
|
||||||
|
|
||||||
|
4.18.3 / 2024-02-29
|
||||||
|
==========
|
||||||
|
|
||||||
|
* Fix routing requests without method
|
||||||
|
* deps: body-parser@1.20.2
|
||||||
|
- Fix strict json error message on Node.js 19+
|
||||||
|
- deps: content-type@~1.0.5
|
||||||
|
- deps: raw-body@2.5.2
|
||||||
|
* deps: cookie@0.6.0
|
||||||
|
- Add `partitioned` option
|
||||||
|
|
||||||
|
4.18.2 / 2022-10-08
|
||||||
|
===================
|
||||||
|
|
||||||
|
* Fix regression routing a large stack in a single route
|
||||||
|
* deps: body-parser@1.20.1
|
||||||
|
- deps: qs@6.11.0
|
||||||
|
- perf: remove unnecessary object clone
|
||||||
|
* deps: qs@6.11.0
|
||||||
|
|
||||||
|
4.18.1 / 2022-04-29
|
||||||
|
===================
|
||||||
|
|
||||||
|
* Fix hanging on large stack of sync routes
|
||||||
|
|
||||||
|
4.18.0 / 2022-04-25
|
||||||
|
===================
|
||||||
|
|
||||||
|
* Add "root" option to `res.download`
|
||||||
|
* Allow `options` without `filename` in `res.download`
|
||||||
|
* Deprecate string and non-integer arguments to `res.status`
|
||||||
|
* Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie`
|
||||||
|
* Fix handling very large stacks of sync middleware
|
||||||
|
* Ignore `Object.prototype` values in settings through `app.set`/`app.get`
|
||||||
|
* Invoke `default` with same arguments as types in `res.format`
|
||||||
|
* Support proper 205 responses using `res.send`
|
||||||
|
* Use `http-errors` for `res.format` error
|
||||||
|
* deps: body-parser@1.20.0
|
||||||
|
- Fix error message for json parse whitespace in `strict`
|
||||||
|
- Fix internal error when inflated body exceeds limit
|
||||||
|
- Prevent loss of async hooks context
|
||||||
|
- Prevent hanging when request already read
|
||||||
|
- deps: depd@2.0.0
|
||||||
|
- deps: http-errors@2.0.0
|
||||||
|
- deps: on-finished@2.4.1
|
||||||
|
- deps: qs@6.10.3
|
||||||
|
- deps: raw-body@2.5.1
|
||||||
|
* deps: cookie@0.5.0
|
||||||
|
- Add `priority` option
|
||||||
|
- Fix `expires` option to reject invalid dates
|
||||||
|
* deps: depd@2.0.0
|
||||||
|
- Replace internal `eval` usage with `Function` constructor
|
||||||
|
- Use instance methods on `process` to check for listeners
|
||||||
|
* deps: finalhandler@1.2.0
|
||||||
|
- Remove set content headers that break response
|
||||||
|
- deps: on-finished@2.4.1
|
||||||
|
- deps: statuses@2.0.1
|
||||||
|
* deps: on-finished@2.4.1
|
||||||
|
- Prevent loss of async hooks context
|
||||||
|
* deps: qs@6.10.3
|
||||||
|
* deps: send@0.18.0
|
||||||
|
- Fix emitted 416 error missing headers property
|
||||||
|
- Limit the headers removed for 304 response
|
||||||
|
- deps: depd@2.0.0
|
||||||
|
- deps: destroy@1.2.0
|
||||||
|
- deps: http-errors@2.0.0
|
||||||
|
- deps: on-finished@2.4.1
|
||||||
|
- deps: statuses@2.0.1
|
||||||
|
* deps: serve-static@1.15.0
|
||||||
|
- deps: send@0.18.0
|
||||||
|
* deps: statuses@2.0.1
|
||||||
|
- Remove code 306
|
||||||
|
- Rename `425 Unordered Collection` to standard `425 Too Early`
|
||||||
|
|
||||||
4.17.3 / 2022-02-16
|
4.17.3 / 2022-02-16
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
|
@ -2212,7 +2292,7 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
||||||
* deps: connect@2.21.0
|
* deps: connect@2.21.0
|
||||||
- deprecate `connect(middleware)` -- use `app.use(middleware)` instead
|
- deprecate `connect(middleware)` -- use `app.use(middleware)` instead
|
||||||
- deprecate `connect.createServer()` -- use `connect()` instead
|
- deprecate `connect.createServer()` -- use `connect()` instead
|
||||||
- fix `res.setHeader()` patch to work with with get -> append -> set pattern
|
- fix `res.setHeader()` patch to work with get -> append -> set pattern
|
||||||
- deps: compression@~1.0.8
|
- deps: compression@~1.0.8
|
||||||
- deps: errorhandler@~1.1.1
|
- deps: errorhandler@~1.1.1
|
||||||
- deps: express-session@~1.5.0
|
- deps: express-session@~1.5.0
|
||||||
|
|
@ -3423,8 +3503,8 @@ Shaw]
|
||||||
* Added node v0.1.97 compatibility
|
* Added node v0.1.97 compatibility
|
||||||
* Added support for deleting cookies via Request#cookie('key', null)
|
* Added support for deleting cookies via Request#cookie('key', null)
|
||||||
* Updated haml submodule
|
* Updated haml submodule
|
||||||
* Fixed not-found page, now using using charset utf-8
|
* Fixed not-found page, now using charset utf-8
|
||||||
* Fixed show-exceptions page, now using using charset utf-8
|
* Fixed show-exceptions page, now using charset utf-8
|
||||||
* Fixed view support due to fs.readFile Buffers
|
* Fixed view support due to fs.readFile Buffers
|
||||||
* Changed; mime.type() no longer accepts ".type" due to node extname() changes
|
* Changed; mime.type() no longer accepts ".type" due to node extname() changes
|
||||||
|
|
||||||
|
|
@ -3459,7 +3539,7 @@ Shaw]
|
||||||
==================
|
==================
|
||||||
|
|
||||||
* Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s
|
* Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s
|
||||||
encoding is set to 'utf8' or 'utf-8'.
|
encoding is set to 'utf8' or 'utf-8').
|
||||||
* Added "encoding" option to Request#render(). Closes #299
|
* Added "encoding" option to Request#render(). Closes #299
|
||||||
* Added "dump exceptions" setting, which is enabled by default.
|
* Added "dump exceptions" setting, which is enabled by default.
|
||||||
* Added simple ejs template engine support
|
* Added simple ejs template engine support
|
||||||
|
|
@ -3498,7 +3578,7 @@ Shaw]
|
||||||
* Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js
|
* Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js
|
||||||
* Added callback function support to Request#halt() as 3rd/4th arg
|
* Added callback function support to Request#halt() as 3rd/4th arg
|
||||||
* Added preprocessing of route param wildcards using param(). Closes #251
|
* Added preprocessing of route param wildcards using param(). Closes #251
|
||||||
* Added view partial support (with collections etc)
|
* Added view partial support (with collections etc.)
|
||||||
* Fixed bug preventing falsey params (such as ?page=0). Closes #286
|
* Fixed bug preventing falsey params (such as ?page=0). Closes #286
|
||||||
* Fixed setting of multiple cookies. Closes #199
|
* Fixed setting of multiple cookies. Closes #199
|
||||||
* Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)
|
* Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)
|
||||||
|
|
|
||||||
82
Readme.md
82
Readme.md
|
|
@ -1,12 +1,10 @@
|
||||||
[](http://expressjs.com/)
|
[](http://expressjs.com/)
|
||||||
|
|
||||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org).
|
||||||
|
|
||||||
[![NPM Version][npm-image]][npm-url]
|
[![NPM Version][npm-version-image]][npm-url]
|
||||||
[![NPM Downloads][downloads-image]][downloads-url]
|
[![NPM Install Size][npm-install-size-image]][npm-install-size-url]
|
||||||
[![Linux Build][ci-image]][ci-url]
|
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
|
||||||
[![Windows Build][appveyor-image]][appveyor-url]
|
|
||||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
|
|
@ -33,7 +31,7 @@ the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
|
||||||
Installation is done using the
|
Installation is done using the
|
||||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
$ npm install express
|
$ npm install express
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -53,7 +51,7 @@ for more information.
|
||||||
## Docs & Community
|
## Docs & Community
|
||||||
|
|
||||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
|
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
|
||||||
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
|
* [#express](https://web.libera.chat/#express) on [Libera Chat](https://libera.chat) IRC
|
||||||
* [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules
|
* [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||||
* Visit the [Wiki](https://github.com/expressjs/express/wiki)
|
* Visit the [Wiki](https://github.com/expressjs/express/wiki)
|
||||||
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
||||||
|
|
@ -61,35 +59,31 @@ for more information.
|
||||||
|
|
||||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/expressjs/express/wiki/New-features-in-4.x).
|
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/expressjs/express/wiki/New-features-in-4.x).
|
||||||
|
|
||||||
### Security Issues
|
|
||||||
|
|
||||||
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:
|
The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:
|
||||||
|
|
||||||
Install the executable. The executable's major version will match Express's:
|
Install the executable. The executable's major version will match Express's:
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
$ npm install -g express-generator@4
|
$ npm install -g express-generator@4
|
||||||
```
|
```
|
||||||
|
|
||||||
Create the app:
|
Create the app:
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
$ express /tmp/foo && cd /tmp/foo
|
$ express /tmp/foo && cd /tmp/foo
|
||||||
```
|
```
|
||||||
|
|
||||||
Install dependencies:
|
Install dependencies:
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
$ npm install
|
$ npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
Start the server:
|
Start the server:
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
$ npm start
|
$ npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -109,30 +103,42 @@ $ npm start
|
||||||
|
|
||||||
To view the examples, clone the Express repo and install the dependencies:
|
To view the examples, clone the Express repo and install the dependencies:
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
$ git clone git://github.com/expressjs/express.git --depth 1
|
$ git clone https://github.com/expressjs/express.git --depth 1
|
||||||
$ cd express
|
$ cd express
|
||||||
$ npm install
|
$ npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
Then run whichever example you want:
|
Then run whichever example you want:
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
$ node examples/content-negotiation
|
$ node examples/content-negotiation
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
To run the test suite, first install the dependencies, then run `npm test`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ npm install
|
|
||||||
$ npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
[Contributing Guide](Contributing.md)
|
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
|
||||||
|
[![Windows Build][appveyor-image]][appveyor-url]
|
||||||
|
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||||
|
|
||||||
|
The Express.js project welcomes all constructive contributions. Contributions take many forms,
|
||||||
|
from code for bug fixes and enhancements, to additions and fixes to documentation, additional
|
||||||
|
tests, triaging incoming pull requests and issues, and more!
|
||||||
|
|
||||||
|
See the [Contributing Guide](Contributing.md) for more technical details on contributing.
|
||||||
|
|
||||||
|
### Security Issues
|
||||||
|
|
||||||
|
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
To run the test suite, first install the dependencies, then run `npm test`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ npm install
|
||||||
|
$ npm test
|
||||||
|
```
|
||||||
|
|
||||||
## People
|
## People
|
||||||
|
|
||||||
|
|
@ -146,13 +152,15 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d
|
||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
|
||||||
[ci-image]: https://img.shields.io/github/workflow/status/expressjs/express/ci/master.svg?label=linux
|
[appveyor-image]: https://badgen.net/appveyor/ci/dougwilson/express/master?label=windows
|
||||||
[ci-url]: https://github.com/expressjs/express/actions?query=workflow%3Aci
|
|
||||||
[npm-image]: https://img.shields.io/npm/v/express.svg
|
|
||||||
[npm-url]: https://npmjs.org/package/express
|
|
||||||
[downloads-image]: https://img.shields.io/npm/dm/express.svg
|
|
||||||
[downloads-url]: https://npmcharts.com/compare/express?minimal=true
|
|
||||||
[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/express/master.svg?label=windows
|
|
||||||
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/express
|
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/express
|
||||||
[coveralls-image]: https://img.shields.io/coveralls/expressjs/express/master.svg
|
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/express/master
|
||||||
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
|
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
|
||||||
|
[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/express/master?label=linux
|
||||||
|
[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml
|
||||||
|
[npm-downloads-image]: https://badgen.net/npm/dm/express
|
||||||
|
[npm-downloads-url]: https://npmcharts.com/compare/express?minimal=true
|
||||||
|
[npm-install-size-image]: https://badgen.net/packagephobia/install/express
|
||||||
|
[npm-install-size-url]: https://packagephobia.com/result?p=express
|
||||||
|
[npm-url]: https://npmjs.org/package/express
|
||||||
|
[npm-version-image]: https://badgen.net/npm/v/express
|
||||||
|
|
|
||||||
|
|
@ -184,3 +184,9 @@ $ npm publish
|
||||||
|
|
||||||
**NOTE:** The version number to publish will be picked up automatically from
|
**NOTE:** The version number to publish will be picked up automatically from
|
||||||
package.json.
|
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,
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,7 @@ endeavor to keep you informed of the progress towards a fix and full
|
||||||
announcement, and may ask for additional information or guidance.
|
announcement, and may ask for additional information or guidance.
|
||||||
|
|
||||||
Report security bugs in third-party modules to the person or team maintaining
|
Report security bugs in third-party modules to the person or team maintaining
|
||||||
the module. You can also report a vulnerability through the
|
the module.
|
||||||
[Node Security Project](https://nodesecurity.io/report).
|
|
||||||
|
|
||||||
## Disclosure Policy
|
## Disclosure Policy
|
||||||
|
|
||||||
|
|
|
||||||
36
appveyor.yml
36
appveyor.yml
|
|
@ -10,18 +10,29 @@ environment:
|
||||||
- nodejs_version: "11.15"
|
- nodejs_version: "11.15"
|
||||||
- nodejs_version: "12.22"
|
- nodejs_version: "12.22"
|
||||||
- nodejs_version: "13.14"
|
- nodejs_version: "13.14"
|
||||||
- nodejs_version: "14.19"
|
- 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:
|
cache:
|
||||||
- node_modules
|
- node_modules
|
||||||
install:
|
install:
|
||||||
# Install Node.js
|
# Install Node.js
|
||||||
- ps: >-
|
- ps: >-
|
||||||
try { Install-Product node $env:nodejs_version -ErrorAction Stop }
|
try { Install-Product node $env:nodejs_version -ErrorAction Stop }
|
||||||
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) }
|
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64 }
|
||||||
# Configure npm
|
# Configure npm
|
||||||
- ps: |
|
- ps: |
|
||||||
npm config set loglevel error
|
npm config set loglevel error
|
||||||
npm config set shrinkwrap false
|
if ((npm config get package-lock) -eq "true") {
|
||||||
|
npm config set package-lock false
|
||||||
|
} else {
|
||||||
|
npm config set shrinkwrap false
|
||||||
|
}
|
||||||
# Remove all non-test dependencies
|
# Remove all non-test dependencies
|
||||||
- ps: |
|
- ps: |
|
||||||
# Remove example dependencies
|
# Remove example dependencies
|
||||||
|
|
@ -37,7 +48,10 @@ install:
|
||||||
# - use 6.x for Node.js < 8
|
# - use 6.x for Node.js < 8
|
||||||
# - use 7.x for Node.js < 10
|
# - use 7.x for Node.js < 10
|
||||||
# - use 8.x for Node.js < 12
|
# - use 8.x for Node.js < 12
|
||||||
if ([int]$env:nodejs_version.split(".")[0] -lt 6) {
|
# - 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
|
npm install --silent --save-dev mocha@5.2.0
|
||||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
|
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
|
||||||
npm install --silent --save-dev mocha@6.2.2
|
npm install --silent --save-dev mocha@6.2.2
|
||||||
|
|
@ -45,23 +59,29 @@ install:
|
||||||
npm install --silent --save-dev mocha@7.2.0
|
npm install --silent --save-dev mocha@7.2.0
|
||||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 12) {
|
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 12) {
|
||||||
npm install --silent --save-dev mocha@8.4.0
|
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: |
|
- ps: |
|
||||||
# nyc for test coverage
|
# nyc for test coverage
|
||||||
# - use 10.3.2 for Node.js < 4
|
# - use 10.3.2 for Node.js < 4
|
||||||
# - use 11.9.0 for Node.js < 6
|
# - use 11.9.0 for Node.js < 6
|
||||||
# - use 14.1.1 for Node.js < 8
|
# - use 14.1.1 for Node.js < 10
|
||||||
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
|
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
|
||||||
npm install --silent --save-dev nyc@10.3.2
|
npm install --silent --save-dev nyc@10.3.2
|
||||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
|
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
|
||||||
npm install --silent --save-dev nyc@11.9.0
|
npm install --silent --save-dev nyc@11.9.0
|
||||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
|
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
|
||||||
npm install --silent --save-dev nyc@14.1.1
|
npm install --silent --save-dev nyc@14.1.1
|
||||||
}
|
}
|
||||||
- ps: |
|
- ps: |
|
||||||
# supertest for http calls
|
# supertest for http calls
|
||||||
# - use 3.4.2 for Node.js < 6
|
# - use 2.0.0 for Node.js < 4
|
||||||
if ([int]$env:nodejs_version.split(".")[0] -lt 6) {
|
# - use 3.4.2 for Node.js < 7
|
||||||
|
# - use 6.1.6 for Node.js < 8
|
||||||
|
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
|
||||||
|
npm install --silent --save-dev supertest@2.0.0
|
||||||
|
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 7) {
|
||||||
npm install --silent --save-dev supertest@3.4.2
|
npm install --silent --save-dev supertest@3.4.2
|
||||||
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
|
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
|
||||||
npm install --silent --save-dev supertest@6.1.6
|
npm install --silent --save-dev supertest@6.1.6
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@./run 1 middleware
|
@./run 1 middleware 50
|
||||||
@./run 5 middleware
|
@./run 5 middleware 50
|
||||||
@./run 10 middleware
|
@./run 10 middleware 50
|
||||||
@./run 15 middleware
|
@./run 15 middleware 50
|
||||||
@./run 20 middleware
|
@./run 20 middleware 50
|
||||||
@./run 30 middleware
|
@./run 30 middleware 50
|
||||||
@./run 50 middleware
|
@./run 50 middleware 50
|
||||||
@./run 100 middleware
|
@./run 100 middleware 50
|
||||||
|
@./run 10 middleware 100
|
||||||
|
@./run 10 middleware 250
|
||||||
|
@./run 10 middleware 500
|
||||||
|
@./run 10 middleware 1000
|
||||||
@echo
|
@echo
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
|
|
||||||
34
benchmarks/README.md
Normal file
34
benchmarks/README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Express Benchmarks
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
You will need to install [wrk](https://github.com/wg/wrk/blob/master/INSTALL) in order to run the benchmarks.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
To run the benchmarks, first install the dependencies `npm i`, then run `make`
|
||||||
|
|
||||||
|
The output will look something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
50 connections
|
||||||
|
1 middleware
|
||||||
|
7.15ms
|
||||||
|
6784.01
|
||||||
|
|
||||||
|
[...redacted...]
|
||||||
|
|
||||||
|
1000 connections
|
||||||
|
10 middleware
|
||||||
|
139.21ms
|
||||||
|
6155.19
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tip: Include Node.js version in output
|
||||||
|
|
||||||
|
You can use `make && node -v` to include the node.js version in the output.
|
||||||
|
|
||||||
|
### Tip: Save the results to a file
|
||||||
|
|
||||||
|
You can use `make > results.log` to save the results to a file `results.log`.
|
||||||
|
|
@ -13,7 +13,7 @@ while (n--) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use(function(req, res, next){
|
app.use(function(req, res){
|
||||||
res.send('Hello World')
|
res.send('Hello World')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,15 @@ echo
|
||||||
MW=$1 node $2 &
|
MW=$1 node $2 &
|
||||||
pid=$!
|
pid=$!
|
||||||
|
|
||||||
|
echo " $3 connections"
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
wrk 'http://localhost:3333/?foo[bar]=baz' \
|
wrk 'http://localhost:3333/?foo[bar]=baz' \
|
||||||
-d 3 \
|
-d 3 \
|
||||||
-c 50 \
|
-c $3 \
|
||||||
-t 8 \
|
-t 8 \
|
||||||
| grep 'Requests/sec' \
|
| grep 'Requests/sec\|Latency' \
|
||||||
| awk '{ print " " $2 }'
|
| awk '{ print " " $2 }'
|
||||||
|
|
||||||
kill $pid
|
kill $pid
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ This page contains list of examples using Express.
|
||||||
- [hello-world](./hello-world) - Simple request handler
|
- [hello-world](./hello-world) - Simple request handler
|
||||||
- [markdown](./markdown) - Markdown as template engine
|
- [markdown](./markdown) - Markdown as template engine
|
||||||
- [multi-router](./multi-router) - Working with multiple Express routers
|
- [multi-router](./multi-router) - Working with multiple Express routers
|
||||||
- [multipart](./multipart) - Accepting multipart-encoded forms
|
|
||||||
- [mvc](./mvc) - MVC-style controllers
|
- [mvc](./mvc) - MVC-style controllers
|
||||||
- [online](./online) - Tracking online user activity with `online` and `redis` packages
|
- [online](./online) - Tracking online user activity with `online` and `redis` packages
|
||||||
- [params](./params) - Working with route parameters
|
- [params](./params) - Working with route parameters
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
|
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
|
||||||
<form method="post" action="/login">
|
<form method="post" action="/login">
|
||||||
<p>
|
<p>
|
||||||
<label>Username:</label>
|
<label for="username">Username:</label>
|
||||||
<input type="text" name="username">
|
<input type="text" name="username" id="username">
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label>Password:</label>
|
<label for="password">Password:</label>
|
||||||
<input type="text" name="password">
|
<input type="text" name="password" id="password">
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input type="submit" value="Login">
|
<input type="submit" value="Login">
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,10 @@ var app = module.exports = express();
|
||||||
app.use(cookieSession({ secret: 'manny is cool' }));
|
app.use(cookieSession({ secret: 'manny is cool' }));
|
||||||
|
|
||||||
// do something with the session
|
// do something with the session
|
||||||
app.use(count);
|
app.get('/', function (req, res) {
|
||||||
|
|
||||||
// custom middleware
|
|
||||||
function count(req, res) {
|
|
||||||
req.session.count = (req.session.count || 0) + 1
|
req.session.count = (req.session.count || 0) + 1
|
||||||
res.send('viewed ' + req.session.count + ' times\n')
|
res.send('viewed ' + req.session.count + ' times\n')
|
||||||
}
|
})
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (!module.parent) {
|
if (!module.parent) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
var express = require('../../');
|
var express = require('../../');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var resolvePath = require('resolve-path')
|
|
||||||
|
|
||||||
var app = module.exports = express();
|
var app = module.exports = express();
|
||||||
|
|
||||||
|
|
@ -25,9 +24,7 @@ app.get('/', function(req, res){
|
||||||
// /files/* is accessed via req.params[0]
|
// /files/* is accessed via req.params[0]
|
||||||
// but here we name it :file
|
// but here we name it :file
|
||||||
app.get('/files/:file+', function (req, res, next) {
|
app.get('/files/:file+', function (req, res, next) {
|
||||||
var filePath = resolvePath(FILES_DIR, req.params.file)
|
res.download(req.params.file, { root: FILES_DIR }, function (err) {
|
||||||
|
|
||||||
res.download(filePath, function (err) {
|
|
||||||
if (!err) return; // file sent
|
if (!err) return; // file sent
|
||||||
if (err.status !== 404) return next(err); // non-404 error
|
if (err.status !== 404) return next(err); // non-404 error
|
||||||
// file for download not found
|
// file for download not found
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ function error(err, req, res, next) {
|
||||||
res.send('Internal Server Error');
|
res.send('Internal Server Error');
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/', function(req, res){
|
app.get('/', function () {
|
||||||
// Caught and passed down to the errorHandler middleware
|
// Caught and passed down to the errorHandler middleware
|
||||||
throw new Error('something broke!');
|
throw new Error('something broke!');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ app.engine('md', function(path, options, fn){
|
||||||
|
|
||||||
app.set('views', path.join(__dirname, 'views'));
|
app.set('views', path.join(__dirname, 'views'));
|
||||||
|
|
||||||
// make it the default so we dont need .md
|
// make it the default, so we don't need .md
|
||||||
app.set('view engine', 'md');
|
app.set('view engine', 'md');
|
||||||
|
|
||||||
app.get('/', function(req, res){
|
app.get('/', function(req, res){
|
||||||
|
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var express = require('../..');
|
|
||||||
var multiparty = require('multiparty');
|
|
||||||
var format = require('util').format;
|
|
||||||
|
|
||||||
var app = module.exports = express();
|
|
||||||
|
|
||||||
app.get('/', function(req, res){
|
|
||||||
res.send('<form method="post" enctype="multipart/form-data">'
|
|
||||||
+ '<p>Title: <input type="text" name="title" /></p>'
|
|
||||||
+ '<p>Image: <input type="file" name="image" /></p>'
|
|
||||||
+ '<p><input type="submit" value="Upload" /></p>'
|
|
||||||
+ '</form>');
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post('/', function(req, res, next){
|
|
||||||
// create a form to begin parsing
|
|
||||||
var form = new multiparty.Form();
|
|
||||||
var image;
|
|
||||||
var title;
|
|
||||||
|
|
||||||
form.on('error', next);
|
|
||||||
form.on('close', function(){
|
|
||||||
res.send(format('\nuploaded %s (%d Kb) as %s'
|
|
||||||
, image.filename
|
|
||||||
, image.size / 1024 | 0
|
|
||||||
, title));
|
|
||||||
});
|
|
||||||
|
|
||||||
// listen on field event for title
|
|
||||||
form.on('field', function(name, val){
|
|
||||||
if (name !== 'title') return;
|
|
||||||
title = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
// listen on part event for image file
|
|
||||||
form.on('part', function(part){
|
|
||||||
if (!part.filename) return;
|
|
||||||
if (part.name !== 'image') return part.resume();
|
|
||||||
image = {};
|
|
||||||
image.filename = part.filename;
|
|
||||||
image.size = 0;
|
|
||||||
part.on('data', function(buf){
|
|
||||||
image.size += buf.length;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// parse the form
|
|
||||||
form.parse(req);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (!module.parent) {
|
|
||||||
app.listen(4000);
|
|
||||||
console.log('Express started on port 4000');
|
|
||||||
}
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
* Module dependencies.
|
* Module dependencies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var createError = require('http-errors')
|
||||||
var express = require('../../');
|
var express = require('../../');
|
||||||
var app = module.exports = express();
|
var app = module.exports = express();
|
||||||
|
|
||||||
|
|
@ -17,14 +18,6 @@ var users = [
|
||||||
, { name: 'bandit' }
|
, { name: 'bandit' }
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create HTTP error
|
|
||||||
|
|
||||||
function createError(status, message) {
|
|
||||||
var err = new Error(message);
|
|
||||||
err.status = status;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert :to and :from to integers
|
// Convert :to and :from to integers
|
||||||
|
|
||||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||||
|
|
@ -58,7 +51,7 @@ app.get('/', function(req, res){
|
||||||
* GET :user.
|
* GET :user.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
app.get('/user/:user', function(req, res, next){
|
app.get('/user/:user', function (req, res) {
|
||||||
res.send('user ' + req.user.name);
|
res.send('user ' + req.user.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -66,7 +59,7 @@ app.get('/user/:user', function(req, res, next){
|
||||||
* GET users :from - :to.
|
* GET users :from - :to.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
app.get('/users/:from-:to', function(req, res, next){
|
app.get('/users/:from-:to', function (req, res) {
|
||||||
var from = req.params.from;
|
var from = req.params.from;
|
||||||
var to = req.params.to;
|
var to = req.params.to;
|
||||||
var names = users.map(function(user){ return user.name; });
|
var names = users.map(function(user){ return user.name; });
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<h1>Editing <%= user.name %></h1>
|
<h1>Editing <%= user.name %></h1>
|
||||||
|
|
||||||
<div id="user">
|
<div id="user">
|
||||||
<form action="?_method=put", method="post">
|
<form action="?_method=put" method="post">
|
||||||
<p>
|
<p>
|
||||||
Name:
|
Name:
|
||||||
<input type="text" value="<%= user.name %>" name="user[name]" />
|
<input type="text" value="<%= user.name %>" name="user[name]" />
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Search example</title>
|
<title>Search example</title>
|
||||||
<style type="text/css">
|
<style>
|
||||||
body {
|
body {
|
||||||
font: 14px "Helvetica Neue", Helvetica;
|
font: 14px "Helvetica Neue", Helvetica;
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
<h2>Search</h2>
|
<h2>Search</h2>
|
||||||
<p>Try searching for "ferret" or "cat".</p>
|
<p>Try searching for "ferret" or "cat".</p>
|
||||||
<input type="search" name="search" value="" />
|
<input type="search" name="search" value="" />
|
||||||
<pre />
|
<pre></pre>
|
||||||
<script src="/client.js" charset="utf-8"></script>
|
<script src="/client.js" charset="utf-8"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ function users(req, res, next) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/middleware', count, users, function(req, res, next){
|
app.get('/middleware', count, users, function (req, res) {
|
||||||
res.render('index', {
|
res.render('index', {
|
||||||
title: 'Users',
|
title: 'Users',
|
||||||
count: req.count,
|
count: req.count,
|
||||||
|
|
@ -99,7 +99,7 @@ function users2(req, res, next) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/middleware-locals', count2, users2, function(req, res, next){
|
app.get('/middleware-locals', count2, users2, function (req, res) {
|
||||||
// you can see now how we have much less
|
// you can see now how we have much less
|
||||||
// to pass to res.render(). If we have
|
// to pass to res.render(). If we have
|
||||||
// several routes related to users this
|
// several routes related to users this
|
||||||
|
|
|
||||||
|
|
@ -72,12 +72,12 @@ var userRepos = {
|
||||||
// and simply expose the data
|
// and simply expose the data
|
||||||
|
|
||||||
// example: http://localhost:3000/api/users/?api-key=foo
|
// example: http://localhost:3000/api/users/?api-key=foo
|
||||||
app.get('/api/users', function(req, res, next){
|
app.get('/api/users', function (req, res) {
|
||||||
res.send(users);
|
res.send(users);
|
||||||
});
|
});
|
||||||
|
|
||||||
// example: http://localhost:3000/api/repos/?api-key=foo
|
// example: http://localhost:3000/api/repos/?api-key=foo
|
||||||
app.get('/api/repos', function(req, res, next){
|
app.get('/api/repos', function (req, res) {
|
||||||
res.send(repos);
|
res.send(repos);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,13 @@ var merge = require('utils-merge');
|
||||||
var resolve = require('path').resolve;
|
var resolve = require('path').resolve;
|
||||||
var Router = require('router');
|
var Router = require('router');
|
||||||
var setPrototypeOf = require('setprototypeof')
|
var setPrototypeOf = require('setprototypeof')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module variables.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var hasOwnProperty = Object.prototype.hasOwnProperty
|
||||||
var slice = Array.prototype.slice;
|
var slice = Array.prototype.slice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -346,7 +353,17 @@ app.param = function param(name, fn) {
|
||||||
app.set = function set(setting, val) {
|
app.set = function set(setting, val) {
|
||||||
if (arguments.length === 1) {
|
if (arguments.length === 1) {
|
||||||
// app.get(setting)
|
// app.get(setting)
|
||||||
return this.settings[setting];
|
var settings = this.settings
|
||||||
|
|
||||||
|
while (settings && settings !== Object.prototype) {
|
||||||
|
if (hasOwnProperty.call(settings, setting)) {
|
||||||
|
return settings[setting]
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = Object.getPrototypeOf(settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('set "%s" to %o', setting, val);
|
debug('set "%s" to %o', setting, val);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
var Buffer = require('safe-buffer').Buffer
|
var Buffer = require('safe-buffer').Buffer
|
||||||
var contentDisposition = require('content-disposition');
|
var contentDisposition = require('content-disposition');
|
||||||
|
var createError = require('http-errors')
|
||||||
|
var deprecate = require('depd')('express');
|
||||||
var encodeUrl = require('encodeurl');
|
var encodeUrl = require('encodeurl');
|
||||||
var escapeHtml = require('escape-html');
|
var escapeHtml = require('escape-html');
|
||||||
var http = require('http');
|
var http = require('http');
|
||||||
|
|
@ -32,6 +34,7 @@ var send = require('send');
|
||||||
var extname = path.extname;
|
var extname = path.extname;
|
||||||
var resolve = path.resolve;
|
var resolve = path.resolve;
|
||||||
var vary = require('vary');
|
var vary = require('vary');
|
||||||
|
var urlParse = require('url').parse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response prototype.
|
* Response prototype.
|
||||||
|
|
@ -56,6 +59,9 @@ module.exports = res
|
||||||
*/
|
*/
|
||||||
|
|
||||||
res.status = function status(code) {
|
res.status = function status(code) {
|
||||||
|
if ((typeof code === 'string' || Math.floor(code) !== code) && code > 99 && code < 1000) {
|
||||||
|
deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead')
|
||||||
|
}
|
||||||
this.statusCode = code;
|
this.statusCode = code;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
@ -180,6 +186,13 @@ res.send = function send(body) {
|
||||||
chunk = '';
|
chunk = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alter headers for 205
|
||||||
|
if (this.statusCode === 205) {
|
||||||
|
this.set('Content-Length', '0')
|
||||||
|
this.removeHeader('Transfer-Encoding')
|
||||||
|
chunk = ''
|
||||||
|
}
|
||||||
|
|
||||||
if (req.method === 'HEAD') {
|
if (req.method === 'HEAD') {
|
||||||
// skip body for HEAD
|
// skip body for HEAD
|
||||||
this.end();
|
this.end();
|
||||||
|
|
@ -293,7 +306,7 @@ res.jsonp = function jsonp(obj) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
res.sendStatus = function sendStatus(statusCode) {
|
res.sendStatus = function sendStatus(statusCode) {
|
||||||
var body = statuses[statusCode] || String(statusCode)
|
var body = statuses.message[statusCode] || String(statusCode)
|
||||||
|
|
||||||
this.statusCode = statusCode;
|
this.statusCode = statusCode;
|
||||||
this.type('txt');
|
this.type('txt');
|
||||||
|
|
@ -416,6 +429,13 @@ res.download = function download (path, filename, options, callback) {
|
||||||
opts = null
|
opts = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// support optional filename, where options may be in it's place
|
||||||
|
if (typeof filename === 'object' &&
|
||||||
|
(typeof options === 'function' || options === undefined)) {
|
||||||
|
name = null
|
||||||
|
opts = filename
|
||||||
|
}
|
||||||
|
|
||||||
// set Content-Disposition when file is sent
|
// set Content-Disposition when file is sent
|
||||||
var headers = {
|
var headers = {
|
||||||
'Content-Disposition': contentDisposition(name || path)
|
'Content-Disposition': contentDisposition(name || path)
|
||||||
|
|
@ -437,7 +457,9 @@ res.download = function download (path, filename, options, callback) {
|
||||||
opts.headers = headers
|
opts.headers = headers
|
||||||
|
|
||||||
// Resolve the full path for sendFile
|
// Resolve the full path for sendFile
|
||||||
var fullPath = resolve(path);
|
var fullPath = !opts.root
|
||||||
|
? resolve(path)
|
||||||
|
: path
|
||||||
|
|
||||||
// send file
|
// send file
|
||||||
return this.sendFile(fullPath, opts, done)
|
return this.sendFile(fullPath, opts, done)
|
||||||
|
|
@ -532,9 +554,8 @@ res.format = function(obj){
|
||||||
var req = this.req;
|
var req = this.req;
|
||||||
var next = req.next;
|
var next = req.next;
|
||||||
|
|
||||||
var fn = obj.default;
|
var keys = Object.keys(obj)
|
||||||
if (fn) delete obj.default;
|
.filter(function (v) { return v !== 'default' })
|
||||||
var keys = Object.keys(obj);
|
|
||||||
|
|
||||||
var key = keys.length > 0
|
var key = keys.length > 0
|
||||||
? req.accepts(keys)
|
? req.accepts(keys)
|
||||||
|
|
@ -545,13 +566,12 @@ res.format = function(obj){
|
||||||
if (key) {
|
if (key) {
|
||||||
this.set('Content-Type', normalizeType(key).value);
|
this.set('Content-Type', normalizeType(key).value);
|
||||||
obj[key](req, this, next);
|
obj[key](req, this, next);
|
||||||
} else if (fn) {
|
} else if (obj.default) {
|
||||||
fn();
|
obj.default(req, this, next)
|
||||||
} else {
|
} else {
|
||||||
var err = new Error('Not Acceptable');
|
next(createError(406, {
|
||||||
err.status = err.statusCode = 406;
|
types: normalizeTypes(keys).map(function (o) { return o.value })
|
||||||
err.types = normalizeTypes(keys).map(function(o){ return o.value });
|
}))
|
||||||
next(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -717,9 +737,13 @@ res.cookie = function (name, value, options) {
|
||||||
val = 's:' + sign(val, secret);
|
val = 's:' + sign(val, secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('maxAge' in opts) {
|
if (opts.maxAge != null) {
|
||||||
opts.expires = new Date(Date.now() + opts.maxAge);
|
var maxAge = opts.maxAge - 0
|
||||||
opts.maxAge /= 1000;
|
|
||||||
|
if (!isNaN(maxAge)) {
|
||||||
|
opts.expires = new Date(Date.now() + maxAge)
|
||||||
|
opts.maxAge = Math.floor(maxAge / 1000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.path == null) {
|
if (opts.path == null) {
|
||||||
|
|
@ -756,8 +780,25 @@ res.location = function location(url) {
|
||||||
loc = this.req.get('Referrer') || '/';
|
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
|
// set location
|
||||||
return this.set('Location', encodeUrl(loc));
|
return this.set('Location', encodedUrl);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -795,12 +836,12 @@ res.redirect = function redirect(url) {
|
||||||
// Support text/{plain,html} by default
|
// Support text/{plain,html} by default
|
||||||
this.format({
|
this.format({
|
||||||
text: function(){
|
text: function(){
|
||||||
body = statuses[status] + '. Redirecting to ' + address
|
body = statuses.message[status] + '. Redirecting to ' + address
|
||||||
},
|
},
|
||||||
|
|
||||||
html: function(){
|
html: function(){
|
||||||
var u = escapeHtml(address);
|
var u = escapeHtml(address);
|
||||||
body = '<p>' + statuses[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
|
body = '<p>' + statuses.message[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
|
||||||
},
|
},
|
||||||
|
|
||||||
default: function(){
|
default: function(){
|
||||||
|
|
@ -969,7 +1010,7 @@ function sendfile(res, file, options, callback) {
|
||||||
* ability to escape characters that can trigger HTML sniffing.
|
* ability to escape characters that can trigger HTML sniffing.
|
||||||
*
|
*
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @param {function} replaces
|
* @param {function} replacer
|
||||||
* @param {number} spaces
|
* @param {number} spaces
|
||||||
* @param {boolean} escape
|
* @param {boolean} escape
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
|
|
||||||
|
|
@ -77,16 +77,15 @@ exports.normalizeTypes = function(types){
|
||||||
/**
|
/**
|
||||||
* Parse accept params `str` returning an
|
* Parse accept params `str` returning an
|
||||||
* object with `.value`, `.quality` and `.params`.
|
* object with `.value`, `.quality` and `.params`.
|
||||||
* also includes `.originalIndex` for stable sorting
|
|
||||||
*
|
*
|
||||||
* @param {String} str
|
* @param {String} str
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
* @api private
|
* @api private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function acceptParams(str, index) {
|
function acceptParams (str) {
|
||||||
var parts = str.split(/ *; */);
|
var parts = str.split(/ *; */);
|
||||||
var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
|
var ret = { value: parts[0], quality: 1, params: {} }
|
||||||
|
|
||||||
for (var i = 1; i < parts.length; ++i) {
|
for (var i = 1; i < parts.length; ++i) {
|
||||||
var pms = parts[i].split(/ *= */);
|
var pms = parts[i].split(/ *= */);
|
||||||
|
|
@ -240,6 +239,7 @@ function createETagGenerator (options) {
|
||||||
/**
|
/**
|
||||||
* Parse an extended query string with qs.
|
* Parse an extended query string with qs.
|
||||||
*
|
*
|
||||||
|
* @param {String} str
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ function View(name, options) {
|
||||||
|
|
||||||
if (!opts.engines[this.ext]) {
|
if (!opts.engines[this.ext]) {
|
||||||
// load engine
|
// load engine
|
||||||
var mod = this.ext.substr(1)
|
var mod = this.ext.slice(1)
|
||||||
debug('require "%s"', mod)
|
debug('require "%s"', mod)
|
||||||
|
|
||||||
// default engine export
|
// default engine export
|
||||||
|
|
|
||||||
34
package.json
34
package.json
|
|
@ -28,35 +28,36 @@
|
||||||
"api"
|
"api"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.7",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "3.0.0",
|
"array-flatten": "3.0.0",
|
||||||
"body-parser": "2.0.0-beta.1",
|
"body-parser": "2.0.0-beta.2",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "~1.0.4",
|
||||||
"cookie": "0.4.2",
|
"cookie": "0.6.0",
|
||||||
"cookie-signature": "1.0.6",
|
"cookie-signature": "1.0.6",
|
||||||
"debug": "3.1.0",
|
"debug": "3.1.0",
|
||||||
"depd": "~1.1.2",
|
"depd": "2.0.0",
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~1.0.2",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"etag": "~1.8.1",
|
"etag": "~1.8.1",
|
||||||
"finalhandler": "~1.1.2",
|
"finalhandler": "1.2.0",
|
||||||
"fresh": "0.5.2",
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
"merge-descriptors": "1.0.1",
|
"merge-descriptors": "1.0.1",
|
||||||
"methods": "~1.1.2",
|
"methods": "~1.1.2",
|
||||||
"mime-types": "~2.1.34",
|
"mime-types": "~2.1.34",
|
||||||
"on-finished": "~2.3.0",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"path-is-absolute": "1.0.1",
|
"path-is-absolute": "1.0.1",
|
||||||
"proxy-addr": "~2.0.7",
|
"proxy-addr": "~2.0.7",
|
||||||
"qs": "6.9.7",
|
"qs": "6.11.0",
|
||||||
"range-parser": "~1.2.1",
|
"range-parser": "~1.2.1",
|
||||||
"router": "2.0.0-beta.1",
|
"router": "2.0.0-beta.2",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"send": "1.0.0-beta.1",
|
"send": "1.0.0-beta.2",
|
||||||
"serve-static": "2.0.0-beta.1",
|
"serve-static": "2.0.0-beta.2",
|
||||||
"setprototypeof": "1.2.0",
|
"setprototypeof": "1.2.0",
|
||||||
"statuses": "~1.5.0",
|
"statuses": "2.0.1",
|
||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
"utils-merge": "1.0.1",
|
"utils-merge": "1.0.1",
|
||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
|
|
@ -66,20 +67,17 @@
|
||||||
"connect-redis": "3.4.2",
|
"connect-redis": "3.4.2",
|
||||||
"cookie-parser": "1.4.6",
|
"cookie-parser": "1.4.6",
|
||||||
"cookie-session": "2.0.0",
|
"cookie-session": "2.0.0",
|
||||||
"ejs": "3.1.6",
|
"ejs": "3.1.9",
|
||||||
"eslint": "7.32.0",
|
"eslint": "8.47.0",
|
||||||
"express-session": "1.17.2",
|
"express-session": "1.17.2",
|
||||||
"hbs": "4.2.0",
|
"hbs": "4.2.0",
|
||||||
"marked": "0.7.0",
|
"marked": "0.7.0",
|
||||||
"method-override": "3.0.0",
|
"method-override": "3.0.0",
|
||||||
"mocha": "9.2.0",
|
"mocha": "10.2.0",
|
||||||
"morgan": "1.10.0",
|
"morgan": "1.10.0",
|
||||||
"multiparty": "4.2.3",
|
|
||||||
"nyc": "15.1.0",
|
"nyc": "15.1.0",
|
||||||
"pbkdf2-password": "1.2.1",
|
"pbkdf2-password": "1.2.1",
|
||||||
"resolve-path": "1.4.0",
|
"supertest": "6.3.0",
|
||||||
"should": "13.2.3",
|
|
||||||
"supertest": "6.2.2",
|
|
||||||
"vhost": "~3.0.2"
|
"vhost": "~3.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var after = require('after');
|
var after = require('after');
|
||||||
var should = require('should');
|
var assert = require('assert')
|
||||||
var express = require('../')
|
var express = require('../')
|
||||||
, Route = express.Route
|
, Route = express.Route
|
||||||
, methods = require('methods')
|
, methods = require('methods')
|
||||||
|
|
@ -13,6 +13,37 @@ describe('Route', function(){
|
||||||
route.dispatch(req, {}, done)
|
route.dispatch(req, {}, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not stack overflow with a large sync stack', function (done) {
|
||||||
|
this.timeout(5000) // long-running test
|
||||||
|
|
||||||
|
var req = { method: 'GET', url: '/' }
|
||||||
|
var route = new Route('/foo')
|
||||||
|
|
||||||
|
route.get(function (req, res, next) {
|
||||||
|
req.counter = 0
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
for (var i = 0; i < 6000; i++) {
|
||||||
|
route.all(function (req, res, next) {
|
||||||
|
req.counter++
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
route.get(function (req, res, next) {
|
||||||
|
req.called = true
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
route.dispatch(req, {}, function (err) {
|
||||||
|
if (err) return done(err)
|
||||||
|
assert.ok(req.called)
|
||||||
|
assert.strictEqual(req.counter, 6000)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('.all', function(){
|
describe('.all', function(){
|
||||||
it('should add handler', function(done){
|
it('should add handler', function(done){
|
||||||
var req = { method: 'GET', url: '/' };
|
var req = { method: 'GET', url: '/' };
|
||||||
|
|
@ -25,7 +56,7 @@ describe('Route', function(){
|
||||||
|
|
||||||
route.dispatch(req, {}, function (err) {
|
route.dispatch(req, {}, function (err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(req.called).be.ok()
|
assert.ok(req.called)
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -35,7 +66,7 @@ describe('Route', function(){
|
||||||
var route = new Route('/foo');
|
var route = new Route('/foo');
|
||||||
var cb = after(methods.length, function (err) {
|
var cb = after(methods.length, function (err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
count.should.equal(methods.length);
|
assert.strictEqual(count, methods.length)
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -66,7 +97,7 @@ describe('Route', function(){
|
||||||
|
|
||||||
route.dispatch(req, {}, function (err) {
|
route.dispatch(req, {}, function (err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
req.count.should.equal(2);
|
assert.strictEqual(req.count, 2)
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -84,7 +115,7 @@ describe('Route', function(){
|
||||||
|
|
||||||
route.dispatch(req, {}, function (err) {
|
route.dispatch(req, {}, function (err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(req.called).be.ok()
|
assert.ok(req.called)
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -93,7 +124,7 @@ describe('Route', function(){
|
||||||
var req = { method: 'POST', url: '/' };
|
var req = { method: 'POST', url: '/' };
|
||||||
var route = new Route('');
|
var route = new Route('');
|
||||||
|
|
||||||
route.get(function(req, res, next) {
|
route.get(function () {
|
||||||
throw new Error('not me!');
|
throw new Error('not me!');
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -104,7 +135,7 @@ describe('Route', function(){
|
||||||
|
|
||||||
route.dispatch(req, {}, function (err) {
|
route.dispatch(req, {}, function (err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(req.called).be.true()
|
assert.ok(req.called)
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -130,7 +161,7 @@ describe('Route', function(){
|
||||||
|
|
||||||
route.dispatch(req, {}, function (err) {
|
route.dispatch(req, {}, function (err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
req.order.should.equal('abc');
|
assert.strictEqual(req.order, 'abc')
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -156,9 +187,9 @@ describe('Route', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
route.dispatch(req, {}, function (err) {
|
route.dispatch(req, {}, function (err) {
|
||||||
should(err).be.ok()
|
assert.ok(err)
|
||||||
should(err.message).equal('foobar');
|
assert.strictEqual(err.message, 'foobar')
|
||||||
req.order.should.equal('a');
|
assert.strictEqual(req.order, 'a')
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -167,7 +198,7 @@ describe('Route', function(){
|
||||||
var req = { order: '', method: 'GET', url: '/' };
|
var req = { order: '', method: 'GET', url: '/' };
|
||||||
var route = new Route('');
|
var route = new Route('');
|
||||||
|
|
||||||
route.all(function(req, res, next){
|
route.all(function () {
|
||||||
throw new Error('foobar');
|
throw new Error('foobar');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -182,9 +213,9 @@ describe('Route', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
route.dispatch(req, {}, function (err) {
|
route.dispatch(req, {}, function (err) {
|
||||||
should(err).be.ok()
|
assert.ok(err)
|
||||||
should(err.message).equal('foobar');
|
assert.strictEqual(err.message, 'foobar')
|
||||||
req.order.should.equal('a');
|
assert.strictEqual(req.order, 'a')
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -193,7 +224,7 @@ describe('Route', function(){
|
||||||
var req = { method: 'GET', url: '/' };
|
var req = { method: 'GET', url: '/' };
|
||||||
var route = new Route('');
|
var route = new Route('');
|
||||||
|
|
||||||
route.get(function(req, res, next){
|
route.get(function () {
|
||||||
throw new Error('boom!');
|
throw new Error('boom!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -208,7 +239,7 @@ describe('Route', function(){
|
||||||
|
|
||||||
route.dispatch(req, {}, function (err) {
|
route.dispatch(req, {}, function (err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(req.message).equal('oops');
|
assert.strictEqual(req.message, 'oops')
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -222,8 +253,8 @@ describe('Route', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
route.dispatch(req, {}, function(err){
|
route.dispatch(req, {}, function(err){
|
||||||
should(err).be.ok()
|
assert.ok(err)
|
||||||
err.message.should.equal('boom!');
|
assert.strictEqual(err.message, 'boom!')
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -234,7 +265,7 @@ describe('Route', function(){
|
||||||
|
|
||||||
route.all(function(err, req, res, next){
|
route.all(function(err, req, res, next){
|
||||||
// this should not execute
|
// this should not execute
|
||||||
true.should.be.false()
|
throw new Error('should not be called')
|
||||||
});
|
});
|
||||||
|
|
||||||
route.dispatch(req, {}, done);
|
route.dispatch(req, {}, done);
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,36 @@ describe('Router', function(){
|
||||||
router.handle({ method: 'GET' }, {}, done)
|
router.handle({ method: 'GET' }, {}, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('handle missing method', function (done) {
|
||||||
|
var all = false
|
||||||
|
var router = new Router()
|
||||||
|
var route = router.route('/foo')
|
||||||
|
var use = false
|
||||||
|
|
||||||
|
route.post(function (req, res, next) { next(new Error('should not run')) })
|
||||||
|
route.all(function (req, res, next) {
|
||||||
|
all = true
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
route.get(function (req, res, next) { next(new Error('should not run')) })
|
||||||
|
|
||||||
|
router.get('/foo', function (req, res, next) { next(new Error('should not run')) })
|
||||||
|
router.use(function (req, res, next) {
|
||||||
|
use = true
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
router.handle({ url: '/foo' }, {}, function (err) {
|
||||||
|
if (err) return done(err)
|
||||||
|
assert.ok(all)
|
||||||
|
assert.ok(use)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('should not stack overflow with many registered routes', function(done){
|
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();
|
var router = new Router();
|
||||||
|
|
||||||
|
|
@ -76,6 +105,60 @@ describe('Router', function(){
|
||||||
router.handle({ url: '/', method: 'GET' }, { end: done }, function(){});
|
router.handle({ url: '/', method: 'GET' }, { end: done }, function(){});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not stack overflow with a large sync route stack', function (done) {
|
||||||
|
this.timeout(5000) // long-running test
|
||||||
|
|
||||||
|
var router = new Router()
|
||||||
|
|
||||||
|
router.get('/foo', function (req, res, next) {
|
||||||
|
req.counter = 0
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
for (var i = 0; i < 6000; i++) {
|
||||||
|
router.get('/foo', function (req, res, next) {
|
||||||
|
req.counter++
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
router.get('/foo', function (req, res) {
|
||||||
|
assert.strictEqual(req.counter, 6000)
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
router.handle({ url: '/foo', method: 'GET' }, { end: done }, function (err) {
|
||||||
|
assert(!err, err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not stack overflow with a large sync middleware stack', function (done) {
|
||||||
|
this.timeout(5000) // long-running test
|
||||||
|
|
||||||
|
var router = new Router()
|
||||||
|
|
||||||
|
router.use(function (req, res, next) {
|
||||||
|
req.counter = 0
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
for (var i = 0; i < 6000; i++) {
|
||||||
|
router.use(function (req, res, next) {
|
||||||
|
req.counter++
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
router.use(function (req, res) {
|
||||||
|
assert.strictEqual(req.counter, 6000)
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
router.handle({ url: '/', method: 'GET' }, { end: done }, function (err) {
|
||||||
|
assert(!err, err);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('.handle', function(){
|
describe('.handle', function(){
|
||||||
it('should dispatch', function(done){
|
it('should dispatch', function(done){
|
||||||
var router = new Router();
|
var router = new Router();
|
||||||
|
|
@ -149,7 +232,7 @@ describe('Router', function(){
|
||||||
it('should handle throwing inside routes with params', function(done) {
|
it('should handle throwing inside routes with params', function(done) {
|
||||||
var router = new Router();
|
var router = new Router();
|
||||||
|
|
||||||
router.get('/foo/:id', function(req, res, next){
|
router.get('/foo/:id', function () {
|
||||||
throw new Error('foo');
|
throw new Error('foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -519,8 +602,8 @@ describe('Router', function(){
|
||||||
var req2 = { url: '/foo/10/bar', method: 'get' };
|
var req2 = { url: '/foo/10/bar', method: 'get' };
|
||||||
var router = new Router();
|
var router = new Router();
|
||||||
var sub = new Router();
|
var sub = new Router();
|
||||||
|
var cb = after(2, done)
|
||||||
|
|
||||||
done = after(2, done);
|
|
||||||
|
|
||||||
sub.get('/bar', function(req, res, next) {
|
sub.get('/bar', function(req, res, next) {
|
||||||
next();
|
next();
|
||||||
|
|
@ -539,14 +622,14 @@ describe('Router', function(){
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(req1.ms, 50);
|
assert.equal(req1.ms, 50);
|
||||||
assert.equal(req1.originalUrl, '/foo/50/bar');
|
assert.equal(req1.originalUrl, '/foo/50/bar');
|
||||||
done();
|
cb()
|
||||||
});
|
});
|
||||||
|
|
||||||
router.handle(req2, {}, function(err) {
|
router.handle(req2, {}, function(err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(req2.ms, 10);
|
assert.equal(req2.ms, 10);
|
||||||
assert.equal(req2.originalUrl, '/foo/10/bar');
|
assert.equal(req2.originalUrl, '/foo/10/bar');
|
||||||
done();
|
cb()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@ describe('app.listen()', function(){
|
||||||
it('should wrap with an HTTP server', function(done){
|
it('should wrap with an HTTP server', function(done){
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
var server = app.listen(9999, function(){
|
var server = app.listen(0, function () {
|
||||||
server.close();
|
server.close(done)
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,28 +2,24 @@
|
||||||
|
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
var express = require('../')
|
var express = require('../')
|
||||||
var should = require('should')
|
|
||||||
|
|
||||||
describe('app', function(){
|
describe('app', function(){
|
||||||
describe('.locals(obj)', function(){
|
describe('.locals', function () {
|
||||||
it('should merge locals', function(){
|
it('should default object', function () {
|
||||||
var app = express();
|
var app = express()
|
||||||
should(Object.keys(app.locals)).eql(['settings'])
|
assert.ok(app.locals)
|
||||||
app.locals.user = 'tobi';
|
assert.strictEqual(typeof app.locals, 'object')
|
||||||
app.locals.age = 2;
|
|
||||||
should(Object.keys(app.locals)).eql(['settings', 'user', 'age'])
|
|
||||||
assert.strictEqual(app.locals.user, 'tobi')
|
|
||||||
assert.strictEqual(app.locals.age, 2)
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
describe('.locals.settings', function(){
|
describe('.settings', function () {
|
||||||
it('should expose app settings', function(){
|
it('should contain app settings ', function () {
|
||||||
var app = express();
|
var app = express()
|
||||||
app.set('title', 'House of Manny');
|
app.set('title', 'Express')
|
||||||
var obj = app.locals.settings;
|
assert.ok(app.locals.settings)
|
||||||
should(obj).have.property('env', 'test')
|
assert.strictEqual(typeof app.locals.settings, 'object')
|
||||||
should(obj).have.property('title', 'House of Manny')
|
assert.strictEqual(app.locals.settings, app.settings)
|
||||||
|
assert.strictEqual(app.locals.settings.title, 'Express')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ describe('app', function(){
|
||||||
app.get('/:user', function(req, res, next) {
|
app.get('/:user', function(req, res, next) {
|
||||||
next('route');
|
next('route');
|
||||||
});
|
});
|
||||||
app.get('/:user', function(req, res, next) {
|
app.get('/:user', function (req, res) {
|
||||||
res.send(req.params.user);
|
res.send(req.params.user);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -145,11 +145,11 @@ describe('app', function(){
|
||||||
next(new Error('invalid invocation'))
|
next(new Error('invalid invocation'))
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/:user', function(req, res, next) {
|
app.post('/:user', function (req, res) {
|
||||||
res.send(req.params.user);
|
res.send(req.params.user);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/:thing', function(req, res, next) {
|
app.get('/:thing', function (req, res) {
|
||||||
res.send(req.thing);
|
res.send(req.thing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ describe('app.router', function(){
|
||||||
it('should decode correct params', function(done){
|
it('should decode correct params', function(done){
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
app.get('/:name', function(req, res, next){
|
app.get('/:name', function (req, res) {
|
||||||
res.send(req.params.name);
|
res.send(req.params.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ describe('app.router', function(){
|
||||||
it('should not accept params in malformed paths', function(done) {
|
it('should not accept params in malformed paths', function(done) {
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
app.get('/:name', function(req, res, next){
|
app.get('/:name', function (req, res) {
|
||||||
res.send(req.params.name);
|
res.send(req.params.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@ describe('app.router', function(){
|
||||||
it('should not decode spaces', function(done) {
|
it('should not decode spaces', function(done) {
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
app.get('/:name', function(req, res, next){
|
app.get('/:name', function (req, res) {
|
||||||
res.send(req.params.name);
|
res.send(req.params.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -128,7 +128,7 @@ describe('app.router', function(){
|
||||||
it('should work with unicode', function(done) {
|
it('should work with unicode', function(done) {
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
app.get('/:name', function(req, res, next){
|
app.get('/:name', function (req, res) {
|
||||||
res.send(req.params.name);
|
res.send(req.params.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -791,7 +791,7 @@ describe('app.router', function(){
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/foo.json')
|
.get('/foo.json')
|
||||||
.expect(200, 'foo as json', done)
|
.expect(200, 'foo as json', cb)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -805,7 +805,7 @@ describe('app.router', function(){
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/bar', function(req, res){
|
app.get('/bar', function () {
|
||||||
assert(0);
|
assert(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -814,7 +814,7 @@ describe('app.router', function(){
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/foo', function(req, res, next){
|
app.get('/foo', function (req, res) {
|
||||||
calls.push('/foo 2');
|
calls.push('/foo 2');
|
||||||
res.json(calls)
|
res.json(calls)
|
||||||
});
|
});
|
||||||
|
|
@ -834,7 +834,7 @@ describe('app.router', function(){
|
||||||
next('route')
|
next('route')
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/foo', fn, function(req, res, next){
|
app.get('/foo', fn, function (req, res) {
|
||||||
res.end('failure')
|
res.end('failure')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -859,11 +859,11 @@ describe('app.router', function(){
|
||||||
next('router')
|
next('router')
|
||||||
}
|
}
|
||||||
|
|
||||||
router.get('/foo', fn, function (req, res, next) {
|
router.get('/foo', fn, function (req, res) {
|
||||||
res.end('failure')
|
res.end('failure')
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/foo', function (req, res, next) {
|
router.get('/foo', function (req, res) {
|
||||||
res.end('failure')
|
res.end('failure')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -890,7 +890,7 @@ describe('app.router', function(){
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/bar', function(req, res){
|
app.get('/bar', function () {
|
||||||
assert(0);
|
assert(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -899,7 +899,7 @@ describe('app.router', function(){
|
||||||
next(new Error('fail'));
|
next(new Error('fail'));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/foo', function(req, res, next){
|
app.get('/foo', function () {
|
||||||
assert(0);
|
assert(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ describe('app', function(){
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/forum')
|
.get('/forum')
|
||||||
.expect(200, 'forum', done)
|
.expect(200, 'forum', cb)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set the child\'s .parent', function(){
|
it('should set the child\'s .parent', function(){
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ describe('config', function () {
|
||||||
assert.equal(app.get('foo'), 'bar');
|
assert.equal(app.get('foo'), 'bar');
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should set prototype values', function () {
|
||||||
|
var app = express()
|
||||||
|
app.set('hasOwnProperty', 42)
|
||||||
|
assert.strictEqual(app.get('hasOwnProperty'), 42)
|
||||||
|
})
|
||||||
|
|
||||||
it('should return the app', function () {
|
it('should return the app', function () {
|
||||||
var app = express();
|
var app = express();
|
||||||
assert.equal(app.set('foo', 'bar'), app);
|
assert.equal(app.set('foo', 'bar'), app);
|
||||||
|
|
@ -21,6 +27,17 @@ describe('config', function () {
|
||||||
assert.equal(app.set('foo', undefined), app);
|
assert.equal(app.set('foo', undefined), app);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should return set value', function () {
|
||||||
|
var app = express()
|
||||||
|
app.set('foo', 'bar')
|
||||||
|
assert.strictEqual(app.set('foo'), 'bar')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined for prototype values', function () {
|
||||||
|
var app = express()
|
||||||
|
assert.strictEqual(app.set('hasOwnProperty'), undefined)
|
||||||
|
})
|
||||||
|
|
||||||
describe('"etag"', function(){
|
describe('"etag"', function(){
|
||||||
it('should throw on bad value', function(){
|
it('should throw on bad value', function(){
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
@ -51,6 +68,11 @@ describe('config', function () {
|
||||||
assert.strictEqual(app.get('foo'), undefined);
|
assert.strictEqual(app.get('foo'), undefined);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should return undefined for prototype values', function () {
|
||||||
|
var app = express()
|
||||||
|
assert.strictEqual(app.get('hasOwnProperty'), undefined)
|
||||||
|
})
|
||||||
|
|
||||||
it('should otherwise return the value', function(){
|
it('should otherwise return the value', function(){
|
||||||
var app = express();
|
var app = express();
|
||||||
app.set('foo', 'bar');
|
app.set('foo', 'bar');
|
||||||
|
|
@ -125,6 +147,12 @@ describe('config', function () {
|
||||||
assert.equal(app.enable('tobi'), app);
|
assert.equal(app.enable('tobi'), app);
|
||||||
assert.strictEqual(app.get('tobi'), true);
|
assert.strictEqual(app.get('tobi'), true);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should set prototype values', function () {
|
||||||
|
var app = express()
|
||||||
|
app.enable('hasOwnProperty')
|
||||||
|
assert.strictEqual(app.get('hasOwnProperty'), true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.disable()', function(){
|
describe('.disable()', function(){
|
||||||
|
|
@ -133,6 +161,12 @@ describe('config', function () {
|
||||||
assert.equal(app.disable('tobi'), app);
|
assert.equal(app.disable('tobi'), app);
|
||||||
assert.strictEqual(app.get('tobi'), false);
|
assert.strictEqual(app.get('tobi'), false);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should set prototype values', function () {
|
||||||
|
var app = express()
|
||||||
|
app.disable('hasOwnProperty')
|
||||||
|
assert.strictEqual(app.get('hasOwnProperty'), false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.enabled()', function(){
|
describe('.enabled()', function(){
|
||||||
|
|
@ -146,6 +180,11 @@ describe('config', function () {
|
||||||
app.set('foo', 'bar');
|
app.set('foo', 'bar');
|
||||||
assert.strictEqual(app.enabled('foo'), true);
|
assert.strictEqual(app.enabled('foo'), true);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should default to false for prototype values', function () {
|
||||||
|
var app = express()
|
||||||
|
assert.strictEqual(app.enabled('hasOwnProperty'), false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.disabled()', function(){
|
describe('.disabled()', function(){
|
||||||
|
|
@ -159,5 +198,10 @@ describe('config', function () {
|
||||||
app.set('foo', 'bar');
|
app.set('foo', 'bar');
|
||||||
assert.strictEqual(app.disabled('foo'), false);
|
assert.strictEqual(app.disabled('foo'), false);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should default to true for prototype values', function () {
|
||||||
|
var app = express()
|
||||||
|
assert.strictEqual(app.disabled('hasOwnProperty'), true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ var request = require('supertest');
|
||||||
|
|
||||||
describe('exports', function(){
|
describe('exports', function(){
|
||||||
it('should expose Router', function(){
|
it('should expose Router', function(){
|
||||||
express.Router.should.be.a.Function()
|
assert.strictEqual(typeof express.Router, 'function')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should expose json middleware', function () {
|
it('should expose json middleware', function () {
|
||||||
|
|
@ -35,20 +35,23 @@ describe('exports', function(){
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should expose the application prototype', function(){
|
it('should expose the application prototype', function(){
|
||||||
express.application.set.should.be.a.Function()
|
assert.strictEqual(typeof express.application, 'object')
|
||||||
|
assert.strictEqual(typeof express.application.set, 'function')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should expose the request prototype', function(){
|
it('should expose the request prototype', function(){
|
||||||
express.request.accepts.should.be.a.Function()
|
assert.strictEqual(typeof express.request, 'object')
|
||||||
|
assert.strictEqual(typeof express.request.accepts, 'function')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should expose the response prototype', function(){
|
it('should expose the response prototype', function(){
|
||||||
express.response.send.should.be.a.Function()
|
assert.strictEqual(typeof express.response, 'object')
|
||||||
|
assert.strictEqual(typeof express.response.send, 'function')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should permit modifying the .application prototype', function(){
|
it('should permit modifying the .application prototype', function(){
|
||||||
express.application.foo = function(){ return 'bar'; };
|
express.application.foo = function(){ return 'bar'; };
|
||||||
express().foo().should.equal('bar');
|
assert.strictEqual(express().foo(), 'bar')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should permit modifying the .request prototype', function(done){
|
it('should permit modifying the .request prototype', function(done){
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
|
var asyncHooks = tryRequire('async_hooks')
|
||||||
var Buffer = require('safe-buffer').Buffer
|
var Buffer = require('safe-buffer').Buffer
|
||||||
var express = require('..')
|
var express = require('..')
|
||||||
var request = require('supertest')
|
var request = require('supertest')
|
||||||
|
|
||||||
|
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||||
|
? describe
|
||||||
|
: describe.skip
|
||||||
|
|
||||||
describe('express.json()', function () {
|
describe('express.json()', function () {
|
||||||
it('should parse JSON', function (done) {
|
it('should parse JSON', function (done) {
|
||||||
request(createApp())
|
request(createApp())
|
||||||
|
|
@ -38,6 +43,15 @@ describe('express.json()', function () {
|
||||||
.expect(200, '{}', done)
|
.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(' \n'), done)
|
||||||
|
})
|
||||||
|
|
||||||
it('should 400 when invalid content-length', function (done) {
|
it('should 400 when invalid content-length', function (done) {
|
||||||
var app = express()
|
var app = express()
|
||||||
|
|
||||||
|
|
@ -86,7 +100,7 @@ describe('express.json()', function () {
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send('{:')
|
.send('{:')
|
||||||
.expect(400, parseError('{:'), done)
|
.expect(400, '[entity.parse.failed] ' + parseError('{:'), done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should 400 for incomplete', function (done) {
|
it('should 400 for incomplete', function (done) {
|
||||||
|
|
@ -94,16 +108,7 @@ describe('express.json()', function () {
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send('{"user"')
|
.send('{"user"')
|
||||||
.expect(400, parseError('{"user"'), done)
|
.expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done)
|
||||||
})
|
|
||||||
|
|
||||||
it('should error with type = "entity.parse.failed"', function (done) {
|
|
||||||
request(this.app)
|
|
||||||
.post('/')
|
|
||||||
.set('Content-Type', 'application/json')
|
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send(' {"user"')
|
|
||||||
.expect(400, 'entity.parse.failed', done)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should include original body on error object', function (done) {
|
it('should include original body on error object', function (done) {
|
||||||
|
|
@ -124,24 +129,13 @@ describe('express.json()', function () {
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.set('Content-Length', '1034')
|
.set('Content-Length', '1034')
|
||||||
.send(JSON.stringify({ str: buf.toString() }))
|
.send(JSON.stringify({ str: buf.toString() }))
|
||||||
.expect(413, done)
|
.expect(413, '[entity.too.large] request entity too large', done)
|
||||||
})
|
|
||||||
|
|
||||||
it('should error with type = "entity.too.large"', function (done) {
|
|
||||||
var buf = Buffer.alloc(1024, '.')
|
|
||||||
request(createApp({ limit: '1kb' }))
|
|
||||||
.post('/')
|
|
||||||
.set('Content-Type', 'application/json')
|
|
||||||
.set('Content-Length', '1034')
|
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send(JSON.stringify({ str: buf.toString() }))
|
|
||||||
.expect(413, 'entity.too.large', done)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should 413 when over limit with chunked encoding', function (done) {
|
it('should 413 when over limit with chunked encoding', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
var buf = Buffer.alloc(1024, '.')
|
var buf = Buffer.alloc(1024, '.')
|
||||||
var server = createApp({ limit: '1kb' })
|
var test = request(app).post('/')
|
||||||
var test = request(server).post('/')
|
|
||||||
test.set('Content-Type', 'application/json')
|
test.set('Content-Type', 'application/json')
|
||||||
test.set('Transfer-Encoding', 'chunked')
|
test.set('Transfer-Encoding', 'chunked')
|
||||||
test.write('{"str":')
|
test.write('{"str":')
|
||||||
|
|
@ -149,6 +143,15 @@ describe('express.json()', function () {
|
||||||
test.expect(413, done)
|
test.expect(413, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should 413 when inflated body over limit', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var test = request(app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/json')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex'))
|
||||||
|
test.expect(413, done)
|
||||||
|
})
|
||||||
|
|
||||||
it('should accept number of bytes', function (done) {
|
it('should accept number of bytes', function (done) {
|
||||||
var buf = Buffer.alloc(1024, '.')
|
var buf = Buffer.alloc(1024, '.')
|
||||||
request(createApp({ limit: 1024 }))
|
request(createApp({ limit: 1024 }))
|
||||||
|
|
@ -161,11 +164,11 @@ describe('express.json()', function () {
|
||||||
it('should not change when options altered', function (done) {
|
it('should not change when options altered', function (done) {
|
||||||
var buf = Buffer.alloc(1024, '.')
|
var buf = Buffer.alloc(1024, '.')
|
||||||
var options = { limit: '1kb' }
|
var options = { limit: '1kb' }
|
||||||
var server = createApp(options)
|
var app = createApp(options)
|
||||||
|
|
||||||
options.limit = '100kb'
|
options.limit = '100kb'
|
||||||
|
|
||||||
request(server)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send(JSON.stringify({ str: buf.toString() }))
|
.send(JSON.stringify({ str: buf.toString() }))
|
||||||
|
|
@ -174,14 +177,23 @@ describe('express.json()', function () {
|
||||||
|
|
||||||
it('should not hang response', function (done) {
|
it('should not hang response', function (done) {
|
||||||
var buf = Buffer.alloc(10240, '.')
|
var buf = Buffer.alloc(10240, '.')
|
||||||
var server = createApp({ limit: '8kb' })
|
var app = createApp({ limit: '8kb' })
|
||||||
var test = request(server).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/json')
|
test.set('Content-Type', 'application/json')
|
||||||
test.write(buf)
|
test.write(buf)
|
||||||
test.write(buf)
|
test.write(buf)
|
||||||
test.write(buf)
|
test.write(buf)
|
||||||
test.expect(413, done)
|
test.expect(413, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not error when inflating', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var test = request(app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/json')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex'))
|
||||||
|
test.expect(413, done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with inflate option', function () {
|
describe('with inflate option', function () {
|
||||||
|
|
@ -195,7 +207,7 @@ describe('express.json()', function () {
|
||||||
test.set('Content-Encoding', 'gzip')
|
test.set('Content-Encoding', 'gzip')
|
||||||
test.set('Content-Type', 'application/json')
|
test.set('Content-Type', 'application/json')
|
||||||
test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
|
test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
|
||||||
test.expect(415, 'content encoding unsupported', done)
|
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -225,7 +237,7 @@ describe('express.json()', function () {
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send('true')
|
.send('true')
|
||||||
.expect(400, parseError('#rue').replace('#', 't'), done)
|
.expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -253,7 +265,7 @@ describe('express.json()', function () {
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send('true')
|
.send('true')
|
||||||
.expect(400, parseError('#rue').replace('#', 't'), done)
|
.expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not parse primitives with leading whitespaces', function (done) {
|
it('should not parse primitives with leading whitespaces', function (done) {
|
||||||
|
|
@ -261,7 +273,7 @@ describe('express.json()', function () {
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send(' true')
|
.send(' true')
|
||||||
.expect(400, parseError(' #rue').replace('#', 't'), done)
|
.expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow leading whitespaces in JSON', function (done) {
|
it('should allow leading whitespaces in JSON', function (done) {
|
||||||
|
|
@ -272,15 +284,6 @@ describe('express.json()', function () {
|
||||||
.expect(200, '{"user":"tobi"}', done)
|
.expect(200, '{"user":"tobi"}', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should error with type = "entity.parse.failed"', function (done) {
|
|
||||||
request(this.app)
|
|
||||||
.post('/')
|
|
||||||
.set('Content-Type', 'application/json')
|
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send('true')
|
|
||||||
.expect(400, 'entity.parse.failed', done)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should include correct message in stack trace', function (done) {
|
it('should include correct message in stack trace', function (done) {
|
||||||
request(this.app)
|
request(this.app)
|
||||||
.post('/')
|
.post('/')
|
||||||
|
|
@ -288,7 +291,7 @@ describe('express.json()', function () {
|
||||||
.set('X-Error-Property', 'stack')
|
.set('X-Error-Property', 'stack')
|
||||||
.send('true')
|
.send('true')
|
||||||
.expect(400)
|
.expect(400)
|
||||||
.expect(shouldContainInBody(parseError('#rue').replace('#', 't')))
|
.expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't')))
|
||||||
.end(done)
|
.end(done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -397,65 +400,59 @@ describe('express.json()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should error from verify', function (done) {
|
it('should error from verify', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send('["tobi"]')
|
.send('["tobi"]')
|
||||||
.expect(403, 'no arrays', done)
|
.expect(403, '[entity.verify.failed] no arrays', done)
|
||||||
})
|
|
||||||
|
|
||||||
it('should error with type = "entity.verify.failed"', function (done) {
|
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
|
||||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
|
||||||
} })
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.post('/')
|
|
||||||
.set('Content-Type', 'application/json')
|
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send('["tobi"]')
|
|
||||||
.expect(403, 'entity.verify.failed', done)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow custom codes', function (done) {
|
it('should allow custom codes', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] !== 0x5b) return
|
verify: function (req, res, buf) {
|
||||||
var err = new Error('no arrays')
|
if (buf[0] !== 0x5b) return
|
||||||
err.status = 400
|
var err = new Error('no arrays')
|
||||||
throw err
|
err.status = 400
|
||||||
} })
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send('["tobi"]')
|
.send('["tobi"]')
|
||||||
.expect(400, 'no arrays', done)
|
.expect(400, '[entity.verify.failed] no arrays', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow custom type', function (done) {
|
it('should allow custom type', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] !== 0x5b) return
|
verify: function (req, res, buf) {
|
||||||
var err = new Error('no arrays')
|
if (buf[0] !== 0x5b) return
|
||||||
err.type = 'foo.bar'
|
var err = new Error('no arrays')
|
||||||
throw err
|
err.type = 'foo.bar'
|
||||||
} })
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send('["tobi"]')
|
.send('["tobi"]')
|
||||||
.expect(403, 'foo.bar', done)
|
.expect(403, '[foo.bar] no arrays', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should include original body on error object', function (done) {
|
it('should include original body on error object', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
|
|
@ -466,9 +463,11 @@ describe('express.json()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow pass-through', function (done) {
|
it('should allow pass-through', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
|
|
@ -478,9 +477,11 @@ describe('express.json()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work with different charsets', function (done) {
|
it('should work with different charsets', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/json; charset=utf-16')
|
test.set('Content-Type', 'application/json; charset=utf-16')
|
||||||
|
|
@ -489,14 +490,120 @@ describe('express.json()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should 415 on unknown charset prior to verify', function (done) {
|
it('should 415 on unknown charset prior to verify', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
throw new Error('unexpected verify call')
|
verify: function (req, res, buf) {
|
||||||
} })
|
throw new Error('unexpected verify call')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/json; charset=x-bogus')
|
test.set('Content-Type', 'application/json; charset=x-bogus')
|
||||||
test.write(Buffer.from('00000000', 'hex'))
|
test.write(Buffer.from('00000000', 'hex'))
|
||||||
test.expect(415, 'unsupported charset "X-BOGUS"', done)
|
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describeAsyncHooks('async local storage', function () {
|
||||||
|
before(function () {
|
||||||
|
var app = express()
|
||||||
|
var store = { foo: 'bar' }
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||||
|
req.asyncLocalStorage.run(store, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(express.json())
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(err.status || 500)
|
||||||
|
res.send('[' + err.type + '] ' + err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/', function (req, res) {
|
||||||
|
res.json(req.body)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.app = app
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.send('{"user":"tobi"}')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.expect('{"user":"tobi"}')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should 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) {
|
||||||
|
var test = request(this.app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/json')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
|
||||||
|
test.expect(200)
|
||||||
|
test.expect('x-store-foo', 'bar')
|
||||||
|
test.expect('{"name":"论"}')
|
||||||
|
test.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when inflate error', function (done) {
|
||||||
|
var test = request(this.app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/json')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
|
||||||
|
test.expect(400)
|
||||||
|
test.expect('x-store-foo', 'bar')
|
||||||
|
test.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when parse error', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.send('{"user":')
|
||||||
|
.expect(400)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when limit exceeded', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}')
|
||||||
|
.expect(413)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.end(done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -538,15 +645,7 @@ describe('express.json()', function () {
|
||||||
var test = request(this.app).post('/')
|
var test = request(this.app).post('/')
|
||||||
test.set('Content-Type', 'application/json; charset=koi8-r')
|
test.set('Content-Type', 'application/json; charset=koi8-r')
|
||||||
test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
|
test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
|
||||||
test.expect(415, 'unsupported charset "KOI8-R"', done)
|
test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
|
||||||
})
|
|
||||||
|
|
||||||
it('should error with type = "charset.unsupported"', function (done) {
|
|
||||||
var test = request(this.app).post('/')
|
|
||||||
test.set('Content-Type', 'application/json; charset=koi8-r')
|
|
||||||
test.set('X-Error-Property', 'type')
|
|
||||||
test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
|
|
||||||
test.expect(415, 'charset.unsupported', done)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -599,16 +698,7 @@ describe('express.json()', function () {
|
||||||
test.set('Content-Encoding', 'nulls')
|
test.set('Content-Encoding', 'nulls')
|
||||||
test.set('Content-Type', 'application/json')
|
test.set('Content-Type', 'application/json')
|
||||||
test.write(Buffer.from('000000000000', 'hex'))
|
test.write(Buffer.from('000000000000', 'hex'))
|
||||||
test.expect(415, 'unsupported content encoding "nulls"', done)
|
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
|
||||||
})
|
|
||||||
|
|
||||||
it('should error with type = "encoding.unsupported"', function (done) {
|
|
||||||
var test = request(this.app).post('/')
|
|
||||||
test.set('Content-Encoding', 'nulls')
|
|
||||||
test.set('Content-Type', 'application/json')
|
|
||||||
test.set('X-Error-Property', 'type')
|
|
||||||
test.write(Buffer.from('000000000000', 'hex'))
|
|
||||||
test.expect(415, 'encoding.unsupported', done)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should 400 on malformed encoding', function (done) {
|
it('should 400 on malformed encoding', function (done) {
|
||||||
|
|
@ -638,8 +728,11 @@ function createApp (options) {
|
||||||
app.use(express.json(options))
|
app.use(express.json(options))
|
||||||
|
|
||||||
app.use(function (err, req, res, next) {
|
app.use(function (err, req, res, next) {
|
||||||
|
// console.log(err)
|
||||||
res.status(err.status || 500)
|
res.status(err.status || 500)
|
||||||
res.send(String(err[req.headers['x-error-property'] || 'message']))
|
res.send(String(req.headers['x-error-property']
|
||||||
|
? err[req.headers['x-error-property']]
|
||||||
|
: ('[' + err.type + '] ' + err.message)))
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post('/', function (req, res) {
|
app.post('/', function (req, res) {
|
||||||
|
|
@ -663,3 +756,11 @@ function shouldContainInBody (str) {
|
||||||
'expected \'' + res.text + '\' to contain \'' + str + '\'')
|
'expected \'' + res.text + '\' to contain \'' + str + '\'')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryRequire (name) {
|
||||||
|
try {
|
||||||
|
return require(name)
|
||||||
|
} catch (e) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
|
var asyncHooks = tryRequire('async_hooks')
|
||||||
var Buffer = require('safe-buffer').Buffer
|
var Buffer = require('safe-buffer').Buffer
|
||||||
var express = require('..')
|
var express = require('..')
|
||||||
var request = require('supertest')
|
var request = require('supertest')
|
||||||
|
|
||||||
|
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||||
|
? describe
|
||||||
|
: describe.skip
|
||||||
|
|
||||||
describe('express.raw()', function () {
|
describe('express.raw()', function () {
|
||||||
before(function () {
|
before(function () {
|
||||||
this.app = createApp()
|
this.app = createApp()
|
||||||
|
|
@ -102,6 +107,15 @@ describe('express.raw()', function () {
|
||||||
test.expect(413, done)
|
test.expect(413, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should 413 when inflated body over limit', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var test = request(app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))
|
||||||
|
test.expect(413, done)
|
||||||
|
})
|
||||||
|
|
||||||
it('should accept number of bytes', function (done) {
|
it('should accept number of bytes', function (done) {
|
||||||
var buf = Buffer.alloc(1028, '.')
|
var buf = Buffer.alloc(1028, '.')
|
||||||
var app = createApp({ limit: 1024 })
|
var app = createApp({ limit: 1024 })
|
||||||
|
|
@ -134,6 +148,15 @@ describe('express.raw()', function () {
|
||||||
test.write(buf)
|
test.write(buf)
|
||||||
test.expect(413, done)
|
test.expect(413, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not error when inflating', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var test = request(app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex'))
|
||||||
|
test.expect(413, done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with inflate option', function () {
|
describe('with inflate option', function () {
|
||||||
|
|
@ -147,7 +170,7 @@ describe('express.raw()', function () {
|
||||||
test.set('Content-Encoding', 'gzip')
|
test.set('Content-Encoding', 'gzip')
|
||||||
test.set('Content-Type', 'application/octet-stream')
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
||||||
test.expect(415, 'content encoding unsupported', done)
|
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -263,34 +286,40 @@ describe('express.raw()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should error from verify', function (done) {
|
it('should error from verify', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x00) throw new Error('no leading null')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x00) throw new Error('no leading null')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/octet-stream')
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
test.write(Buffer.from('000102', 'hex'))
|
test.write(Buffer.from('000102', 'hex'))
|
||||||
test.expect(403, 'no leading null', done)
|
test.expect(403, '[entity.verify.failed] no leading null', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow custom codes', function (done) {
|
it('should allow custom codes', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] !== 0x00) return
|
verify: function (req, res, buf) {
|
||||||
var err = new Error('no leading null')
|
if (buf[0] !== 0x00) return
|
||||||
err.status = 400
|
var err = new Error('no leading null')
|
||||||
throw err
|
err.status = 400
|
||||||
} })
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/octet-stream')
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
test.write(Buffer.from('000102', 'hex'))
|
test.write(Buffer.from('000102', 'hex'))
|
||||||
test.expect(400, 'no leading null', done)
|
test.expect(400, '[entity.verify.failed] no leading null', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow pass-through', function (done) {
|
it('should allow pass-through', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x00) throw new Error('no leading null')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x00) throw new Error('no leading null')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/octet-stream')
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
|
|
@ -299,6 +328,103 @@ describe('express.raw()', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describeAsyncHooks('async local storage', function () {
|
||||||
|
before(function () {
|
||||||
|
var app = express()
|
||||||
|
var store = { foo: 'bar' }
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||||
|
req.asyncLocalStorage.run(store, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(express.raw())
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(err.status || 500)
|
||||||
|
res.send('[' + err.type + '] ' + err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/', function (req, res) {
|
||||||
|
if (Buffer.isBuffer(req.body)) {
|
||||||
|
res.json({ buf: req.body.toString('hex') })
|
||||||
|
} else {
|
||||||
|
res.json(req.body)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.app = app
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/octet-stream')
|
||||||
|
.send('the user is tobi')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.expect({ buf: '746865207573657220697320746f6269' })
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when unmatched content-type', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/fizzbuzz')
|
||||||
|
.send('buzz')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when inflated', function (done) {
|
||||||
|
var test = request(this.app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
||||||
|
test.expect(200)
|
||||||
|
test.expect('x-store-foo', 'bar')
|
||||||
|
test.expect({ buf: '6e616d653de8aeba' })
|
||||||
|
test.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when inflate error', function (done) {
|
||||||
|
var test = request(this.app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))
|
||||||
|
test.expect(400)
|
||||||
|
test.expect('x-store-foo', 'bar')
|
||||||
|
test.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when limit exceeded', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/octet-stream')
|
||||||
|
.send('the user is ' + Buffer.alloc(1024 * 100, '.').toString())
|
||||||
|
.expect(413)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('charset', function () {
|
describe('charset', function () {
|
||||||
before(function () {
|
before(function () {
|
||||||
this.app = createApp()
|
this.app = createApp()
|
||||||
|
|
@ -356,12 +482,12 @@ describe('express.raw()', function () {
|
||||||
test.expect(200, { buf: '6e616d653de8aeba' }, done)
|
test.expect(200, { buf: '6e616d653de8aeba' }, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fail on unknown encoding', function (done) {
|
it('should 415 on unknown encoding', function (done) {
|
||||||
var test = request(this.app).post('/')
|
var test = request(this.app).post('/')
|
||||||
test.set('Content-Encoding', 'nulls')
|
test.set('Content-Encoding', 'nulls')
|
||||||
test.set('Content-Type', 'application/octet-stream')
|
test.set('Content-Type', 'application/octet-stream')
|
||||||
test.write(Buffer.from('000000000000', 'hex'))
|
test.write(Buffer.from('000000000000', 'hex'))
|
||||||
test.expect(415, 'unsupported content encoding "nulls"', done)
|
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -373,7 +499,9 @@ function createApp (options) {
|
||||||
|
|
||||||
app.use(function (err, req, res, next) {
|
app.use(function (err, req, res, next) {
|
||||||
res.status(err.status || 500)
|
res.status(err.status || 500)
|
||||||
res.send(String(err[req.headers['x-error-property'] || 'message']))
|
res.send(String(req.headers['x-error-property']
|
||||||
|
? err[req.headers['x-error-property']]
|
||||||
|
: ('[' + err.type + '] ' + err.message)))
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post('/', function (req, res) {
|
app.post('/', function (req, res) {
|
||||||
|
|
@ -386,3 +514,11 @@ function createApp (options) {
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryRequire (name) {
|
||||||
|
try {
|
||||||
|
return require(name)
|
||||||
|
} catch (e) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
|
var asyncHooks = tryRequire('async_hooks')
|
||||||
var Buffer = require('safe-buffer').Buffer
|
var Buffer = require('safe-buffer').Buffer
|
||||||
var express = require('..')
|
var express = require('..')
|
||||||
var request = require('supertest')
|
var request = require('supertest')
|
||||||
|
|
||||||
|
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||||
|
? describe
|
||||||
|
: describe.skip
|
||||||
|
|
||||||
describe('express.text()', function () {
|
describe('express.text()', function () {
|
||||||
before(function () {
|
before(function () {
|
||||||
this.app = createApp()
|
this.app = createApp()
|
||||||
|
|
@ -75,16 +80,16 @@ describe('express.text()', function () {
|
||||||
|
|
||||||
describe('with defaultCharset option', function () {
|
describe('with defaultCharset option', function () {
|
||||||
it('should change default charset', function (done) {
|
it('should change default charset', function (done) {
|
||||||
var app = createApp({ defaultCharset: 'koi8-r' })
|
var server = createApp({ defaultCharset: 'koi8-r' })
|
||||||
var test = request(app).post('/')
|
var test = request(server).post('/')
|
||||||
test.set('Content-Type', 'text/plain')
|
test.set('Content-Type', 'text/plain')
|
||||||
test.write(Buffer.from('6e616d6520697320cec5d4', 'hex'))
|
test.write(Buffer.from('6e616d6520697320cec5d4', 'hex'))
|
||||||
test.expect(200, '"name is нет"', done)
|
test.expect(200, '"name is нет"', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should honor content-type charset', function (done) {
|
it('should honor content-type charset', function (done) {
|
||||||
var app = createApp({ defaultCharset: 'koi8-r' })
|
var server = createApp({ defaultCharset: 'koi8-r' })
|
||||||
var test = request(app).post('/')
|
var test = request(server).post('/')
|
||||||
test.set('Content-Type', 'text/plain; charset=utf-8')
|
test.set('Content-Type', 'text/plain; charset=utf-8')
|
||||||
test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))
|
test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))
|
||||||
test.expect(200, '"name is 论"', done)
|
test.expect(200, '"name is 论"', done)
|
||||||
|
|
@ -103,8 +108,8 @@ describe('express.text()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should 413 when over limit with chunked encoding', function (done) {
|
it('should 413 when over limit with chunked encoding', function (done) {
|
||||||
var buf = Buffer.alloc(1028, '.')
|
|
||||||
var app = createApp({ limit: '1kb' })
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var buf = Buffer.alloc(1028, '.')
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'text/plain')
|
test.set('Content-Type', 'text/plain')
|
||||||
test.set('Transfer-Encoding', 'chunked')
|
test.set('Transfer-Encoding', 'chunked')
|
||||||
|
|
@ -112,6 +117,15 @@ describe('express.text()', function () {
|
||||||
test.expect(413, done)
|
test.expect(413, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should 413 when inflated body over limit', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var test = request(app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'text/plain')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))
|
||||||
|
test.expect(413, done)
|
||||||
|
})
|
||||||
|
|
||||||
it('should accept number of bytes', function (done) {
|
it('should accept number of bytes', function (done) {
|
||||||
var buf = Buffer.alloc(1028, '.')
|
var buf = Buffer.alloc(1028, '.')
|
||||||
request(createApp({ limit: 1024 }))
|
request(createApp({ limit: 1024 }))
|
||||||
|
|
@ -136,8 +150,8 @@ describe('express.text()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not hang response', function (done) {
|
it('should not hang response', function (done) {
|
||||||
var buf = Buffer.alloc(10240, '.')
|
|
||||||
var app = createApp({ limit: '8kb' })
|
var app = createApp({ limit: '8kb' })
|
||||||
|
var buf = Buffer.alloc(10240, '.')
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'text/plain')
|
test.set('Content-Type', 'text/plain')
|
||||||
test.write(buf)
|
test.write(buf)
|
||||||
|
|
@ -145,6 +159,17 @@ describe('express.text()', function () {
|
||||||
test.write(buf)
|
test.write(buf)
|
||||||
test.expect(413, done)
|
test.expect(413, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not error when inflating', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var test = request(app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'text/plain')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a1470404', 'hex'))
|
||||||
|
setTimeout(function () {
|
||||||
|
test.expect(413, done)
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with inflate option', function () {
|
describe('with inflate option', function () {
|
||||||
|
|
@ -158,7 +183,7 @@ describe('express.text()', function () {
|
||||||
test.set('Content-Encoding', 'gzip')
|
test.set('Content-Encoding', 'gzip')
|
||||||
test.set('Content-Type', 'text/plain')
|
test.set('Content-Type', 'text/plain')
|
||||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))
|
||||||
test.expect(415, 'content encoding unsupported', done)
|
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -278,36 +303,42 @@ describe('express.text()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should error from verify', function (done) {
|
it('should error from verify', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'text/plain')
|
.set('Content-Type', 'text/plain')
|
||||||
.send(' user is tobi')
|
.send(' user is tobi')
|
||||||
.expect(403, 'no leading space', done)
|
.expect(403, '[entity.verify.failed] no leading space', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow custom codes', function (done) {
|
it('should allow custom codes', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] !== 0x20) return
|
verify: function (req, res, buf) {
|
||||||
var err = new Error('no leading space')
|
if (buf[0] !== 0x20) return
|
||||||
err.status = 400
|
var err = new Error('no leading space')
|
||||||
throw err
|
err.status = 400
|
||||||
} })
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'text/plain')
|
.set('Content-Type', 'text/plain')
|
||||||
.send(' user is tobi')
|
.send(' user is tobi')
|
||||||
.expect(400, 'no leading space', done)
|
.expect(400, '[entity.verify.failed] no leading space', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow pass-through', function (done) {
|
it('should allow pass-through', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
|
|
@ -317,14 +348,109 @@ describe('express.text()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should 415 on unknown charset prior to verify', function (done) {
|
it('should 415 on unknown charset prior to verify', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
throw new Error('unexpected verify call')
|
verify: function (req, res, buf) {
|
||||||
} })
|
throw new Error('unexpected verify call')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'text/plain; charset=x-bogus')
|
test.set('Content-Type', 'text/plain; charset=x-bogus')
|
||||||
test.write(Buffer.from('00000000', 'hex'))
|
test.write(Buffer.from('00000000', 'hex'))
|
||||||
test.expect(415, 'unsupported charset "X-BOGUS"', done)
|
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describeAsyncHooks('async local storage', function () {
|
||||||
|
before(function () {
|
||||||
|
var app = express()
|
||||||
|
var store = { foo: 'bar' }
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||||
|
req.asyncLocalStorage.run(store, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(express.text())
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(err.status || 500)
|
||||||
|
res.send('[' + err.type + '] ' + err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/', function (req, res) {
|
||||||
|
res.json(req.body)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.app = app
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'text/plain')
|
||||||
|
.send('user is tobi')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.expect('"user is tobi"')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when unmatched content-type', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/fizzbuzz')
|
||||||
|
.send('buzz')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when inflated', function (done) {
|
||||||
|
var test = request(this.app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'text/plain')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex'))
|
||||||
|
test.expect(200)
|
||||||
|
test.expect('x-store-foo', 'bar')
|
||||||
|
test.expect('"name is 论"')
|
||||||
|
test.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when inflate error', function (done) {
|
||||||
|
var test = request(this.app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'text/plain')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000', 'hex'))
|
||||||
|
test.expect(400)
|
||||||
|
test.expect('x-store-foo', 'bar')
|
||||||
|
test.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when limit exceeded', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'text/plain')
|
||||||
|
.send('user is ' + Buffer.alloc(1024 * 100, '.').toString())
|
||||||
|
.expect(413)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.end(done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -366,7 +492,7 @@ describe('express.text()', function () {
|
||||||
var test = request(this.app).post('/')
|
var test = request(this.app).post('/')
|
||||||
test.set('Content-Type', 'text/plain; charset=x-bogus')
|
test.set('Content-Type', 'text/plain; charset=x-bogus')
|
||||||
test.write(Buffer.from('00000000', 'hex'))
|
test.write(Buffer.from('00000000', 'hex'))
|
||||||
test.expect(415, 'unsupported charset "X-BOGUS"', done)
|
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -414,12 +540,12 @@ describe('express.text()', function () {
|
||||||
test.expect(200, '"name is 论"', done)
|
test.expect(200, '"name is 论"', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fail on unknown encoding', function (done) {
|
it('should 415 on unknown encoding', function (done) {
|
||||||
var test = request(this.app).post('/')
|
var test = request(this.app).post('/')
|
||||||
test.set('Content-Encoding', 'nulls')
|
test.set('Content-Encoding', 'nulls')
|
||||||
test.set('Content-Type', 'text/plain')
|
test.set('Content-Type', 'text/plain')
|
||||||
test.write(Buffer.from('000000000000', 'hex'))
|
test.write(Buffer.from('000000000000', 'hex'))
|
||||||
test.expect(415, 'unsupported content encoding "nulls"', done)
|
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -431,7 +557,9 @@ function createApp (options) {
|
||||||
|
|
||||||
app.use(function (err, req, res, next) {
|
app.use(function (err, req, res, next) {
|
||||||
res.status(err.status || 500)
|
res.status(err.status || 500)
|
||||||
res.send(err.message)
|
res.send(String(req.headers['x-error-property']
|
||||||
|
? err[req.headers['x-error-property']]
|
||||||
|
: ('[' + err.type + '] ' + err.message)))
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post('/', function (req, res) {
|
app.post('/', function (req, res) {
|
||||||
|
|
@ -440,3 +568,11 @@ function createApp (options) {
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryRequire (name) {
|
||||||
|
try {
|
||||||
|
return require(name)
|
||||||
|
} catch (e) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
|
var asyncHooks = tryRequire('async_hooks')
|
||||||
var Buffer = require('safe-buffer').Buffer
|
var Buffer = require('safe-buffer').Buffer
|
||||||
var express = require('..')
|
var express = require('..')
|
||||||
var request = require('supertest')
|
var request = require('supertest')
|
||||||
|
|
||||||
|
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||||
|
? describe
|
||||||
|
: describe.skip
|
||||||
|
|
||||||
describe('express.urlencoded()', function () {
|
describe('express.urlencoded()', function () {
|
||||||
before(function () {
|
before(function () {
|
||||||
this.app = createApp()
|
this.app = createApp()
|
||||||
|
|
@ -217,7 +222,7 @@ describe('express.urlencoded()', function () {
|
||||||
test.set('Content-Encoding', 'gzip')
|
test.set('Content-Encoding', 'gzip')
|
||||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
||||||
test.expect(415, 'content encoding unsupported', done)
|
test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -248,8 +253,8 @@ describe('express.urlencoded()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should 413 when over limit with chunked encoding', function (done) {
|
it('should 413 when over limit with chunked encoding', function (done) {
|
||||||
var buf = Buffer.alloc(1024, '.')
|
|
||||||
var app = createApp({ limit: '1kb' })
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var buf = Buffer.alloc(1024, '.')
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
test.set('Transfer-Encoding', 'chunked')
|
test.set('Transfer-Encoding', 'chunked')
|
||||||
|
|
@ -258,6 +263,15 @@ describe('express.urlencoded()', function () {
|
||||||
test.expect(413, done)
|
test.expect(413, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should 413 when inflated body over limit', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var test = request(app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex'))
|
||||||
|
test.expect(413, done)
|
||||||
|
})
|
||||||
|
|
||||||
it('should accept number of bytes', function (done) {
|
it('should accept number of bytes', function (done) {
|
||||||
var buf = Buffer.alloc(1024, '.')
|
var buf = Buffer.alloc(1024, '.')
|
||||||
request(createApp({ limit: 1024 }))
|
request(createApp({ limit: 1024 }))
|
||||||
|
|
@ -282,8 +296,8 @@ describe('express.urlencoded()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not hang response', function (done) {
|
it('should not hang response', function (done) {
|
||||||
var buf = Buffer.alloc(10240, '.')
|
|
||||||
var app = createApp({ limit: '8kb' })
|
var app = createApp({ limit: '8kb' })
|
||||||
|
var buf = Buffer.alloc(10240, '.')
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
test.write(buf)
|
test.write(buf)
|
||||||
|
|
@ -291,6 +305,15 @@ describe('express.urlencoded()', function () {
|
||||||
test.write(buf)
|
test.write(buf)
|
||||||
test.expect(413, done)
|
test.expect(413, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not error when inflating', function (done) {
|
||||||
|
var app = createApp({ limit: '1kb' })
|
||||||
|
var test = request(app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex'))
|
||||||
|
test.expect(413, done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with parameterLimit option', function () {
|
describe('with parameterLimit option', function () {
|
||||||
|
|
@ -310,16 +333,7 @@ describe('express.urlencoded()', function () {
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
.send(createManyParams(11))
|
.send(createManyParams(11))
|
||||||
.expect(413, /too many parameters/, done)
|
.expect(413, '[parameters.too.many] too many parameters', done)
|
||||||
})
|
|
||||||
|
|
||||||
it('should error with type = "parameters.too.many"', function (done) {
|
|
||||||
request(createApp({ extended: false, parameterLimit: 10 }))
|
|
||||||
.post('/')
|
|
||||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send(createManyParams(11))
|
|
||||||
.expect(413, 'parameters.too.many', done)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work when at the limit', function (done) {
|
it('should work when at the limit', function (done) {
|
||||||
|
|
@ -374,16 +388,7 @@ describe('express.urlencoded()', function () {
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
.send(createManyParams(11))
|
.send(createManyParams(11))
|
||||||
.expect(413, /too many parameters/, done)
|
.expect(413, '[parameters.too.many] too many parameters', done)
|
||||||
})
|
|
||||||
|
|
||||||
it('should error with type = "parameters.too.many"', function (done) {
|
|
||||||
request(createApp({ extended: true, parameterLimit: 10 }))
|
|
||||||
.post('/')
|
|
||||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send(createManyParams(11))
|
|
||||||
.expect(413, 'parameters.too.many', done)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work when at the limit', function (done) {
|
it('should work when at the limit', function (done) {
|
||||||
|
|
@ -526,65 +531,59 @@ describe('express.urlencoded()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should error from verify', function (done) {
|
it('should error from verify', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x20) throw new Error('no leading space')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
.send(' user=tobi')
|
.send(' user=tobi')
|
||||||
.expect(403, 'no leading space', done)
|
.expect(403, '[entity.verify.failed] no leading space', done)
|
||||||
})
|
|
||||||
|
|
||||||
it('should error with type = "entity.verify.failed"', function (done) {
|
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
|
||||||
if (buf[0] === 0x20) throw new Error('no leading space')
|
|
||||||
} })
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.post('/')
|
|
||||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send(' user=tobi')
|
|
||||||
.expect(403, 'entity.verify.failed', done)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow custom codes', function (done) {
|
it('should allow custom codes', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] !== 0x20) return
|
verify: function (req, res, buf) {
|
||||||
var err = new Error('no leading space')
|
if (buf[0] !== 0x20) return
|
||||||
err.status = 400
|
var err = new Error('no leading space')
|
||||||
throw err
|
err.status = 400
|
||||||
} })
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
.send(' user=tobi')
|
.send(' user=tobi')
|
||||||
.expect(400, 'no leading space', done)
|
.expect(400, '[entity.verify.failed] no leading space', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow custom type', function (done) {
|
it('should allow custom type', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] !== 0x20) return
|
verify: function (req, res, buf) {
|
||||||
var err = new Error('no leading space')
|
if (buf[0] !== 0x20) return
|
||||||
err.type = 'foo.bar'
|
var err = new Error('no leading space')
|
||||||
throw err
|
err.type = 'foo.bar'
|
||||||
} })
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
.set('X-Error-Property', 'type')
|
|
||||||
.send(' user=tobi')
|
.send(' user=tobi')
|
||||||
.expect(403, 'foo.bar', done)
|
.expect(403, '[foo.bar] no leading space', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow pass-through', function (done) {
|
it('should allow pass-through', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
if (buf[0] === 0x5b) throw new Error('no arrays')
|
verify: function (req, res, buf) {
|
||||||
} })
|
if (buf[0] === 0x5b) throw new Error('no arrays')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/')
|
.post('/')
|
||||||
|
|
@ -594,14 +593,109 @@ describe('express.urlencoded()', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should 415 on unknown charset prior to verify', function (done) {
|
it('should 415 on unknown charset prior to verify', function (done) {
|
||||||
var app = createApp({ verify: function (req, res, buf) {
|
var app = createApp({
|
||||||
throw new Error('unexpected verify call')
|
verify: function (req, res, buf) {
|
||||||
} })
|
throw new Error('unexpected verify call')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var test = request(app).post('/')
|
var test = request(app).post('/')
|
||||||
test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus')
|
test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus')
|
||||||
test.write(Buffer.from('00000000', 'hex'))
|
test.write(Buffer.from('00000000', 'hex'))
|
||||||
test.expect(415, 'unsupported charset "X-BOGUS"', done)
|
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describeAsyncHooks('async local storage', function () {
|
||||||
|
before(function () {
|
||||||
|
var app = express()
|
||||||
|
var store = { foo: 'bar' }
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||||
|
req.asyncLocalStorage.run(store, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(express.urlencoded())
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(err.status || 500)
|
||||||
|
res.send('[' + err.type + '] ' + err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/', function (req, res) {
|
||||||
|
res.json(req.body)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.app = app
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
.send('user=tobi')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.expect('{"user":"tobi"}')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when unmatched content-type', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/fizzbuzz')
|
||||||
|
.send('buzz')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when inflated', function (done) {
|
||||||
|
var test = request(this.app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
|
||||||
|
test.expect(200)
|
||||||
|
test.expect('x-store-foo', 'bar')
|
||||||
|
test.expect('{"name":"论"}')
|
||||||
|
test.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when inflate error', function (done) {
|
||||||
|
var test = request(this.app).post('/')
|
||||||
|
test.set('Content-Encoding', 'gzip')
|
||||||
|
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))
|
||||||
|
test.expect(400)
|
||||||
|
test.expect('x-store-foo', 'bar')
|
||||||
|
test.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store when limit exceeded', function (done) {
|
||||||
|
request(this.app)
|
||||||
|
.post('/')
|
||||||
|
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
.send('user=' + Buffer.alloc(1024 * 100, '.').toString())
|
||||||
|
.expect(413)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.end(done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -636,7 +730,7 @@ describe('express.urlencoded()', function () {
|
||||||
var test = request(this.app).post('/')
|
var test = request(this.app).post('/')
|
||||||
test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r')
|
test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r')
|
||||||
test.write(Buffer.from('6e616d653dcec5d4', 'hex'))
|
test.write(Buffer.from('6e616d653dcec5d4', 'hex'))
|
||||||
test.expect(415, 'unsupported charset "KOI8-R"', done)
|
test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -684,12 +778,12 @@ describe('express.urlencoded()', function () {
|
||||||
test.expect(200, '{"name":"论"}', done)
|
test.expect(200, '{"name":"论"}', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fail on unknown encoding', function (done) {
|
it('should 415 on unknown encoding', function (done) {
|
||||||
var test = request(this.app).post('/')
|
var test = request(this.app).post('/')
|
||||||
test.set('Content-Encoding', 'nulls')
|
test.set('Content-Encoding', 'nulls')
|
||||||
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
test.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
test.write(Buffer.from('000000000000', 'hex'))
|
test.write(Buffer.from('000000000000', 'hex'))
|
||||||
test.expect(415, 'unsupported content encoding "nulls"', done)
|
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -718,7 +812,9 @@ function createApp (options) {
|
||||||
|
|
||||||
app.use(function (err, req, res, next) {
|
app.use(function (err, req, res, next) {
|
||||||
res.status(err.status || 500)
|
res.status(err.status || 500)
|
||||||
res.send(String(err[req.headers['x-error-property'] || 'message']))
|
res.send(String(req.headers['x-error-property']
|
||||||
|
? err[req.headers['x-error-property']]
|
||||||
|
: ('[' + err.type + '] ' + err.message)))
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post('/', function (req, res) {
|
app.post('/', function (req, res) {
|
||||||
|
|
@ -733,3 +829,11 @@ function expectKeyCount (count) {
|
||||||
assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count)
|
assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryRequire (name) {
|
||||||
|
try {
|
||||||
|
return require(name)
|
||||||
|
} catch (e) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
--require should
|
|
||||||
--slow 20
|
|
||||||
|
|
@ -1,31 +1,29 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
var assert = require('assert')
|
||||||
var express = require('..')
|
var express = require('..')
|
||||||
var request = require('supertest')
|
var request = require('supertest')
|
||||||
var should = require('should')
|
|
||||||
|
|
||||||
describe('res', function () {
|
describe('res', function () {
|
||||||
// note about these tests: "Link" and "X-*" are chosen because
|
|
||||||
// the common node.js versions white list which _incoming_
|
|
||||||
// headers can appear multiple times; there is no such white list
|
|
||||||
// for outgoing, though
|
|
||||||
describe('.append(field, val)', function () {
|
describe('.append(field, val)', function () {
|
||||||
it('should append multiple headers', function (done) {
|
it('should append multiple headers', function (done) {
|
||||||
var app = express()
|
var app = express()
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
res.append('Link', '<http://localhost/>')
|
res.append('Set-Cookie', 'foo=bar')
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use(function (req, res) {
|
app.use(function (req, res) {
|
||||||
res.append('Link', '<http://localhost:80/>')
|
res.append('Set-Cookie', 'fizz=buzz')
|
||||||
res.end()
|
res.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect('Link', '<http://localhost/>, <http://localhost:80/>', done)
|
.expect(200)
|
||||||
|
.expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz']))
|
||||||
|
.end(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should accept array of values', function (done) {
|
it('should accept array of values', function (done) {
|
||||||
|
|
@ -37,51 +35,54 @@ describe('res', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect(function (res) {
|
.expect(200)
|
||||||
should(res.headers['set-cookie']).eql(['foo=bar', 'fizz=buzz'])
|
.expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz']))
|
||||||
})
|
.end(done)
|
||||||
.expect(200, done)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should get reset by res.set(field, val)', function (done) {
|
it('should get reset by res.set(field, val)', function (done) {
|
||||||
var app = express()
|
var app = express()
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
res.append('Link', '<http://localhost/>')
|
res.append('Set-Cookie', 'foo=bar')
|
||||||
res.append('Link', '<http://localhost:80/>')
|
res.append('Set-Cookie', 'fizz=buzz')
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use(function (req, res) {
|
app.use(function (req, res) {
|
||||||
res.set('Link', '<http://127.0.0.1/>')
|
res.set('Set-Cookie', 'pet=tobi')
|
||||||
res.end()
|
res.end()
|
||||||
});
|
});
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect('Link', '<http://127.0.0.1/>', done)
|
.expect(200)
|
||||||
|
.expect(shouldHaveHeaderValues('Set-Cookie', ['pet=tobi']))
|
||||||
|
.end(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work with res.set(field, val) first', function (done) {
|
it('should work with res.set(field, val) first', function (done) {
|
||||||
var app = express()
|
var app = express()
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
res.set('Link', '<http://localhost/>')
|
res.set('Set-Cookie', 'foo=bar')
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use(function(req, res){
|
app.use(function(req, res){
|
||||||
res.append('Link', '<http://localhost:80/>')
|
res.append('Set-Cookie', 'fizz=buzz')
|
||||||
res.end()
|
res.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect('Link', '<http://localhost/>, <http://localhost:80/>', done)
|
.expect(200)
|
||||||
|
.expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz']))
|
||||||
|
.end(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work with cookies', function (done) {
|
it('should work together with res.cookie', function (done) {
|
||||||
var app = express()
|
var app = express()
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
|
|
@ -90,16 +91,26 @@ describe('res', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use(function (req, res) {
|
app.use(function (req, res) {
|
||||||
res.append('Set-Cookie', 'bar=baz')
|
res.append('Set-Cookie', 'fizz=buzz')
|
||||||
res.end()
|
res.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect(function (res) {
|
.expect(200)
|
||||||
should(res.headers['set-cookie']).eql(['foo=bar; Path=/', 'bar=baz'])
|
.expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar; Path=/', 'fizz=buzz']))
|
||||||
})
|
.end(done)
|
||||||
.expect(200, done)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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 + '"')
|
||||||
|
for (var i = 0; i < values.length; i++) {
|
||||||
|
assert.strictEqual(headers[i], values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,37 @@ describe('res', function(){
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('expires', function () {
|
||||||
|
it('should throw on invalid date', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.cookie('name', 'tobi', { expires: new Date(NaN) })
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(500, /option expires is invalid/, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('partitioned', function () {
|
||||||
|
it('should set partitioned', function (done) {
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.cookie('name', 'tobi', { partitioned: true });
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect('Set-Cookie', 'name=tobi; Path=/; Partitioned')
|
||||||
|
.expect(200, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('maxAge', function(){
|
describe('maxAge', function(){
|
||||||
it('should set relative expires', function(done){
|
it('should set relative expires', function(done){
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
@ -111,6 +142,36 @@ describe('res', function(){
|
||||||
.expect(200, optionsCopy, done)
|
.expect(200, optionsCopy, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not throw on null', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.cookie('name', 'tobi', { maxAge: null })
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Set-Cookie', 'name=tobi; Path=/')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not throw on undefined', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.cookie('name', 'tobi', { maxAge: undefined })
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Set-Cookie', 'name=tobi; Path=/')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
it('should throw an error with invalid maxAge', function (done) {
|
it('should throw an error with invalid maxAge', function (done) {
|
||||||
var app = express()
|
var app = express()
|
||||||
|
|
||||||
|
|
@ -125,6 +186,63 @@ describe('res', function(){
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('priority', function () {
|
||||||
|
it('should set low priority', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.cookie('name', 'tobi', { priority: 'low' })
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect('Set-Cookie', /Priority=Low/)
|
||||||
|
.expect(200, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should set medium priority', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.cookie('name', 'tobi', { priority: 'medium' })
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect('Set-Cookie', /Priority=Medium/)
|
||||||
|
.expect(200, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should set high priority', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.cookie('name', 'tobi', { priority: 'high' })
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect('Set-Cookie', /Priority=High/)
|
||||||
|
.expect(200, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw with invalid priority', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.cookie('name', 'tobi', { priority: 'foobar' })
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(500, /option priority is invalid/, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('signed', function(){
|
describe('signed', function(){
|
||||||
it('should generate a signed JSON cookie', function(done){
|
it('should generate a signed JSON cookie', function(done){
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,19 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var after = require('after');
|
var after = require('after');
|
||||||
var assert = require('assert');
|
var assert = require('assert')
|
||||||
|
var asyncHooks = tryRequire('async_hooks')
|
||||||
var Buffer = require('safe-buffer').Buffer
|
var Buffer = require('safe-buffer').Buffer
|
||||||
var express = require('..');
|
var express = require('..');
|
||||||
|
var path = require('path')
|
||||||
var request = require('supertest');
|
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('res', function(){
|
||||||
describe('.download(path)', function(){
|
describe('.download(path)', function(){
|
||||||
|
|
@ -81,6 +90,272 @@ describe('res', function(){
|
||||||
.expect('Content-Disposition', 'attachment; filename="user.html"')
|
.expect('Content-Disposition', 'attachment; filename="user.html"')
|
||||||
.expect(200, cb);
|
.expect(200, cb);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describeAsyncHooks('async local storage', function () {
|
||||||
|
it('should presist store', function (done) {
|
||||||
|
var app = express()
|
||||||
|
var cb = after(2, done)
|
||||||
|
var store = { foo: 'bar' }
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||||
|
req.asyncLocalStorage.run(store, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/name.txt', function (err) {
|
||||||
|
if (err) return cb(err)
|
||||||
|
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
assert.strictEqual(local.foo, 'bar')
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
.expect('Content-Disposition', 'attachment; filename="name.txt"')
|
||||||
|
.expect(200, 'tobi', cb)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store on error', function (done) {
|
||||||
|
var app = express()
|
||||||
|
var store = { foo: 'bar' }
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||||
|
req.asyncLocalStorage.run(store, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/does-not-exist', function (err) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(err ? 'got ' + err.status + ' error' : 'no error')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.expect('got 404 error')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('.download(path, options)', function () {
|
||||||
|
it('should allow options to res.sendFile()', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/.name', {
|
||||||
|
dotfiles: 'allow',
|
||||||
|
maxAge: '4h'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Disposition', 'attachment; filename=".name"')
|
||||||
|
.expect('Cache-Control', 'public, max-age=14400')
|
||||||
|
.expect(utils.shouldHaveBody(Buffer.from('tobi')))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "headers" option', function () {
|
||||||
|
it('should set headers on response', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/user.html', {
|
||||||
|
headers: {
|
||||||
|
'X-Foo': 'Bar',
|
||||||
|
'X-Bar': 'Foo'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('X-Foo', 'Bar')
|
||||||
|
.expect('X-Bar', 'Foo')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use last header when duplicated', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/user.html', {
|
||||||
|
headers: {
|
||||||
|
'X-Foo': 'Bar',
|
||||||
|
'x-foo': 'bar'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('X-Foo', 'bar')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should override Content-Type', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/user.html', {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/x-custom'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Type', 'text/x-custom')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not set headers on 404', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/does-not-exist', {
|
||||||
|
headers: {
|
||||||
|
'X-Foo': 'Bar'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(404)
|
||||||
|
.expect(utils.shouldNotHaveHeader('X-Foo'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when headers contains Content-Disposition', function () {
|
||||||
|
it('should be ignored', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/user.html', {
|
||||||
|
headers: {
|
||||||
|
'Content-Disposition': 'inline'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Disposition', 'attachment; filename="user.html"')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be ignored case-insensitively', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('test/fixtures/user.html', {
|
||||||
|
headers: {
|
||||||
|
'content-disposition': 'inline'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Disposition', 'attachment; filename="user.html"')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "root" option', function () {
|
||||||
|
it('should allow relative path', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('name.txt', {
|
||||||
|
root: FIXTURES_PATH
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Disposition', 'attachment; filename="name.txt"')
|
||||||
|
.expect(utils.shouldHaveBody(Buffer.from('tobi')))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should allow up within root', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('fake/../name.txt', {
|
||||||
|
root: FIXTURES_PATH
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Disposition', 'attachment; filename="name.txt"')
|
||||||
|
.expect(utils.shouldHaveBody(Buffer.from('tobi')))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject up outside root', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
var p = '..' + path.sep +
|
||||||
|
path.relative(path.dirname(FIXTURES_PATH), path.join(FIXTURES_PATH, 'name.txt'))
|
||||||
|
|
||||||
|
res.download(p, {
|
||||||
|
root: FIXTURES_PATH
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(403)
|
||||||
|
.expect(utils.shouldNotHaveHeader('Content-Disposition'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject reading outside root', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.download('../name.txt', {
|
||||||
|
root: FIXTURES_PATH
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(403)
|
||||||
|
.expect(utils.shouldNotHaveHeader('Content-Disposition'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.download(path, filename, fn)', function(){
|
describe('.download(path, filename, fn)', function(){
|
||||||
|
|
@ -107,7 +382,7 @@ describe('res', function(){
|
||||||
var options = {}
|
var options = {}
|
||||||
|
|
||||||
app.use(function (req, res) {
|
app.use(function (req, res) {
|
||||||
res.download('test/fixtures/user.html', 'document', options, done)
|
res.download('test/fixtures/user.html', 'document', options, cb)
|
||||||
})
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
|
|
@ -129,12 +404,12 @@ describe('res', function(){
|
||||||
})
|
})
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Disposition', 'attachment; filename="document"')
|
.expect('Content-Disposition', 'attachment; filename="document"')
|
||||||
.expect('Cache-Control', 'public, max-age=14400')
|
.expect('Cache-Control', 'public, max-age=14400')
|
||||||
.expect(shouldHaveBody(Buffer.from('tobi')))
|
.expect(utils.shouldHaveBody(Buffer.from('tobi')))
|
||||||
.end(done)
|
.end(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when options.headers contains Content-Disposition', function () {
|
describe('when options.headers contains Content-Disposition', function () {
|
||||||
|
|
@ -207,25 +482,17 @@ describe('res', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect(shouldNotHaveHeader('Content-Disposition'))
|
.expect(utils.shouldNotHaveHeader('Content-Disposition'))
|
||||||
.expect(200, 'failed', done);
|
.expect(200, 'failed', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function shouldHaveBody (buf) {
|
function tryRequire (name) {
|
||||||
return function (res) {
|
try {
|
||||||
var body = !Buffer.isBuffer(res.body)
|
return require(name)
|
||||||
? Buffer.from(res.text)
|
} catch (e) {
|
||||||
: res.body
|
return {}
|
||||||
assert.ok(body, 'response has body')
|
|
||||||
assert.strictEqual(body.toString('hex'), buf.toString('hex'))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldNotHaveHeader(header) {
|
|
||||||
return function (res) {
|
|
||||||
assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -52,13 +52,18 @@ var app3 = express();
|
||||||
app3.use(function(req, res, next){
|
app3.use(function(req, res, next){
|
||||||
res.format({
|
res.format({
|
||||||
text: function(){ res.send('hey') },
|
text: function(){ res.send('hey') },
|
||||||
default: function(){ res.send('default') }
|
default: function (a, b, c) {
|
||||||
|
assert(req === a)
|
||||||
|
assert(res === b)
|
||||||
|
assert(next === c)
|
||||||
|
res.send('default')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
var app4 = express();
|
var app4 = express();
|
||||||
|
|
||||||
app4.get('/', function(req, res, next){
|
app4.get('/', function (req, res) {
|
||||||
res.format({
|
res.format({
|
||||||
text: function(){ res.send('hey') },
|
text: function(){ res.send('hey') },
|
||||||
html: function(){ res.send('<p>hey</p>') },
|
html: function(){ res.send('<p>hey</p>') },
|
||||||
|
|
@ -122,6 +127,28 @@ describe('res', function(){
|
||||||
.set('Accept', '*/*')
|
.set('Accept', '*/*')
|
||||||
.expect('hey', done);
|
.expect('hey', done);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should be able to invoke other formatter', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
res.format({
|
||||||
|
json: function () { res.send('json') },
|
||||||
|
default: function () {
|
||||||
|
res.header('x-default', '1')
|
||||||
|
this.json()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.set('Accept', 'text/plain')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-default', '1')
|
||||||
|
.expect('json')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('in router', function(){
|
describe('in router', function(){
|
||||||
|
|
@ -132,7 +159,7 @@ describe('res', function(){
|
||||||
var app = express();
|
var app = express();
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
|
|
||||||
router.get('/', function(req, res, next){
|
router.get('/', function (req, res) {
|
||||||
res.format({
|
res.format({
|
||||||
text: function(){ res.send('hey') },
|
text: function(){ res.send('hey') },
|
||||||
html: function(){ res.send('<p>hey</p>') },
|
html: function(){ res.send('<p>hey</p>') },
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,27 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var express = require('../')
|
var express = require('../')
|
||||||
, request = require('supertest');
|
, request = require('supertest')
|
||||||
|
, url = require('url');
|
||||||
|
|
||||||
describe('res', function(){
|
describe('res', function(){
|
||||||
describe('.location(url)', function(){
|
describe('.location(url)', function(){
|
||||||
it('should set the header', function(done){
|
it('should set the header', function(done){
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
|
app.use(function(req, res){
|
||||||
|
res.location('http://google.com/').end();
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect('Location', 'http://google.com/')
|
||||||
|
.expect(200, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should preserve trailing slashes when not present', function(done){
|
||||||
|
var app = express();
|
||||||
|
|
||||||
app.use(function(req, res){
|
app.use(function(req, res){
|
||||||
res.location('http://google.com').end();
|
res.location('http://google.com').end();
|
||||||
});
|
});
|
||||||
|
|
@ -31,6 +45,36 @@ describe('res', function(){
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not encode bad "url"', 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();
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/?q=http://google.com\\@apple.com')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Location', 'http://google.com\\@apple.com')
|
||||||
|
.end(function (err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This ensures that our protocol check is case insensitive
|
||||||
|
request(app)
|
||||||
|
.get('/?q=HTTP://google.com\\@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 not touch already-encoded sequences in "url"', function (done) {
|
||||||
var app = express()
|
var app = express()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var assert = require('assert')
|
|
||||||
var express = require('..');
|
var express = require('..');
|
||||||
var request = require('supertest');
|
var request = require('supertest');
|
||||||
var utils = require('./support/utils');
|
var utils = require('./support/utils');
|
||||||
|
|
@ -71,11 +70,11 @@ describe('res', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.head('/')
|
.head('/')
|
||||||
.expect(302)
|
.expect(302)
|
||||||
.expect('Location', 'http://google.com')
|
.expect('Location', 'http://google.com')
|
||||||
.expect(shouldNotHaveBody())
|
.expect(utils.shouldNotHaveBody())
|
||||||
.end(done)
|
.end(done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -184,20 +183,14 @@ describe('res', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.set('Accept', 'application/octet-stream')
|
.set('Accept', 'application/octet-stream')
|
||||||
.expect(302)
|
.expect(302)
|
||||||
.expect('location', 'http://google.com')
|
.expect('location', 'http://google.com')
|
||||||
.expect('content-length', '0')
|
.expect('content-length', '0')
|
||||||
.expect(utils.shouldNotHaveHeader('Content-Type'))
|
.expect(utils.shouldNotHaveHeader('Content-Type'))
|
||||||
.expect(shouldNotHaveBody())
|
.expect(utils.shouldNotHaveBody())
|
||||||
.end(done)
|
.end(done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function shouldNotHaveBody () {
|
|
||||||
return function (res) {
|
|
||||||
assert.ok(res.text === '' || res.text === undefined)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -143,11 +143,11 @@ describe('res', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', 'application/octet-stream')
|
.expect('Content-Type', 'application/octet-stream')
|
||||||
.expect(shouldHaveBody(Buffer.from('hello')))
|
.expect(utils.shouldHaveBody(Buffer.from('hello')))
|
||||||
.end(done)
|
.end(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set ETag', function (done) {
|
it('should set ETag', function (done) {
|
||||||
|
|
@ -214,10 +214,10 @@ describe('res', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.head('/')
|
.head('/')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect(shouldNotHaveBody())
|
.expect(utils.shouldNotHaveBody())
|
||||||
.end(done)
|
.end(done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -238,6 +238,22 @@ describe('res', function(){
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('when .statusCode is 205', function () {
|
||||||
|
it('should strip Transfer-Encoding field and body, set Content-Length', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(205).set('Transfer-Encoding', 'chunked').send('foo')
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(utils.shouldNotHaveHeader('Transfer-Encoding'))
|
||||||
|
.expect('Content-Length', '0')
|
||||||
|
.expect(205, '', done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('when .statusCode is 304', function(){
|
describe('when .statusCode is 304', function(){
|
||||||
it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){
|
it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
@ -533,19 +549,3 @@ describe('res', function(){
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function shouldHaveBody (buf) {
|
|
||||||
return function (res) {
|
|
||||||
var body = !Buffer.isBuffer(res.body)
|
|
||||||
? Buffer.from(res.text)
|
|
||||||
: res.body
|
|
||||||
assert.ok(body, 'response has body')
|
|
||||||
assert.strictEqual(body.toString('hex'), buf.toString('hex'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldNotHaveBody () {
|
|
||||||
return function (res) {
|
|
||||||
assert.ok(res.text === '' || res.text === undefined)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,19 @@
|
||||||
|
|
||||||
var after = require('after');
|
var after = require('after');
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
|
var asyncHooks = tryRequire('async_hooks')
|
||||||
var Buffer = require('safe-buffer').Buffer
|
var Buffer = require('safe-buffer').Buffer
|
||||||
var express = require('../')
|
var express = require('../')
|
||||||
, request = require('supertest')
|
, request = require('supertest')
|
||||||
var onFinished = require('on-finished');
|
var onFinished = require('on-finished');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var should = require('should');
|
|
||||||
var fixtures = path.join(__dirname, 'fixtures');
|
var fixtures = path.join(__dirname, 'fixtures');
|
||||||
var utils = require('./support/utils');
|
var utils = require('./support/utils');
|
||||||
|
|
||||||
|
var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
|
||||||
|
? describe
|
||||||
|
: describe.skip
|
||||||
|
|
||||||
describe('res', function(){
|
describe('res', function(){
|
||||||
describe('.sendFile(path)', function () {
|
describe('.sendFile(path)', function () {
|
||||||
it('should error missing path', function (done) {
|
it('should error missing path', function (done) {
|
||||||
|
|
@ -29,6 +33,14 @@ describe('res', function(){
|
||||||
.expect(500, /TypeError: path must be a string to res.sendFile/, done)
|
.expect(500, /TypeError: path must be a string to res.sendFile/, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should error for non-absolute path', function (done) {
|
||||||
|
var app = createApp('name.txt')
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(500, /TypeError: path must be absolute/, done)
|
||||||
|
})
|
||||||
|
|
||||||
it('should transfer a file', function (done) {
|
it('should transfer a file', function (done) {
|
||||||
var app = createApp(path.resolve(fixtures, 'name.txt'));
|
var app = createApp(path.resolve(fixtures, 'name.txt'));
|
||||||
|
|
||||||
|
|
@ -91,6 +103,23 @@ describe('res', function(){
|
||||||
.expect(404, done);
|
.expect(404, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should send cache-control by default', function (done) {
|
||||||
|
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'))
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect('Cache-Control', 'public, max-age=0')
|
||||||
|
.expect(200, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not serve dotfiles by default', function (done) {
|
||||||
|
var app = createApp(path.resolve(__dirname, 'fixtures/.name'))
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(404, done)
|
||||||
|
})
|
||||||
|
|
||||||
it('should not override manual content-types', function (done) {
|
it('should not override manual content-types', function (done) {
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
|
|
@ -132,136 +161,6 @@ describe('res', function(){
|
||||||
server.close(cb)
|
server.close(cb)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with "cacheControl" option', function () {
|
|
||||||
it('should enable cacheControl by default', function (done) {
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'))
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect('Cache-Control', 'public, max-age=0')
|
|
||||||
.expect(200, done)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should accept cacheControl option', function (done) {
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), { cacheControl: false })
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect(utils.shouldNotHaveHeader('Cache-Control'))
|
|
||||||
.expect(200, done)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with "dotfiles" option', function () {
|
|
||||||
it('should not serve dotfiles by default', function (done) {
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/.name'));
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect(404, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should accept dotfiles option', function(done){
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/.name'), { dotfiles: 'allow' });
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect(200)
|
|
||||||
.expect(shouldHaveBody(Buffer.from('tobi')))
|
|
||||||
.end(done)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with "headers" option', function () {
|
|
||||||
it('should accept headers option', function (done) {
|
|
||||||
var headers = {
|
|
||||||
'x-success': 'sent',
|
|
||||||
'x-other': 'done'
|
|
||||||
};
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), { headers: headers });
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect('x-success', 'sent')
|
|
||||||
.expect('x-other', 'done')
|
|
||||||
.expect(200, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should ignore headers option on 404', function (done) {
|
|
||||||
var headers = { 'x-success': 'sent' };
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/does-not-exist'), { headers: headers });
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect(utils.shouldNotHaveHeader('X-Success'))
|
|
||||||
.expect(404, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with "immutable" option', function () {
|
|
||||||
it('should add immutable cache-control directive', function (done) {
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), {
|
|
||||||
immutable: true,
|
|
||||||
maxAge: '4h'
|
|
||||||
})
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect('Cache-Control', 'public, max-age=14400, immutable')
|
|
||||||
.expect(200, done)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with "maxAge" option', function () {
|
|
||||||
it('should set cache-control max-age from number', function (done) {
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), {
|
|
||||||
maxAge: 14400000
|
|
||||||
})
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect('Cache-Control', 'public, max-age=14400')
|
|
||||||
.expect(200, done)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should set cache-control max-age from string', function (done) {
|
|
||||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), {
|
|
||||||
maxAge: '4h'
|
|
||||||
})
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect('Cache-Control', 'public, max-age=14400')
|
|
||||||
.expect(200, done)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with "root" option', function () {
|
|
||||||
it('should not transfer relative with without', function (done) {
|
|
||||||
var app = createApp('test/fixtures/name.txt');
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect(500, /must be absolute/, done);
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should serve relative to "root"', function (done) {
|
|
||||||
var app = createApp('name.txt', {root: fixtures});
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect(200, 'tobi', done);
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should disallow requesting out of "root"', function (done) {
|
|
||||||
var app = createApp('foo/../../user.html', {root: fixtures});
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get('/')
|
|
||||||
.expect(403, done);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.sendFile(path, fn)', function () {
|
describe('.sendFile(path, fn)', function () {
|
||||||
|
|
@ -359,15 +258,71 @@ describe('res', function(){
|
||||||
|
|
||||||
app.use(function (req, res) {
|
app.use(function (req, res) {
|
||||||
res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) {
|
res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) {
|
||||||
should(err).be.ok()
|
res.send(err ? 'got ' + err.status + ' error' : 'no error')
|
||||||
err.status.should.equal(404);
|
|
||||||
res.send('got it');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect(200, 'got it', done);
|
.expect(200, 'got 404 error', done)
|
||||||
|
})
|
||||||
|
|
||||||
|
describeAsyncHooks('async local storage', function () {
|
||||||
|
it('should presist store', function (done) {
|
||||||
|
var app = express()
|
||||||
|
var cb = after(2, done)
|
||||||
|
var store = { foo: 'bar' }
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||||
|
req.asyncLocalStorage.run(store, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) {
|
||||||
|
if (err) return cb(err)
|
||||||
|
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
assert.strictEqual(local.foo, 'bar')
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
.expect(200, 'tobi', cb)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should presist store on error', function (done) {
|
||||||
|
var app = express()
|
||||||
|
var store = { foo: 'bar' }
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
|
||||||
|
req.asyncLocalStorage.run(store, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) {
|
||||||
|
var local = req.asyncLocalStorage.getStore()
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
res.setHeader('x-store-foo', String(local.foo))
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(err ? 'got ' + err.status + ' error' : 'no error')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('x-store-foo', 'bar')
|
||||||
|
.expect('got 404 error')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -377,6 +332,563 @@ describe('res', function(){
|
||||||
.get('/')
|
.get('/')
|
||||||
.expect(200, 'to', done)
|
.expect(200, 'to', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('with "acceptRanges" option', function () {
|
||||||
|
describe('when true', function () {
|
||||||
|
it('should advertise byte range accepted', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'nums.txt'), {
|
||||||
|
acceptRanges: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Accept-Ranges', 'bytes')
|
||||||
|
.expect('123456789')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond to range request', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'nums.txt'), {
|
||||||
|
acceptRanges: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.set('Range', 'bytes=0-4')
|
||||||
|
.expect(206, '12345', done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when false', function () {
|
||||||
|
it('should not advertise accept-ranges', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'nums.txt'), {
|
||||||
|
acceptRanges: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect(utils.shouldNotHaveHeader('Accept-Ranges'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not honor range requests', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'nums.txt'), {
|
||||||
|
acceptRanges: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.set('Range', 'bytes=0-4')
|
||||||
|
.expect(200, '123456789', done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "cacheControl" option', function () {
|
||||||
|
describe('when true', function () {
|
||||||
|
it('should send cache-control header', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
cacheControl: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=0')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when false', function () {
|
||||||
|
it('should not send cache-control header', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
cacheControl: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect(utils.shouldNotHaveHeader('Cache-Control'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "dotfiles" option', function () {
|
||||||
|
describe('when "allow"', function () {
|
||||||
|
it('should allow dotfiles', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, '.name'), {
|
||||||
|
dotfiles: 'allow'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect(utils.shouldHaveBody(Buffer.from('tobi')))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "deny"', function () {
|
||||||
|
it('should deny dotfiles', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, '.name'), {
|
||||||
|
dotfiles: 'deny'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(403)
|
||||||
|
.expect(/Forbidden/)
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "ignore"', function () {
|
||||||
|
it('should ignore dotfiles', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, '.name'), {
|
||||||
|
dotfiles: 'ignore'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(404)
|
||||||
|
.expect(/Not Found/)
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "headers" option', function () {
|
||||||
|
it('should set headers on response', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
headers: {
|
||||||
|
'X-Foo': 'Bar',
|
||||||
|
'X-Bar': 'Foo'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('X-Foo', 'Bar')
|
||||||
|
.expect('X-Bar', 'Foo')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use last header when duplicated', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
headers: {
|
||||||
|
'X-Foo': 'Bar',
|
||||||
|
'x-foo': 'bar'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('X-Foo', 'bar')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should override Content-Type', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/x-custom'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Type', 'text/x-custom')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not set headers on 404', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'does-not-exist'), {
|
||||||
|
headers: {
|
||||||
|
'X-Foo': 'Bar'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(404)
|
||||||
|
.expect(utils.shouldNotHaveHeader('X-Foo'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "immutable" option', function () {
|
||||||
|
describe('when true', function () {
|
||||||
|
it('should send cache-control header with immutable', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
immutable: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=0, immutable')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when false', function () {
|
||||||
|
it('should not send cache-control header with immutable', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
immutable: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=0')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "lastModified" option', function () {
|
||||||
|
describe('when true', function () {
|
||||||
|
it('should send last-modified header', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
lastModified: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect(utils.shouldHaveHeader('Last-Modified'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should conditionally respond with if-modified-since', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
lastModified: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.set('If-Modified-Since', (new Date(Date.now() + 99999).toUTCString()))
|
||||||
|
.expect(304, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when false', function () {
|
||||||
|
it('should not have last-modified header', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
lastModified: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect(utils.shouldNotHaveHeader('Last-Modified'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not honor if-modified-since', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
lastModified: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.set('If-Modified-Since', (new Date(Date.now() + 99999).toUTCString()))
|
||||||
|
.expect(200)
|
||||||
|
.expect(utils.shouldNotHaveHeader('Last-Modified'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "maxAge" option', function () {
|
||||||
|
it('should set cache-control max-age to milliseconds', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
maxAge: 20000
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=20')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should cap cache-control max-age to 1 year', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
maxAge: 99999999999
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=31536000')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should min cache-control max-age to 0', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
maxAge: -20000
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=0')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should floor cache-control max-age', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
maxAge: 21911.23
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=21')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when cacheControl: false', function () {
|
||||||
|
it('should not send cache-control', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
cacheControl: false,
|
||||||
|
maxAge: 20000
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect(utils.shouldNotHaveHeader('Cache-Control'))
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when string', function () {
|
||||||
|
it('should accept plain number as milliseconds', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
maxAge: '20000'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=20')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should accept suffix "s" for seconds', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
maxAge: '20s'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=20')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should accept suffix "m" for minutes', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
maxAge: '20m'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=1200')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should accept suffix "d" for days', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.resolve(fixtures, 'user.html'), {
|
||||||
|
maxAge: '20d'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Cache-Control', 'public, max-age=1728000')
|
||||||
|
.end(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "root" option', function () {
|
||||||
|
it('should allow relative path', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile('name.txt', {
|
||||||
|
root: fixtures
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200, 'tobi', done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should allow up within root', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile('fake/../name.txt', {
|
||||||
|
root: fixtures
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(200, 'tobi', done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject up outside root', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile('..' + path.sep + path.relative(path.dirname(fixtures), path.join(fixtures, 'name.txt')), {
|
||||||
|
root: fixtures
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(403, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject reading outside root', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile('../name.txt', {
|
||||||
|
root: fixtures
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(403, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -390,12 +902,10 @@ function createApp(path, options, fn) {
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldHaveBody (buf) {
|
function tryRequire (name) {
|
||||||
return function (res) {
|
try {
|
||||||
var body = !Buffer.isBuffer(res.body)
|
return require(name)
|
||||||
? Buffer.from(res.text)
|
} catch (e) {
|
||||||
: res.body
|
return {}
|
||||||
assert.ok(body, 'response has body')
|
|
||||||
assert.strictEqual(body.toString('hex'), buf.toString('hex'))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,202 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
var express = require('../')
|
var express = require('../')
|
||||||
, request = require('supertest');
|
var request = require('supertest')
|
||||||
|
|
||||||
describe('res', function(){
|
var isIoJs = process.release
|
||||||
describe('.status(code)', function(){
|
? process.release.name === 'io.js'
|
||||||
it('should set the response .statusCode', function(done){
|
: ['v1.', 'v2.', 'v3.'].indexOf(process.version.slice(0, 3)) !== -1
|
||||||
var app = express();
|
|
||||||
|
|
||||||
app.use(function(req, res){
|
describe('res', function () {
|
||||||
res.status(201).end('Created');
|
describe('.status(code)', function () {
|
||||||
});
|
describe('when "code" is undefined', function () {
|
||||||
|
it('should raise error for invalid status code', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
request(app)
|
app.use(function (req, res) {
|
||||||
.get('/')
|
res.status(undefined).end()
|
||||||
.expect('Created')
|
})
|
||||||
.expect(201, done);
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(500, /Invalid status code/, function (err) {
|
||||||
|
if (isIoJs) {
|
||||||
|
done(err ? null : new Error('expected error'))
|
||||||
|
} else {
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is null', function () {
|
||||||
|
it('should raise error for invalid status code', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(null).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(500, /Invalid status code/, function (err) {
|
||||||
|
if (isIoJs) {
|
||||||
|
done(err ? null : new Error('expected error'))
|
||||||
|
} else {
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is 201', function () {
|
||||||
|
it('should set the response status code to 201', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(201).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(201, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is 302', function () {
|
||||||
|
it('should set the response status code to 302', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(302).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(302, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is 403', function () {
|
||||||
|
it('should set the response status code to 403', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(403).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(403, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is 501', function () {
|
||||||
|
it('should set the response status code to 501', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(501).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(501, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is "410"', function () {
|
||||||
|
it('should set the response status code to 410', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status('410').end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(410, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is 410.1', function () {
|
||||||
|
it('should set the response status code to 410', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(410.1).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(410, function (err) {
|
||||||
|
if (isIoJs) {
|
||||||
|
done(err ? null : new Error('expected error'))
|
||||||
|
} else {
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is 1000', function () {
|
||||||
|
it('should raise error for invalid status code', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(1000).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(500, /Invalid status code/, function (err) {
|
||||||
|
if (isIoJs) {
|
||||||
|
done(err ? null : new Error('expected error'))
|
||||||
|
} else {
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is 99', function () {
|
||||||
|
it('should raise error for invalid status code', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(99).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(500, /Invalid status code/, function (err) {
|
||||||
|
if (isIoJs) {
|
||||||
|
done(err ? null : new Error('expected error'))
|
||||||
|
} else {
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "code" is -401', function () {
|
||||||
|
it('should raise error for invalid status code', function (done) {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.status(-401).end()
|
||||||
|
})
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.get('/')
|
||||||
|
.expect(500, /Invalid status code/, function (err) {
|
||||||
|
if (isIoJs) {
|
||||||
|
done(err ? null : new Error('expected error'))
|
||||||
|
} else {
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ var Buffer = require('safe-buffer').Buffer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.shouldHaveBody = shouldHaveBody
|
exports.shouldHaveBody = shouldHaveBody
|
||||||
|
exports.shouldHaveHeader = shouldHaveHeader
|
||||||
exports.shouldNotHaveBody = shouldNotHaveBody
|
exports.shouldNotHaveBody = shouldNotHaveBody
|
||||||
exports.shouldNotHaveHeader = shouldNotHaveHeader;
|
exports.shouldNotHaveHeader = shouldNotHaveHeader;
|
||||||
|
|
||||||
|
|
@ -33,6 +34,19 @@ function shouldHaveBody (buf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that a supertest response does have a header.
|
||||||
|
*
|
||||||
|
* @param {string} header Header name to check
|
||||||
|
* @returns {function}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function shouldHaveHeader (header) {
|
||||||
|
return function (res) {
|
||||||
|
assert.ok((header.toLowerCase() in res.headers), 'should have header ' + header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert that a supertest response does not have a body.
|
* Assert that a supertest response does not have a body.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user