mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
183 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92c45199bd | ||
|
|
bd6908516d | ||
|
|
e6eeec3f03 | ||
|
|
269dc5323f | ||
|
|
efbc3f95ee | ||
|
|
99e839a274 | ||
|
|
32dbda1460 | ||
|
|
bcee730354 | ||
|
|
abe0ffa311 | ||
|
|
b601d64203 | ||
|
|
f381f2d9b6 | ||
|
|
12507cfcd0 | ||
|
|
185e327e29 | ||
|
|
c468f5ff20 | ||
|
|
ce3d1fe07e | ||
|
|
3136e98dce | ||
|
|
d1b1dfd472 | ||
|
|
ca306eace1 | ||
|
|
fd35351594 | ||
|
|
8a15f83d72 | ||
|
|
d91bf81c31 | ||
|
|
fd27f1f4e1 | ||
|
|
f0ad557987 | ||
|
|
9bb47fba30 | ||
|
|
78d489d730 | ||
|
|
8ffb9f9477 | ||
|
|
9cb147370e | ||
|
|
81036aa639 | ||
|
|
e7caaf6757 | ||
|
|
a525252690 | ||
|
|
0ebfc6b7bf | ||
|
|
381b3b0f77 | ||
|
|
ba8a4c5a8d | ||
|
|
2cccbc186e | ||
|
|
83bbf0902d | ||
|
|
10618ced22 | ||
|
|
7f26cfca91 | ||
|
|
b89a597029 | ||
|
|
746044b6c2 | ||
|
|
bdfd288eec | ||
|
|
7e01531e50 | ||
|
|
3ffceff3ed | ||
|
|
75422c16bf | ||
|
|
e66667e465 | ||
|
|
7d6208e0af | ||
|
|
f498b660da | ||
|
|
e606d99dc8 | ||
|
|
6aba1b4c49 | ||
|
|
6ee9433f29 | ||
|
|
ffe663aedf | ||
|
|
2a105df9f2 | ||
|
|
9c731f1883 | ||
|
|
5a4e9125de | ||
|
|
9db1367c2d | ||
|
|
8258ce14f4 | ||
|
|
ac573cf830 | ||
|
|
e799c0fb7b | ||
|
|
73c5533e66 | ||
|
|
3b1f747f96 | ||
|
|
9e9827d236 | ||
|
|
a76d508424 | ||
|
|
c361a06bd4 | ||
|
|
3428543bb8 | ||
|
|
9cdbc80522 | ||
|
|
6775658ed5 | ||
|
|
7df7f7a575 | ||
|
|
7daae1912b | ||
|
|
3205f68510 | ||
|
|
898dcfac8b | ||
|
|
b1efa19f97 | ||
|
|
b45fd70f99 | ||
|
|
7f6c7a19c6 | ||
|
|
142462d539 | ||
|
|
f881784e9b | ||
|
|
5af625903f | ||
|
|
dc94f305cc | ||
|
|
8060a49c6c | ||
|
|
4d3e0d88a2 | ||
|
|
253ce4837a | ||
|
|
ad05eb8222 | ||
|
|
21393c244c | ||
|
|
4279e6ef45 | ||
|
|
3db6dd752f | ||
|
|
fcbe68eeb5 | ||
|
|
5019f38e29 | ||
|
|
9bf1247716 | ||
|
|
2fd31f6ea6 | ||
|
|
9cf7bba8f0 | ||
|
|
2e257d1cf7 | ||
|
|
980a15d847 | ||
|
|
56831d7799 | ||
|
|
6d65ae5ba6 | ||
|
|
c919b4a573 | ||
|
|
fe6f392c2d | ||
|
|
bffb71d4c8 | ||
|
|
3b34a537ee | ||
|
|
ad79ce9c4b | ||
|
|
721f6388c3 | ||
|
|
402ec83157 | ||
|
|
298ac11018 | ||
|
|
bb6e207336 | ||
|
|
f433b7c7cf | ||
|
|
a94278abd1 | ||
|
|
f9ec70edd0 | ||
|
|
9e5a758e7c | ||
|
|
492e933796 | ||
|
|
8ccd9d0eb5 | ||
|
|
16fdc11ccb | ||
|
|
a7cd5a2553 | ||
|
|
9e6b881f85 | ||
|
|
95fa49147b | ||
|
|
f665c57c5c | ||
|
|
9024d24e81 | ||
|
|
f92a7ad0a3 | ||
|
|
db4448dda8 | ||
|
|
0dc5836d5e | ||
|
|
8751d7ecf8 | ||
|
|
c21226aa7c | ||
|
|
3e358458f4 | ||
|
|
766b3aecf7 | ||
|
|
8ab96ab80d | ||
|
|
1f2e00ef8d | ||
|
|
b49453cf0d | ||
|
|
faffcb889c | ||
|
|
311e83e591 | ||
|
|
3c0ec59432 | ||
|
|
d4a2843500 | ||
|
|
fb2d918056 | ||
|
|
e7ad49bbbe | ||
|
|
ad9a414fae | ||
|
|
c18c2a8e68 | ||
|
|
1d54868c12 | ||
|
|
dfefea5e9d | ||
|
|
3fbab91231 | ||
|
|
f7e73e2da0 | ||
|
|
867728b5ab | ||
|
|
87e02c30e7 | ||
|
|
c3470c9c96 | ||
|
|
7f049164b7 | ||
|
|
4e12a72873 | ||
|
|
91e0c27252 | ||
|
|
db4a061ed6 | ||
|
|
f6bbeafd26 | ||
|
|
f14e39d451 | ||
|
|
084f5d891b | ||
|
|
b0f72e13d9 | ||
|
|
8d7d80ef9d | ||
|
|
cf5de082b5 | ||
|
|
1944451082 | ||
|
|
602e5a8200 | ||
|
|
83b8b7acb7 | ||
|
|
e660f19507 | ||
|
|
ff412b927d | ||
|
|
392ef1eb06 | ||
|
|
dcecdc9be6 | ||
|
|
ed69b68892 | ||
|
|
42b982e13c | ||
|
|
e7e2592357 | ||
|
|
739586f96a | ||
|
|
4c0f1f53d3 | ||
|
|
359f12791a | ||
|
|
9354ab62dd | ||
|
|
23ff74bb3f | ||
|
|
98d17e2293 | ||
|
|
ababa6ae5b | ||
|
|
097cd0c242 | ||
|
|
b91cd66fc5 | ||
|
|
787d630157 | ||
|
|
1f938c560a | ||
|
|
a96924a555 | ||
|
|
33dc6629ff | ||
|
|
1b3fb0af8c | ||
|
|
12da523ff7 | ||
|
|
0f49d80623 | ||
|
|
1717516a78 | ||
|
|
328c6d3060 | ||
|
|
566720be15 | ||
|
|
65f13c3cc6 | ||
|
|
31b2e2d7b4 | ||
|
|
8fe8d74056 | ||
|
|
fcc4742056 | ||
|
|
d98e2e7498 | ||
|
|
d37ffa1149 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
coverage.html
|
||||
coverage/
|
||||
.DS_Store
|
||||
lib-cov
|
||||
*.seed
|
||||
*.log
|
||||
*.csv
|
||||
@@ -13,7 +12,5 @@ benchmarks/graphs
|
||||
testing
|
||||
node_modules/
|
||||
testing
|
||||
.coverage_data
|
||||
cover_html
|
||||
test.js
|
||||
.idea
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.git*
|
||||
benchmarks/
|
||||
coverage/
|
||||
docs/
|
||||
examples/
|
||||
support/
|
||||
@@ -7,5 +8,4 @@ test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
.travis.yml
|
||||
coverage.html
|
||||
lib-cov
|
||||
Contributing.md
|
||||
|
||||
@@ -6,3 +6,5 @@ matrix:
|
||||
allow_failures:
|
||||
- node_js: "0.11"
|
||||
fast_finish: true
|
||||
script: "npm run-script test-travis"
|
||||
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
|
||||
|
||||
25
Contributing.md
Normal file
25
Contributing.md
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
## Website Issues
|
||||
|
||||
Issues for the expressjs.com website go here https://github.com/visionmedia/expressjs.com
|
||||
|
||||
## PRs and Code contributions
|
||||
|
||||
* Tests must pass.
|
||||
* Follow existing coding style.
|
||||
* If you fix a bug, add a test.
|
||||
|
||||
|
||||
## Issues which are questions
|
||||
|
||||
We will typically close any vague issues or questions that are specific to some app you are writing. Please double check the docs and other references before being trigger happy with posting a question issue.
|
||||
|
||||
Things that will help get your question issue looked at:
|
||||
|
||||
* Full and runnable JS code.
|
||||
* Clear description of the problem or unexpected behavior.
|
||||
* Clear description of the expected result.
|
||||
* Steps you have taken to debug it yourself.
|
||||
|
||||
If you post a question and do not outline the above items or make it easy for us to understand and reproduce your issue, it will be closed.
|
||||
|
||||
330
History.md
330
History.md
@@ -1,3 +1,141 @@
|
||||
4.5.0 / 2014-07-04
|
||||
==================
|
||||
|
||||
* add deprecation message to non-plural `req.accepts*`
|
||||
* add deprecation message to `res.send(body, status)`
|
||||
* add deprecation message to `res.vary()`
|
||||
* add `headers` option to `res.sendfile`
|
||||
- use to set headers on successful file transfer
|
||||
* add `mergeParams` option to `Router`
|
||||
- merges `req.params` from parent routes
|
||||
* add `req.hostname` -- correct name for what `req.host` returns
|
||||
* deprecate things with `depd` module
|
||||
* deprecate `req.host` -- use `req.hostname` instead
|
||||
* fix behavior when handling request without routes
|
||||
* fix handling when `route.all` is only route
|
||||
* invoke `router.param()` only when route matches
|
||||
* restore `req.params` after invoking router
|
||||
* use `finalhandler` for final response handling
|
||||
* use `media-typer` to alter content-type charset
|
||||
* deps: accepts@~1.0.7
|
||||
* deps: send@0.5.0
|
||||
- Accept string for `maxage` (converted by `ms`)
|
||||
- Include link in default redirect response
|
||||
* deps: serve-static@~1.3.0
|
||||
- Accept string for `maxAge` (converted by `ms`)
|
||||
- Add `setHeaders` option
|
||||
- Include HTML link in redirect response
|
||||
- deps: send@0.5.0
|
||||
* deps: type-is@~1.3.2
|
||||
|
||||
4.4.5 / 2014-06-26
|
||||
==================
|
||||
|
||||
* deps: cookie-signature@1.0.4
|
||||
- fix for timing attacks
|
||||
|
||||
4.4.4 / 2014-06-20
|
||||
==================
|
||||
|
||||
* fix `res.attachment` Unicode filenames in Safari
|
||||
* fix "trim prefix" debug message in `express:router`
|
||||
* deps: accepts@~1.0.5
|
||||
* deps: buffer-crc32@0.2.3
|
||||
|
||||
4.4.3 / 2014-06-11
|
||||
==================
|
||||
|
||||
* fix persistence of modified `req.params[name]` from `app.param()`
|
||||
* deps: accepts@1.0.3
|
||||
- deps: negotiator@0.4.6
|
||||
* deps: debug@1.0.2
|
||||
* deps: send@0.4.3
|
||||
- Do not throw un-catchable error on file open race condition
|
||||
- Use `escape-html` for HTML escaping
|
||||
- deps: debug@1.0.2
|
||||
- deps: finished@1.2.2
|
||||
- deps: fresh@0.2.2
|
||||
* deps: serve-static@1.2.3
|
||||
- Do not throw un-catchable error on file open race condition
|
||||
- deps: send@0.4.3
|
||||
|
||||
4.4.2 / 2014-06-09
|
||||
==================
|
||||
|
||||
* fix catching errors from top-level handlers
|
||||
* use `vary` module for `res.vary`
|
||||
* deps: debug@1.0.1
|
||||
* deps: proxy-addr@1.0.1
|
||||
* deps: send@0.4.2
|
||||
- fix "event emitter leak" warnings
|
||||
- deps: debug@1.0.1
|
||||
- deps: finished@1.2.1
|
||||
* deps: serve-static@1.2.2
|
||||
- fix "event emitter leak" warnings
|
||||
- deps: send@0.4.2
|
||||
* deps: type-is@1.2.1
|
||||
|
||||
4.4.1 / 2014-06-02
|
||||
==================
|
||||
|
||||
* deps: methods@1.0.1
|
||||
* deps: send@0.4.1
|
||||
- Send `max-age` in `Cache-Control` in correct format
|
||||
* deps: serve-static@1.2.1
|
||||
- use `escape-html` for escaping
|
||||
- deps: send@0.4.1
|
||||
|
||||
4.4.0 / 2014-05-30
|
||||
==================
|
||||
|
||||
* custom etag control with `app.set('etag', val)`
|
||||
- `app.set('etag', function(body, encoding){ return '"etag"' })` custom etag generation
|
||||
- `app.set('etag', 'weak')` weak tag
|
||||
- `app.set('etag', 'strong')` strong etag
|
||||
- `app.set('etag', false)` turn off
|
||||
- `app.set('etag', true)` standard etag
|
||||
* mark `res.send` ETag as weak and reduce collisions
|
||||
* update accepts to 1.0.2
|
||||
- Fix interpretation when header not in request
|
||||
* update send to 0.4.0
|
||||
- Calculate ETag with md5 for reduced collisions
|
||||
- Ignore stream errors after request ends
|
||||
- deps: debug@0.8.1
|
||||
* update serve-static to 1.2.0
|
||||
- Calculate ETag with md5 for reduced collisions
|
||||
- Ignore stream errors after request ends
|
||||
- deps: send@0.4.0
|
||||
|
||||
4.3.2 / 2014-05-28
|
||||
==================
|
||||
|
||||
* fix handling of errors from `router.param()` callbacks
|
||||
|
||||
4.3.1 / 2014-05-23
|
||||
==================
|
||||
|
||||
* revert "fix behavior of multiple `app.VERB` for the same path"
|
||||
- this caused a regression in the order of route execution
|
||||
|
||||
4.3.0 / 2014-05-21
|
||||
==================
|
||||
|
||||
* add `req.baseUrl` to access the path stripped from `req.url` in routes
|
||||
* fix behavior of multiple `app.VERB` for the same path
|
||||
* fix issue routing requests among sub routers
|
||||
* invoke `router.param()` only when necessary instead of every match
|
||||
* proper proxy trust with `app.set('trust proxy', trust)`
|
||||
- `app.set('trust proxy', 1)` trust first hop
|
||||
- `app.set('trust proxy', 'loopback')` trust loopback addresses
|
||||
- `app.set('trust proxy', '10.0.0.1')` trust single IP
|
||||
- `app.set('trust proxy', '10.0.0.1/16')` trust subnet
|
||||
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
|
||||
- `app.set('trust proxy', false)` turn off
|
||||
- `app.set('trust proxy', true)` trust everything
|
||||
* set proper `charset` in `Content-Type` for `res.send`
|
||||
* update type-is to 1.2.0
|
||||
- support suffix matching
|
||||
|
||||
4.2.0 / 2014-05-11
|
||||
==================
|
||||
|
||||
@@ -94,6 +232,198 @@
|
||||
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
|
||||
- Router & Route - public API
|
||||
|
||||
3.13.0 / 2014-07-03
|
||||
===================
|
||||
|
||||
* add deprecation message to `app.configure`
|
||||
* add deprecation message to `req.auth`
|
||||
* use `basic-auth` to parse `Authorization` header
|
||||
* deps: connect@2.22.0
|
||||
- deps: csurf@~1.3.0
|
||||
- deps: express-session@~1.6.1
|
||||
- deps: multiparty@3.3.0
|
||||
- deps: serve-static@~1.3.0
|
||||
* deps: send@0.5.0
|
||||
- Accept string for `maxage` (converted by `ms`)
|
||||
- Include link in default redirect response
|
||||
|
||||
3.12.1 / 2014-06-26
|
||||
===================
|
||||
|
||||
* deps: connect@2.21.1
|
||||
- deps: cookie-parser@1.3.2
|
||||
- deps: cookie-signature@1.0.4
|
||||
- deps: express-session@~1.5.2
|
||||
- deps: type-is@~1.3.2
|
||||
* deps: cookie-signature@1.0.4
|
||||
- fix for timing attacks
|
||||
|
||||
3.12.0 / 2014-06-21
|
||||
===================
|
||||
|
||||
* use `media-typer` to alter content-type charset
|
||||
* deps: connect@2.21.0
|
||||
- deprecate `connect(middleware)` -- use `app.use(middleware)` instead
|
||||
- deprecate `connect.createServer()` -- use `connect()` instead
|
||||
- fix `res.setHeader()` patch to work with with get -> append -> set pattern
|
||||
- deps: compression@~1.0.8
|
||||
- deps: errorhandler@~1.1.1
|
||||
- deps: express-session@~1.5.0
|
||||
- deps: serve-index@~1.1.3
|
||||
|
||||
3.11.0 / 2014-06-19
|
||||
===================
|
||||
|
||||
* deprecate things with `depd` module
|
||||
* deps: buffer-crc32@0.2.3
|
||||
* deps: connect@2.20.2
|
||||
- deprecate `verify` option to `json` -- use `body-parser` module directly
|
||||
- deprecate `verify` option to `urlencoded` -- use `body-parser` module directly
|
||||
- deprecate things with `depd` module
|
||||
- use `finalhandler` for final response handling
|
||||
- use `media-typer` to parse `content-type` for charset
|
||||
- deps: body-parser@1.4.3
|
||||
- deps: connect-timeout@1.1.1
|
||||
- deps: cookie-parser@1.3.1
|
||||
- deps: csurf@1.2.2
|
||||
- deps: errorhandler@1.1.0
|
||||
- deps: express-session@1.4.0
|
||||
- deps: multiparty@3.2.9
|
||||
- deps: serve-index@1.1.2
|
||||
- deps: type-is@1.3.1
|
||||
- deps: vhost@2.0.0
|
||||
|
||||
3.10.5 / 2014-06-11
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.6
|
||||
- deps: body-parser@1.3.1
|
||||
- deps: compression@1.0.7
|
||||
- deps: debug@1.0.2
|
||||
- deps: serve-index@1.1.1
|
||||
- deps: serve-static@1.2.3
|
||||
* deps: debug@1.0.2
|
||||
* deps: send@0.4.3
|
||||
- Do not throw un-catchable error on file open race condition
|
||||
- Use `escape-html` for HTML escaping
|
||||
- deps: debug@1.0.2
|
||||
- deps: finished@1.2.2
|
||||
- deps: fresh@0.2.2
|
||||
|
||||
3.10.4 / 2014-06-09
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.5
|
||||
- fix "event emitter leak" warnings
|
||||
- deps: csurf@1.2.1
|
||||
- deps: debug@1.0.1
|
||||
- deps: serve-static@1.2.2
|
||||
- deps: type-is@1.2.1
|
||||
* deps: debug@1.0.1
|
||||
* deps: send@0.4.2
|
||||
- fix "event emitter leak" warnings
|
||||
- deps: finished@1.2.1
|
||||
- deps: debug@1.0.1
|
||||
|
||||
3.10.3 / 2014-06-05
|
||||
===================
|
||||
|
||||
* use `vary` module for `res.vary`
|
||||
* deps: connect@2.19.4
|
||||
- deps: errorhandler@1.0.2
|
||||
- deps: method-override@2.0.2
|
||||
- deps: serve-favicon@2.0.1
|
||||
* deps: debug@1.0.0
|
||||
|
||||
3.10.2 / 2014-06-03
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.3
|
||||
- deps: compression@1.0.6
|
||||
|
||||
3.10.1 / 2014-06-03
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.2
|
||||
- deps: compression@1.0.4
|
||||
* deps: proxy-addr@1.0.1
|
||||
|
||||
3.10.0 / 2014-06-02
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.1
|
||||
- deprecate `methodOverride()` -- use `method-override` module directly
|
||||
- deps: body-parser@1.3.0
|
||||
- deps: method-override@2.0.1
|
||||
- deps: multiparty@3.2.8
|
||||
- deps: response-time@2.0.0
|
||||
- deps: serve-static@1.2.1
|
||||
* deps: methods@1.0.1
|
||||
* deps: send@0.4.1
|
||||
- Send `max-age` in `Cache-Control` in correct format
|
||||
|
||||
3.9.0 / 2014-05-30
|
||||
==================
|
||||
|
||||
* custom etag control with `app.set('etag', val)`
|
||||
- `app.set('etag', function(body, encoding){ return '"etag"' })` custom etag generation
|
||||
- `app.set('etag', 'weak')` weak tag
|
||||
- `app.set('etag', 'strong')` strong etag
|
||||
- `app.set('etag', false)` turn off
|
||||
- `app.set('etag', true)` standard etag
|
||||
* Include ETag in HEAD requests
|
||||
* mark `res.send` ETag as weak and reduce collisions
|
||||
* update connect to 2.18.0
|
||||
- deps: compression@1.0.3
|
||||
- deps: serve-index@1.1.0
|
||||
- deps: serve-static@1.2.0
|
||||
* update send to 0.4.0
|
||||
- Calculate ETag with md5 for reduced collisions
|
||||
- Ignore stream errors after request ends
|
||||
- deps: debug@0.8.1
|
||||
|
||||
3.8.1 / 2014-05-27
|
||||
==================
|
||||
|
||||
* update connect to 2.17.3
|
||||
- deps: body-parser@1.2.2
|
||||
- deps: express-session@1.2.1
|
||||
- deps: method-override@1.0.2
|
||||
|
||||
3.8.0 / 2014-05-21
|
||||
==================
|
||||
|
||||
* keep previous `Content-Type` for `res.jsonp`
|
||||
* set proper `charset` in `Content-Type` for `res.send`
|
||||
* update connect to 2.17.1
|
||||
- fix `res.charset` appending charset when `content-type` has one
|
||||
- deps: express-session@1.2.0
|
||||
- deps: morgan@1.1.1
|
||||
- deps: serve-index@1.0.3
|
||||
|
||||
3.7.0 / 2014-05-18
|
||||
==================
|
||||
|
||||
* proper proxy trust with `app.set('trust proxy', trust)`
|
||||
- `app.set('trust proxy', 1)` trust first hop
|
||||
- `app.set('trust proxy', 'loopback')` trust loopback addresses
|
||||
- `app.set('trust proxy', '10.0.0.1')` trust single IP
|
||||
- `app.set('trust proxy', '10.0.0.1/16')` trust subnet
|
||||
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
|
||||
- `app.set('trust proxy', false)` turn off
|
||||
- `app.set('trust proxy', true)` trust everything
|
||||
* update connect to 2.16.2
|
||||
- deprecate `res.headerSent` -- use `res.headersSent`
|
||||
- deprecate `res.on("header")` -- use on-headers module instead
|
||||
- fix edge-case in `res.appendHeader` that would append in wrong order
|
||||
- json: use body-parser
|
||||
- urlencoded: use body-parser
|
||||
- dep: bytes@1.0.0
|
||||
- dep: cookie-parser@1.1.0
|
||||
- dep: csurf@1.2.0
|
||||
- dep: express-session@1.1.0
|
||||
- dep: method-override@1.0.1
|
||||
|
||||
3.6.0 / 2014-05-09
|
||||
==================
|
||||
|
||||
|
||||
34
Makefile
34
Makefile
@@ -1,34 +0,0 @@
|
||||
|
||||
MOCHA_OPTS= --check-leaks
|
||||
REPORTER = dot
|
||||
|
||||
check: test
|
||||
|
||||
test: test-unit test-acceptance
|
||||
|
||||
test-unit:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
--globals setImmediate,clearImmediate \
|
||||
$(MOCHA_OPTS)
|
||||
|
||||
test-acceptance:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
--bail \
|
||||
test/acceptance/*.js
|
||||
|
||||
test-cov: lib-cov
|
||||
@EXPRESS_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
|
||||
|
||||
lib-cov:
|
||||
@jscoverage lib lib-cov
|
||||
|
||||
bench:
|
||||
@$(MAKE) -C benchmarks
|
||||
|
||||
clean:
|
||||
rm -f coverage.html
|
||||
rm -fr lib-cov
|
||||
|
||||
.PHONY: test test-unit test-acceptance bench clean
|
||||
113
Readme.md
113
Readme.md
@@ -1,45 +1,58 @@
|
||||
[](http://expressjs.com/)
|
||||
[](https://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
Fast, unopinionated, minimalist web framework for [node](https://nodejs.org).
|
||||
|
||||
[](https://travis-ci.org/visionmedia/express) [](https://www.gittip.com/visionmedia/) [](http://badge.fury.io/js/express)
|
||||
[](https://badge.fury.io/js/express)
|
||||
[](https://travis-ci.org/visionmedia/express)
|
||||
[](https://coveralls.io/r/visionmedia/express)
|
||||
[](https://www.gittip.com/visionmedia/)
|
||||
|
||||
```js
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var express = require('express')
|
||||
var app = express()
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
app.get('/', function (req, res) {
|
||||
res.send('Hello World')
|
||||
})
|
||||
|
||||
app.listen(3000);
|
||||
app.listen(3000)
|
||||
```
|
||||
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/visionmedia/express/wiki/New-features-in-4.x).
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/visionmedia/express/wiki/New-features-in-4.x).
|
||||
|
||||
## Installation
|
||||
### Installation
|
||||
|
||||
$ npm install express
|
||||
```bash
|
||||
$ npm install express
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable [`express(1)`](http://github.com/expressjs/generator) to generate an application as shown below:
|
||||
|
||||
Install the executable. The executable's major version will match Express's:
|
||||
|
||||
$ npm install -g express-generator@3
|
||||
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:
|
||||
|
||||
Create the app:
|
||||
Install the executable. The executable's major version will match Express's:
|
||||
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
```bash
|
||||
$ npm install -g express-generator@4
|
||||
```
|
||||
|
||||
Install dependencies:
|
||||
Create the app:
|
||||
|
||||
$ npm install
|
||||
```bash
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
```
|
||||
|
||||
Start the server:
|
||||
Install dependencies:
|
||||
|
||||
$ npm start
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Start the server:
|
||||
|
||||
```bash
|
||||
$ npm start
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
@@ -58,51 +71,57 @@ app.listen(3000);
|
||||
HTTP APIs.
|
||||
|
||||
Express does not force you to use any specific ORM or template engine. With support for over
|
||||
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js),
|
||||
14 template engines via [Consolidate.js](https://github.com/visionmedia/consolidate.js),
|
||||
you can quickly craft your perfect framework.
|
||||
|
||||
## More Information
|
||||
|
||||
* [Website and Documentation](http://expressjs.com/) stored at [visionmedia/expressjs.com](https://github.com/visionmedia/expressjs.com)
|
||||
* Join #express on freenode
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) and [defunctzombie](https://twitter.com/defunctzombie) on twitter for updates
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/visionmedia/expressjs.com)]
|
||||
* [Github Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
|
||||
* Visit the [Wiki](https://github.com/visionmedia/express/wiki)
|
||||
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
* Run express examples [online](https://runnable.com/express)
|
||||
|
||||
## Viewing Examples
|
||||
|
||||
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
|
||||
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
|
||||
|
||||
$ git clone git://github.com/visionmedia/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
```bash
|
||||
$ git clone git://github.com/visionmedia/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Then run whichever tests you want:
|
||||
Then run whichever example you want:
|
||||
|
||||
$ node examples/content-negotiation
|
||||
|
||||
You can also view live examples here:
|
||||
You can also view live examples here:
|
||||
|
||||
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
|
||||
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
|
||||
|
||||
## Running Tests
|
||||
|
||||
To run the test suite, first invoke the following command within the repo, installing the development dependencies:
|
||||
To run the test suite, first invoke the following command within the repo, installing the development dependencies:
|
||||
|
||||
$ npm install
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Then run the tests:
|
||||
Then run the tests:
|
||||
|
||||
$ make test
|
||||
```bash
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## Contributors
|
||||
|
||||
Author: [TJ Holowaychuk](http://github.com/visionmedia)
|
||||
Lead Maintainer: [Roman Shtylman](https://github.com/defunctzombie)
|
||||
Contributors: https://github.com/visionmedia/express/graphs/contributors
|
||||
### Contributors
|
||||
|
||||
## License
|
||||
* Author: [TJ Holowaychuk](https://github.com/visionmedia)
|
||||
* Lead Maintainer: [Douglas Christopher Wilson](https://github.com/dougwilson)
|
||||
* [All Contributors](https://github.com/visionmedia/express/graphs/contributors)
|
||||
|
||||
MIT
|
||||
### License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
var express = require('../..');
|
||||
var hash = require('./pass').hash;
|
||||
var bodyParser = require('body-parser');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var session = require('express-session');
|
||||
|
||||
var app = module.exports = express();
|
||||
@@ -17,9 +16,12 @@ app.set('views', __dirname + '/views');
|
||||
|
||||
// middleware
|
||||
|
||||
app.use(bodyParser());
|
||||
app.use(cookieParser('shhhh, very secret'));
|
||||
app.use(session());
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'shhhh, very secret'
|
||||
}));
|
||||
|
||||
// Session-persisted message middleware
|
||||
|
||||
@@ -78,7 +80,7 @@ function restrict(req, res, next) {
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('login');
|
||||
res.redirect('/login');
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, function(req, res){
|
||||
@@ -116,11 +118,12 @@ app.post('/login', function(req, res){
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
+ ' username and password.'
|
||||
+ ' (use "tj" and "foobar")';
|
||||
res.redirect('login');
|
||||
res.redirect('/login');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
|
||||
@@ -24,5 +24,8 @@ app.get('/', function(req, res){
|
||||
res.render('pets', { pets: pets });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ function format(path) {
|
||||
|
||||
app.get('/users', format('./users'));
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -3,15 +3,11 @@
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var cookie-parser = require('cookie-parser');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// pass a secret to cookieParser() for signed cookies
|
||||
app.use(cookieParser('manny is cool'));
|
||||
|
||||
// add req.session cookie support
|
||||
app.use(cookieSession());
|
||||
app.use(cookieSession({ secret: 'manny is cool' }));
|
||||
|
||||
// do something with the session
|
||||
app.use(count);
|
||||
@@ -23,7 +19,8 @@ function count(req, res) {
|
||||
res.send('viewed ' + n + ' times\n');
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -9,8 +9,7 @@ var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// custom log format
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
app.use(logger(':method :url'));
|
||||
if ('test' != process.env.NODE_ENV) app.use(logger(':method :url'));
|
||||
|
||||
// parses request cookies, populating
|
||||
// req.cookies and req.signedCookies
|
||||
@@ -18,8 +17,8 @@ if ('test' != process.env.NODE_ENV)
|
||||
// for signing the cookies.
|
||||
app.use(cookieParser('my secret here'));
|
||||
|
||||
// parses json, x-www-form-urlencoded, and multipart/form-data
|
||||
app.use(bodyParser());
|
||||
// parses x-www-form-urlencoded
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
@@ -42,7 +41,8 @@ app.post('/', function(req, res){
|
||||
res.redirect('back');
|
||||
});
|
||||
|
||||
if (!module.parent){
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ app.use(express.static(__dirname + '/public'));
|
||||
// api middleware
|
||||
|
||||
api.use(logger('dev'));
|
||||
api.use(bodyParser());
|
||||
api.use(bodyParser.json());
|
||||
|
||||
/**
|
||||
* CORS support.
|
||||
@@ -25,7 +25,7 @@ api.all('*', function(req, res, next){
|
||||
if (!req.get('Origin')) return next();
|
||||
// use "*" here to accept any origin
|
||||
res.set('Access-Control-Allow-Origin', 'http://localhost:3000');
|
||||
res.set('Access-Control-Allow-Methods', 'GET, POST');
|
||||
res.set('Access-Control-Allow-Methods', 'PUT');
|
||||
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
|
||||
// res.set('Access-Control-Allow-Max-Age', 3600);
|
||||
if ('OPTIONS' == req.method) return res.send(200);
|
||||
@@ -33,12 +33,12 @@ api.all('*', function(req, res, next){
|
||||
});
|
||||
|
||||
/**
|
||||
* POST a user.
|
||||
* PUT an existing user.
|
||||
*/
|
||||
|
||||
api.post('/user', function(req, res){
|
||||
api.put('/user/:id', function(req, res){
|
||||
console.log(req.body);
|
||||
res.send(201);
|
||||
res.send(204);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<body>
|
||||
<script>
|
||||
var req = new XMLHttpRequest;
|
||||
req.open('POST', 'http://localhost:3001/user', false);
|
||||
req.open('PUT', 'http://localhost:3001/user/1', false);
|
||||
req.setRequestHeader('Content-Type', 'application/json');
|
||||
req.send('{"name":"tobi","species":"ferret"}');
|
||||
console.log(req.responseText);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -10,6 +10,7 @@ app.get('/', function(req, res){
|
||||
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/utf-8 한中日.txt">utf-8 한中日.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/CCTV大赛上海分赛区.txt">CCTV大赛上海分赛区.txt</a>.</li>'
|
||||
+ '</ul>');
|
||||
});
|
||||
|
||||
@@ -19,25 +20,16 @@ app.get('/files/:file(*)', function(req, res, next){
|
||||
var file = req.params.file;
|
||||
var path = __dirname + '/files/' + file;
|
||||
|
||||
res.download(path);
|
||||
});
|
||||
|
||||
// error handling middleware. Because it's
|
||||
// below our routes, you will be able to
|
||||
// "intercept" errors, otherwise Connect
|
||||
// will respond with 500 "Internal Server Error".
|
||||
app.use(function(err, req, res, next){
|
||||
// special-case 404s,
|
||||
// remember you could
|
||||
// render a 404 template here
|
||||
if (404 == err.status) {
|
||||
res.download(path, function(err){
|
||||
if (!err) return; // file sent
|
||||
if (err && err.status !== 404) return next(err); // non-404 error
|
||||
// file for download not found
|
||||
res.statusCode = 404;
|
||||
res.send('Cant find that file, sorry!');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
|
||||
2
examples/downloads/files/CCTV大赛上海分赛区.txt
Normal file
2
examples/downloads/files/CCTV大赛上海分赛区.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Only for test.
|
||||
The file name is faked.
|
||||
@@ -43,7 +43,8 @@ app.get('/', function(req, res){
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ app.enable('verbose errors');
|
||||
|
||||
// disable them in production
|
||||
// use $ NODE_ENV=production node examples/error-pages
|
||||
if ('production' == app.settings.env) {
|
||||
app.disable('verbose errors');
|
||||
}
|
||||
if ('production' == app.settings.env) app.disable('verbose errors');
|
||||
|
||||
silent || app.use(logger('dev'));
|
||||
|
||||
@@ -99,7 +97,8 @@ app.use(function(err, req, res, next){
|
||||
});
|
||||
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
silent || console.log('Express started on port 3000');
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ app.get('/next', function(req, res, next){
|
||||
// from app.get() etc
|
||||
app.use(error);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
|
||||
@@ -57,5 +57,8 @@ app.get('/user', function(req, res){
|
||||
res.render('page');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('app listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -6,5 +6,8 @@ app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -44,5 +44,8 @@ app.use(function(err, req, res, next) {
|
||||
res.send(err.stack);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ app.get('/fail', function(req, res){
|
||||
res.render('missing', { title: 'Markdown Example' });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
|
||||
@@ -53,7 +53,8 @@ app.post('/', function(req, res, next){
|
||||
form.parse(req);
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(4000);
|
||||
console.log('Express started on port 3000');
|
||||
console.log('Express started on port 4000');
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ var db = require('../../db');
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var pet = db.pets[req.params.pet_id];
|
||||
if (!pet) return next(new Error('Pet not found'));
|
||||
if (!pet) return next('route');
|
||||
req.pet = pet;
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= pet.name
|
||||
form(action='/pet/#{pet.id}', method='post')
|
||||
input(type='hidden', name='_method', value='put')
|
||||
form(action='/pet/#{pet.id}?_method=put', method='post')
|
||||
label= 'Name: '
|
||||
input(type='text', name='pet[name]', value=pet.name)
|
||||
input(type='submit', value='Update')
|
||||
|
||||
@@ -11,7 +11,7 @@ exports.create = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
var user = db.users[id];
|
||||
var body = req.body;
|
||||
if (!user) return next(new Error('User not found'));
|
||||
if (!user) return next('route');
|
||||
var pet = { name: body.pet.name };
|
||||
pet.id = db.pets.push(pet) - 1;
|
||||
user.pets.push(pet);
|
||||
|
||||
@@ -11,7 +11,7 @@ exports.before = function(req, res, next){
|
||||
process.nextTick(function(){
|
||||
req.user = db.users[id];
|
||||
// cant find that user
|
||||
if (!req.user) return next(new Error('User not found'));
|
||||
if (!req.user) return next('route');
|
||||
// found it, move on to the routes
|
||||
next();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= user.name
|
||||
form(action='/user/#{user.id}', method='post')
|
||||
input(type='hidden', name='_method', value='put')
|
||||
form(action='/user/#{user.id}?_method=put', method='post')
|
||||
label= 'Name: '
|
||||
input(type='text', name='user[name]', value='#{user.name}')
|
||||
input(type='submit', value='Update')
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var session = require('express-session');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
|
||||
@@ -38,14 +37,17 @@ if (!module.parent) app.use(logger('dev'));
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// session support
|
||||
app.use(cookieParser('some secret here'));
|
||||
app.use(session());
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'some secret here'
|
||||
}));
|
||||
|
||||
// parse request bodies (req.body)
|
||||
app.use(bodyParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
// override methods (put, delete)
|
||||
app.use(methodOverride());
|
||||
// allow overriding methods in query (?_method=put)
|
||||
app.use(methodOverride('_method'));
|
||||
|
||||
// expose the "messages" local variable when views are rendered
|
||||
app.use(function(req, res, next){
|
||||
@@ -73,16 +75,9 @@ app.use(function(req, res, next){
|
||||
// load controllers
|
||||
require('./lib/boot')(app, { verbose: !module.parent });
|
||||
|
||||
// assume "not found" in the error msgs
|
||||
// is a 404. this is somewhat silly, but
|
||||
// valid, you can do whatever you like, set
|
||||
// properties, use instanceof etc.
|
||||
app.use(function(err, req, res, next){
|
||||
// treat as 404
|
||||
if (~err.message.indexOf('not found')) return next();
|
||||
|
||||
// log it
|
||||
console.error(err.stack);
|
||||
if (!module.parent) console.error(err.stack);
|
||||
|
||||
// error page
|
||||
res.status(500).render('5xx');
|
||||
@@ -93,7 +88,8 @@ app.use(function(req, res, next){
|
||||
res.status(404).render('404', { url: req.originalUrl });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('\n listening on port 3000\n');
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ module.exports = function(parent, options){
|
||||
var name = obj.name || name;
|
||||
var prefix = obj.prefix || '';
|
||||
var app = express();
|
||||
var handler;
|
||||
var method;
|
||||
var path;
|
||||
|
||||
@@ -20,16 +21,6 @@ module.exports = function(parent, options){
|
||||
if (obj.engine) app.set('view engine', obj.engine);
|
||||
app.set('views', __dirname + '/../controllers/' + name + '/views');
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
app.all(path, obj.before);
|
||||
verbose && console.log(' ALL %s -> before', path);
|
||||
path = '/' + name + '/:' + name + '_id/*';
|
||||
app.all(path, obj.before);
|
||||
verbose && console.log(' ALL %s -> before', path);
|
||||
}
|
||||
|
||||
// generate routes based
|
||||
// on the exported methods
|
||||
for (var key in obj) {
|
||||
@@ -62,15 +53,25 @@ module.exports = function(parent, options){
|
||||
path = '/';
|
||||
break;
|
||||
default:
|
||||
/* istanbul ignore next */
|
||||
throw new Error('unrecognized route: ' + name + '.' + key);
|
||||
}
|
||||
|
||||
// setup
|
||||
handler = obj[key];
|
||||
path = prefix + path;
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
app[method](path, obj.before, handler);
|
||||
verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), path, key);
|
||||
} else {
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
}
|
||||
}
|
||||
|
||||
// mount the app
|
||||
parent.use(app);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -49,5 +49,8 @@ app.get('/', function(req, res, next){
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ var users = [
|
||||
// Convert :to and :from to integers
|
||||
|
||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
req.params[name] = num = parseInt(num, 10);
|
||||
if( isNaN(num) ){
|
||||
req.params[name] = parseInt(num, 10);
|
||||
if( isNaN(req.params[name]) ){
|
||||
next(new Error('failed to parseInt '+num));
|
||||
} else {
|
||||
next();
|
||||
@@ -63,7 +63,8 @@ app.get('/users/:from-:to', function(req, res, next){
|
||||
res.send('users ' + names.slice(from, to).join(', '));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@ app.resource = function(path, obj) {
|
||||
obj.range(req, res, a, b, format);
|
||||
});
|
||||
this.get(path + '/:id', obj.show);
|
||||
this.delete(path + '/:id', obj.destroy);
|
||||
this.delete(path + '/:id', function(req, res){
|
||||
var id = parseInt(req.params.id, 10);
|
||||
obj.destroy(req, res, id);
|
||||
});
|
||||
};
|
||||
|
||||
// Fake records
|
||||
@@ -40,8 +43,7 @@ var User = {
|
||||
show: function(req, res){
|
||||
res.send(users[req.params.id] || { error: 'Cannot find user' });
|
||||
},
|
||||
destroy: function(req, res){
|
||||
var id = req.params.id;
|
||||
destroy: function(req, res, id){
|
||||
var destroyed = id in users;
|
||||
delete users[id];
|
||||
res.send(destroyed ? 'destroyed' : 'Cannot find user');
|
||||
@@ -84,7 +86,8 @@ app.get('/', function(req, res){
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,4 +65,8 @@ app.map({
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -81,5 +81,8 @@ app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
|
||||
res.send('Deleted user ' + req.user.name);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@ var user = require('./user');
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', __dirname + '/views');
|
||||
app.use(logger('dev'));
|
||||
app.use(methodOverride('_method'));
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// General
|
||||
@@ -37,5 +38,8 @@ app.put('/user/:id/edit', user.update);
|
||||
|
||||
app.get('/posts', post.list);
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ extends ../layout
|
||||
block content
|
||||
h1 Editing #{user.name}
|
||||
#user
|
||||
form(method="post")
|
||||
input(type="hidden", value="put", name="_method")
|
||||
form(action="?_method=put", method="post")
|
||||
p Name:
|
||||
input(type="text", value= user.name, name="user[name]")
|
||||
p Email:
|
||||
|
||||
@@ -57,5 +57,8 @@ app.get('/client.js', function(req, res){
|
||||
res.sendfile(__dirname + '/client.js');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('app listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -3,18 +3,16 @@
|
||||
// $ redis-server
|
||||
|
||||
var express = require('../..');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var session = require('express-session');
|
||||
|
||||
var app = express();
|
||||
|
||||
// Required by session() middleware
|
||||
// pass the secret for signed cookies
|
||||
// (required by session())
|
||||
app.use(cookieParser('keyboard cat'));
|
||||
|
||||
// Populates req.session
|
||||
app.use(session());
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'keyboard cat'
|
||||
}));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
var body = '';
|
||||
@@ -27,5 +25,8 @@ app.get('/', function(req, res){
|
||||
res.send(body + '<p>viewed <strong>' + req.session.views + '</strong> times.</p>');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var session = require('express-session');
|
||||
|
||||
// pass the express to the connect redis module
|
||||
@@ -15,13 +14,13 @@ var app = express();
|
||||
|
||||
app.use(logger('dev'));
|
||||
|
||||
// Required by session() middleware
|
||||
// pass the secret for signed cookies
|
||||
// (required by session())
|
||||
app.use(cookieParser('keyboard cat'));
|
||||
|
||||
// Populates req.session
|
||||
app.use(session({ store: new RedisStore }));
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'keyboard cat',
|
||||
store: new RedisStore
|
||||
}));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
var body = '';
|
||||
|
||||
@@ -18,7 +18,7 @@ edit /etc/hosts:
|
||||
|
||||
var main = express();
|
||||
|
||||
main.use(logger('dev'));
|
||||
if (!module.parent) main.use(logger('dev'));
|
||||
|
||||
main.get('/', function(req, res){
|
||||
res.send('Hello from main app!');
|
||||
@@ -32,17 +32,20 @@ main.get('/:sub', function(req, res){
|
||||
|
||||
var redirect = express();
|
||||
|
||||
redirect.all('*', function(req, res){
|
||||
console.log(req.subdomains);
|
||||
res.redirect('http://example.com:3000/' + req.subdomains[0]);
|
||||
redirect.use(function(req, res){
|
||||
if (!module.parent) console.log(req.vhost);
|
||||
res.redirect('http://example.com:3000/' + req.vhost[0]);
|
||||
});
|
||||
|
||||
// Vhost app
|
||||
|
||||
var app = express();
|
||||
var app = module.exports = express();
|
||||
|
||||
app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app
|
||||
app.use(vhost('example.com', main)); // Serves top level domain via Main server app
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ app.get('/Readme.md', function(req, res){
|
||||
res.render('Readme.md');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
|
||||
@@ -145,5 +145,8 @@ app.all('/api/*', function(req, res, next){
|
||||
|
||||
*/
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Application listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -103,7 +103,8 @@ app.use(function(req, res){
|
||||
res.send(404, { error: "Lame, can't find that" });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
4
index.js
4
index.js
@@ -1,4 +1,2 @@
|
||||
|
||||
module.exports = process.env.EXPRESS_COV
|
||||
? require('./lib-cov/express')
|
||||
: require('./lib/express');
|
||||
module.exports = require('./lib/express');
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var finalhandler = require('finalhandler');
|
||||
var mixin = require('utils-merge');
|
||||
var escapeHtml = require('escape-html');
|
||||
var Router = require('./router');
|
||||
var methods = require('methods');
|
||||
var middleware = require('./middleware/init');
|
||||
@@ -11,7 +11,10 @@ var query = require('./middleware/query');
|
||||
var debug = require('debug')('express:application');
|
||||
var View = require('./view');
|
||||
var http = require('http');
|
||||
var deprecate = require('./utils').deprecate;
|
||||
var compileETag = require('./utils').compileETag;
|
||||
var compileTrust = require('./utils').compileTrust;
|
||||
var deprecate = require('depd')('express');
|
||||
var resolve = require('path').resolve;
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
@@ -45,10 +48,11 @@ app.init = function(){
|
||||
app.defaultConfiguration = function(){
|
||||
// default settings
|
||||
this.enable('x-powered-by');
|
||||
this.enable('etag');
|
||||
this.set('etag', 'weak');
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
this.set('env', env);
|
||||
this.set('subdomain offset', 2);
|
||||
this.set('trust proxy', false);
|
||||
|
||||
debug('booting in %s mode', env);
|
||||
|
||||
@@ -71,7 +75,7 @@ app.defaultConfiguration = function(){
|
||||
|
||||
// default configuration
|
||||
this.set('view', View);
|
||||
this.set('views', process.cwd() + '/views');
|
||||
this.set('views', resolve('views'));
|
||||
this.set('jsonp callback name', 'callback');
|
||||
|
||||
if (env === 'production') {
|
||||
@@ -115,45 +119,22 @@ app.lazyrouter = function() {
|
||||
*/
|
||||
|
||||
app.handle = function(req, res, done) {
|
||||
var env = this.get('env');
|
||||
var router = this._router;
|
||||
|
||||
this._router.handle(req, res, function(err) {
|
||||
if (done) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
// unhandled error
|
||||
if (err) {
|
||||
// default to 500
|
||||
if (res.statusCode < 400) res.statusCode = 500;
|
||||
debug('default %s', res.statusCode);
|
||||
|
||||
// respect err.status
|
||||
if (err.status) res.statusCode = err.status;
|
||||
|
||||
// production gets a basic error message
|
||||
var msg = 'production' == env
|
||||
? http.STATUS_CODES[res.statusCode]
|
||||
: err.stack || err.toString();
|
||||
msg = escapeHtml(msg);
|
||||
|
||||
// log to stderr in a non-test env
|
||||
if ('test' != env) console.error(err.stack || err.toString());
|
||||
if (res.headersSent) return req.socket.destroy();
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Content-Length', Buffer.byteLength(msg));
|
||||
if ('HEAD' == req.method) return res.end();
|
||||
res.end(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 404
|
||||
debug('default 404');
|
||||
res.statusCode = 404;
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
if ('HEAD' == req.method) return res.end();
|
||||
res.end('Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl) + '\n');
|
||||
// final handler
|
||||
done = done || finalhandler(req, res, {
|
||||
env: this.get('env'),
|
||||
onerror: logerror.bind(this)
|
||||
});
|
||||
|
||||
// no routes
|
||||
if (!router) {
|
||||
debug('no routes defined on app');
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
router.handle(req, res, done);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -163,9 +144,6 @@ app.handle = function(req, res, done) {
|
||||
* If the _fn_ parameter is an express app, then it will be
|
||||
* mounted at the _route_ specified.
|
||||
*
|
||||
* @param {String|Function|Server} route
|
||||
* @param {Function|Server} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -303,12 +281,27 @@ app.param = function(name, fn){
|
||||
*/
|
||||
|
||||
app.set = function(setting, val){
|
||||
if (1 == arguments.length) {
|
||||
if (arguments.length === 1) {
|
||||
// app.get(setting)
|
||||
return this.settings[setting];
|
||||
} else {
|
||||
this.settings[setting] = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
// set value
|
||||
this.settings[setting] = val;
|
||||
|
||||
// trigger matched settings
|
||||
switch (setting) {
|
||||
case 'etag':
|
||||
debug('compile etag %s', val);
|
||||
this.set('etag fn', compileETag(val));
|
||||
break;
|
||||
case 'trust proxy':
|
||||
debug('compile trust proxy %s', val);
|
||||
this.set('trust proxy fn', compileTrust(val));
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -433,7 +426,7 @@ app.all = function(path){
|
||||
|
||||
// del -> delete alias
|
||||
|
||||
app.del = deprecate(app.delete, 'app.del: Use app.delete instead');
|
||||
app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');
|
||||
|
||||
/**
|
||||
* Render the given view `name` name with `options`
|
||||
@@ -531,3 +524,14 @@ app.listen = function(){
|
||||
var server = http.createServer(this);
|
||||
return server.listen.apply(server, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* Log error using console.error.
|
||||
*
|
||||
* @param {Error} err
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function logerror(err){
|
||||
if (this.get('env') !== 'test') console.error(err.stack || err.toString());
|
||||
}
|
||||
|
||||
138
lib/request.js
138
lib/request.js
@@ -3,11 +3,13 @@
|
||||
*/
|
||||
|
||||
var accepts = require('accepts');
|
||||
var deprecate = require('depd')('express');
|
||||
var typeis = require('type-is');
|
||||
var http = require('http');
|
||||
var fresh = require('fresh');
|
||||
var parseRange = require('range-parser');
|
||||
var parse = require('parseurl');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
|
||||
/**
|
||||
* Request prototype.
|
||||
@@ -105,53 +107,55 @@ req.accepts = function(){
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given `encoding` is accepted.
|
||||
* Check if the given `encoding`s are accepted.
|
||||
*
|
||||
* @param {String} encoding
|
||||
* @param {String} ...encoding
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsEncoding = // backwards compatibility
|
||||
req.acceptsEncodings = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.encodings.apply(accept, arguments);
|
||||
};
|
||||
|
||||
req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
|
||||
'req.acceptsEncoding: Use acceptsEncodings instead');
|
||||
|
||||
/**
|
||||
* To do: update docs.
|
||||
*
|
||||
* Check if the given `charset` is acceptable,
|
||||
* Check if the given `charset`s are acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* @param {String} charset
|
||||
* @param {String} ...charset
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsCharset = // backwards compatibility
|
||||
req.acceptsCharsets = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.charsets.apply(accept, arguments);
|
||||
};
|
||||
|
||||
req.acceptsCharset = deprecate.function(req.acceptsCharsets,
|
||||
'req.acceptsCharset: Use acceptsCharsets instead');
|
||||
|
||||
/**
|
||||
* To do: update docs.
|
||||
*
|
||||
* Check if the given `lang` is acceptable,
|
||||
* Check if the given `lang`s are acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* @param {String} lang
|
||||
* @param {String} ...lang
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsLanguage = // backwards compatibility
|
||||
req.acceptsLanguages = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.languages.apply(accept, arguments);
|
||||
};
|
||||
|
||||
req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
|
||||
'req.acceptsLanguage: Use acceptsLanguages instead');
|
||||
|
||||
/**
|
||||
* Parse Range header field,
|
||||
* capping to the given `size`.
|
||||
@@ -239,19 +243,26 @@ req.is = function(types){
|
||||
/**
|
||||
* Return the protocol string "http" or "https"
|
||||
* when requested with TLS. When the "trust proxy"
|
||||
* setting is enabled the "X-Forwarded-Proto" header
|
||||
* field will be trusted. If you're running behind
|
||||
* a reverse proxy that supplies https for you this
|
||||
* may be enabled.
|
||||
* setting trusts the socket address, the
|
||||
* "X-Forwarded-Proto" header field will be trusted.
|
||||
* If you're running behind a reverse proxy that
|
||||
* supplies https for you this may be enabled.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('protocol', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
if (this.connection.encrypted) return 'https';
|
||||
if (!trustProxy) return 'http';
|
||||
defineGetter(req, 'protocol', function protocol(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
|
||||
if (!trust(this.connection.remoteAddress)) {
|
||||
return this.connection.encrypted
|
||||
? 'https'
|
||||
: 'http';
|
||||
}
|
||||
|
||||
// Note: X-Forwarded-Proto is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
var proto = this.get('X-Forwarded-Proto') || 'http';
|
||||
return proto.split(/\s*,\s*/)[0];
|
||||
});
|
||||
@@ -265,41 +276,41 @@ req.__defineGetter__('protocol', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('secure', function(){
|
||||
defineGetter(req, 'secure', function secure(){
|
||||
return 'https' == this.protocol;
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the remote address, or when
|
||||
* "trust proxy" is `true` return
|
||||
* the upstream addr.
|
||||
* Return the remote address from the trusted proxy.
|
||||
*
|
||||
* The is the remote address on the socket unless
|
||||
* "trust proxy" is set.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ip', function(){
|
||||
return this.ips[0] || this.connection.remoteAddress;
|
||||
defineGetter(req, 'ip', function ip(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
return proxyaddr(this, trust);
|
||||
});
|
||||
|
||||
/**
|
||||
* When "trust proxy" is `true`, parse
|
||||
* the "X-Forwarded-For" ip address list.
|
||||
* When "trust proxy" is set, trusted proxy addresses + client.
|
||||
*
|
||||
* For example if the value were "client, proxy1, proxy2"
|
||||
* you would receive the array `["client", "proxy1", "proxy2"]`
|
||||
* where "proxy2" is the furthest down-stream.
|
||||
* where "proxy2" is the furthest down-stream and "proxy1" and
|
||||
* "proxy2" were trusted.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ips', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
var val = this.get('X-Forwarded-For');
|
||||
return trustProxy && val
|
||||
? val.split(/ *, */)
|
||||
: [];
|
||||
defineGetter(req, 'ips', function ips() {
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var addrs = proxyaddr.all(this, trust);
|
||||
return addrs.slice(1).reverse();
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -317,9 +328,9 @@ req.__defineGetter__('ips', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('subdomains', function(){
|
||||
defineGetter(req, 'subdomains', function subdomains() {
|
||||
var offset = this.app.get('subdomain offset');
|
||||
return (this.host || '')
|
||||
return (this.hostname || '')
|
||||
.split('.')
|
||||
.reverse()
|
||||
.slice(offset);
|
||||
@@ -332,31 +343,48 @@ req.__defineGetter__('subdomains', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('path', function(){
|
||||
defineGetter(req, 'path', function path() {
|
||||
return parse(this).pathname;
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse the "Host" header field hostname.
|
||||
* Parse the "Host" header field to a hostname.
|
||||
*
|
||||
* When the "trust proxy" setting trusts the socket
|
||||
* address, the "X-Forwarded-Host" header field will
|
||||
* be trusted.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('host', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
var host = trustProxy && this.get('X-Forwarded-Host');
|
||||
host = host || this.get('Host');
|
||||
defineGetter(req, 'hostname', function hostname(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var host = this.get('X-Forwarded-Host');
|
||||
|
||||
if (!host || !trust(this.connection.remoteAddress)) {
|
||||
host = this.get('Host');
|
||||
}
|
||||
|
||||
if (!host) return;
|
||||
|
||||
// IPv6 literal support
|
||||
var offset = host[0] === '['
|
||||
? host.indexOf(']') + 1
|
||||
: 0;
|
||||
var index = host.indexOf(':', offset);
|
||||
|
||||
return ~index
|
||||
? host.substring(0, index)
|
||||
: host;
|
||||
});
|
||||
|
||||
// TODO: change req.host to return host in next major
|
||||
|
||||
defineGetter(req, 'host', deprecate.function(function host(){
|
||||
return this.hostname;
|
||||
}, 'req.host: Use req.hostname instead'));
|
||||
|
||||
/**
|
||||
* Check if the request is fresh, aka
|
||||
* Last-Modified and/or the ETag
|
||||
@@ -366,7 +394,7 @@ req.__defineGetter__('host', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('fresh', function(){
|
||||
defineGetter(req, 'fresh', function(){
|
||||
var method = this.method;
|
||||
var s = this.res.statusCode;
|
||||
|
||||
@@ -390,7 +418,7 @@ req.__defineGetter__('fresh', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('stale', function(){
|
||||
defineGetter(req, 'stale', function stale(){
|
||||
return !this.fresh;
|
||||
});
|
||||
|
||||
@@ -401,7 +429,23 @@ req.__defineGetter__('stale', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('xhr', function(){
|
||||
defineGetter(req, 'xhr', function xhr(){
|
||||
var val = this.get('X-Requested-With') || '';
|
||||
return 'xmlhttprequest' == val.toLowerCase();
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function for creating a getter on an object.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @param {String} name
|
||||
* @param {Function} getter
|
||||
* @api private
|
||||
*/
|
||||
function defineGetter(obj, name, getter) {
|
||||
Object.defineProperty(obj, name, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: getter
|
||||
});
|
||||
};
|
||||
|
||||
144
lib/response.js
144
lib/response.js
@@ -2,22 +2,23 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var deprecate = require('depd')('express');
|
||||
var escapeHtml = require('escape-html');
|
||||
var http = require('http');
|
||||
var path = require('path');
|
||||
var mixin = require('utils-merge');
|
||||
var escapeHtml = require('escape-html');
|
||||
var sign = require('cookie-signature').sign;
|
||||
var normalizeType = require('./utils').normalizeType;
|
||||
var normalizeTypes = require('./utils').normalizeTypes;
|
||||
var setCharset = require('./utils').setCharset;
|
||||
var contentDisposition = require('./utils').contentDisposition;
|
||||
var deprecate = require('./utils').deprecate;
|
||||
var etag = require('./utils').etag;
|
||||
var statusCodes = http.STATUS_CODES;
|
||||
var cookie = require('cookie');
|
||||
var send = require('send');
|
||||
var basename = path.basename;
|
||||
var extname = path.extname;
|
||||
var mime = send.mime;
|
||||
var vary = require('vary');
|
||||
|
||||
/**
|
||||
* Response prototype.
|
||||
@@ -74,15 +75,14 @@ res.links = function(links){
|
||||
* res.send(404, 'Sorry, cant find that');
|
||||
* res.send(404);
|
||||
*
|
||||
* @param {Mixed} body or status
|
||||
* @param {Mixed} body
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.send = function(body){
|
||||
var req = this.req;
|
||||
var head = 'HEAD' == req.method;
|
||||
var type;
|
||||
var encoding;
|
||||
var len;
|
||||
|
||||
// settings
|
||||
@@ -92,6 +92,7 @@ res.send = function(body){
|
||||
if (2 == arguments.length) {
|
||||
// res.send(body, status) backwards compat
|
||||
if ('number' != typeof body && 'number' == typeof arguments[1]) {
|
||||
deprecate('res.send(body, status): Use res.send(status, body) instead');
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
this.statusCode = body;
|
||||
@@ -122,18 +123,31 @@ res.send = function(body){
|
||||
break;
|
||||
}
|
||||
|
||||
// write strings in utf-8
|
||||
if ('string' === typeof body) {
|
||||
encoding = 'utf8';
|
||||
type = this.get('Content-Type');
|
||||
|
||||
// reflect this in content-type
|
||||
if ('string' === typeof type) {
|
||||
this.set('Content-Type', setCharset(type, 'utf-8'));
|
||||
}
|
||||
}
|
||||
|
||||
// populate Content-Length
|
||||
if (undefined !== body && !this.get('Content-Length')) {
|
||||
this.set('Content-Length', len = Buffer.isBuffer(body)
|
||||
len = Buffer.isBuffer(body)
|
||||
? body.length
|
||||
: Buffer.byteLength(body));
|
||||
: Buffer.byteLength(body, encoding);
|
||||
this.set('Content-Length', len);
|
||||
}
|
||||
|
||||
// ETag support
|
||||
// TODO: W/ support
|
||||
if (app.settings.etag && len && ('GET' == req.method || 'HEAD' == req.method)) {
|
||||
var etag = len !== undefined && app.get('etag fn');
|
||||
if (etag && ('GET' === req.method || 'HEAD' === req.method)) {
|
||||
if (!this.get('ETag')) {
|
||||
this.set('ETag', etag(body));
|
||||
etag = etag(body, encoding);
|
||||
etag && this.set('ETag', etag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +163,8 @@ res.send = function(body){
|
||||
}
|
||||
|
||||
// respond
|
||||
this.end(head ? null : body);
|
||||
this.end((head ? null : body), encoding);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -163,9 +178,6 @@ res.send = function(body){
|
||||
* res.json(500, 'oh noes!');
|
||||
* res.json(404, 'I dont have that');
|
||||
*
|
||||
* @param {Mixed} obj or status
|
||||
* @param {Mixed} obj
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -175,9 +187,11 @@ res.json = function(obj){
|
||||
// res.json(body, status) backwards compat
|
||||
if ('number' == typeof arguments[1]) {
|
||||
this.statusCode = arguments[1];
|
||||
return 'number' === typeof obj
|
||||
? jsonNumDeprecated.call(this, obj)
|
||||
: jsonDeprecated.call(this, obj);
|
||||
if (typeof obj === 'number') {
|
||||
deprecate('res.json(obj, status): Use res.json(status, obj) instead');
|
||||
} else {
|
||||
deprecate('res.json(num, status): Use res.status(status).json(num) instead');
|
||||
}
|
||||
} else {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
@@ -196,12 +210,6 @@ res.json = function(obj){
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
var jsonDeprecated = deprecate(res.json,
|
||||
'res.json(obj, status): Use res.json(status, obj) instead');
|
||||
|
||||
var jsonNumDeprecated = deprecate(res.json,
|
||||
'res.json(num, status): Use res.status(status).json(num) instead');
|
||||
|
||||
/**
|
||||
* Send JSON response with JSONP callback support.
|
||||
*
|
||||
@@ -212,9 +220,6 @@ var jsonNumDeprecated = deprecate(res.json,
|
||||
* res.jsonp(500, 'oh noes!');
|
||||
* res.jsonp(404, 'I dont have that');
|
||||
*
|
||||
* @param {Mixed} obj or status
|
||||
* @param {Mixed} obj
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -224,9 +229,11 @@ res.jsonp = function(obj){
|
||||
// res.json(body, status) backwards compat
|
||||
if ('number' == typeof arguments[1]) {
|
||||
this.statusCode = arguments[1];
|
||||
return 'number' === typeof obj
|
||||
? jsonpNumDeprecated.call(this, obj)
|
||||
: jsonpDeprecated.call(this, obj);
|
||||
if (typeof obj === 'number') {
|
||||
deprecate('res.jsonp(obj, status): Use res.jsonp(status, obj) instead');
|
||||
} else {
|
||||
deprecate('res.jsonp(num, status): Use res.status(status).jsonp(num) instead');
|
||||
}
|
||||
} else {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
@@ -260,12 +267,6 @@ res.jsonp = function(obj){
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
var jsonpDeprecated = deprecate(res.json,
|
||||
'res.jsonp(obj, status): Use res.jsonp(status, obj) instead');
|
||||
|
||||
var jsonpNumDeprecated = deprecate(res.json,
|
||||
'res.jsonp(num, status): Use res.status(status).jsonp(num) instead');
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
@@ -277,9 +278,10 @@ var jsonpNumDeprecated = deprecate(res.json,
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` defaulting to 0
|
||||
* - `root` root directory for relative filenames
|
||||
* - `hidden` serve hidden files, defaulting to false
|
||||
* - `maxAge` defaulting to 0 (can be string converted by `ms`)
|
||||
* - `root` root directory for relative filenames
|
||||
* - `headers` object of headers to serve with file
|
||||
* - `hidden` serve hidden files, defaulting to false
|
||||
*
|
||||
* Other options are passed along to `send`.
|
||||
*
|
||||
@@ -303,9 +305,6 @@ var jsonpNumDeprecated = deprecate(res.json,
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Object|Function} options or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -333,14 +332,10 @@ res.sendfile = function(path, options, fn){
|
||||
|
||||
// clean up
|
||||
cleanup();
|
||||
if (!self.headersSent) self.removeHeader('Content-Disposition');
|
||||
|
||||
// callback available
|
||||
if (fn) return fn(err);
|
||||
|
||||
// list in limbo if there's no callback
|
||||
if (self.headersSent) return;
|
||||
|
||||
// delegate
|
||||
next(err);
|
||||
}
|
||||
@@ -365,6 +360,21 @@ res.sendfile = function(path, options, fn){
|
||||
file.on('error', error);
|
||||
file.on('directory', next);
|
||||
file.on('stream', stream);
|
||||
|
||||
if (options.headers) {
|
||||
// set headers on successful transfer
|
||||
file.on('headers', function headers(res) {
|
||||
var obj = options.headers;
|
||||
var keys = Object.keys(obj);
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var k = keys[i];
|
||||
res.setHeader(k, obj[k]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// pipe
|
||||
file.pipe(this);
|
||||
this.on('finish', cleanup);
|
||||
};
|
||||
@@ -379,9 +389,6 @@ res.sendfile = function(path, options, fn){
|
||||
*
|
||||
* This method uses `res.sendfile()`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String|Function} filename or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -393,8 +400,13 @@ res.download = function(path, filename, fn){
|
||||
}
|
||||
|
||||
filename = filename || path;
|
||||
this.set('Content-Disposition', contentDisposition(filename));
|
||||
return this.sendfile(path, fn);
|
||||
|
||||
// set Content-Disposition when file is sent
|
||||
var headers = {
|
||||
'Content-Disposition': contentDisposition(filename)
|
||||
};
|
||||
|
||||
return this.sendfile(path, { headers: headers }, fn);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -734,31 +746,14 @@ res.redirect = function(url){
|
||||
*/
|
||||
|
||||
res.vary = function(field){
|
||||
var self = this;
|
||||
|
||||
// nothing
|
||||
if (!field) return this;
|
||||
|
||||
// array
|
||||
if (Array.isArray(field)) {
|
||||
field.forEach(function(field){
|
||||
self.vary(field);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var vary = this.get('Vary');
|
||||
|
||||
// append
|
||||
if (vary) {
|
||||
vary = vary.split(/ *, */);
|
||||
if (!~vary.indexOf(field)) vary.push(field);
|
||||
this.set('Vary', vary.join(', '));
|
||||
// checks for back-compat
|
||||
if (!field || (Array.isArray(field) && !field.length)) {
|
||||
deprecate('res.vary(): Provide a field name');
|
||||
return this;
|
||||
}
|
||||
|
||||
// set
|
||||
this.set('Vary', field);
|
||||
vary(this, field);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -772,9 +767,6 @@ res.vary = function(field){
|
||||
* - `cache` boolean hinting to the engine it should cache
|
||||
* - `filename` filename of the view being rendered
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object|Function} options or callback function
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
var Route = require('./route');
|
||||
var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
var mixin = require('utils-merge');
|
||||
var debug = require('debug')('express:router');
|
||||
var parseUrl = require('parseurl');
|
||||
var slice = Array.prototype.slice;
|
||||
@@ -30,6 +31,7 @@ var proto = module.exports = function(options) {
|
||||
router.params = {};
|
||||
router._params = [];
|
||||
router.caseSensitive = options.caseSensitive;
|
||||
router.mergeParams = options.mergeParams;
|
||||
router.strict = options.strict;
|
||||
router.stack = [];
|
||||
|
||||
@@ -122,6 +124,7 @@ proto.handle = function(req, res, done) {
|
||||
var idx = 0;
|
||||
var removed = '';
|
||||
var slashAdded = false;
|
||||
var paramcalled = {};
|
||||
|
||||
// store options for OPTIONS request
|
||||
// only used if OPTIONS request
|
||||
@@ -130,12 +133,12 @@ proto.handle = function(req, res, done) {
|
||||
// middleware and routes
|
||||
var stack = self.stack;
|
||||
|
||||
// request-level next
|
||||
var parent = req.next;
|
||||
done = wrap(done, function(old, err) {
|
||||
req.next = parent;
|
||||
old(err);
|
||||
});
|
||||
// manage inter-router variables
|
||||
var parentParams = req.params;
|
||||
var parentUrl = req.baseUrl || '';
|
||||
done = restore(done, req, 'baseUrl', 'next', 'params');
|
||||
|
||||
// setup next layer
|
||||
req.next = next;
|
||||
|
||||
// for options requests, respond with a default if nothing else responds
|
||||
@@ -156,6 +159,8 @@ proto.handle = function(req, res, done) {
|
||||
}
|
||||
|
||||
var layer = stack[idx++];
|
||||
var layerPath;
|
||||
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
}
|
||||
@@ -165,6 +170,7 @@ proto.handle = function(req, res, done) {
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
removed = '';
|
||||
@@ -185,18 +191,30 @@ proto.handle = function(req, res, done) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
req.route = route;
|
||||
var has_method = route._handles_method(method);
|
||||
|
||||
// we can now dispatch to the route
|
||||
if (method === 'options' && !route.methods['options']) {
|
||||
// build up automatic options response
|
||||
if (!has_method && method === 'options') {
|
||||
options.push.apply(options, route._options());
|
||||
}
|
||||
|
||||
// don't even bother
|
||||
if (!has_method && method !== 'head') {
|
||||
return next();
|
||||
}
|
||||
|
||||
// we can now dispatch to the route
|
||||
req.route = route;
|
||||
}
|
||||
|
||||
req.params = layer.params;
|
||||
// Capture one-time layer values
|
||||
req.params = self.mergeParams
|
||||
? mergeParams(layer.params, parentParams)
|
||||
: layer.params;
|
||||
layerPath = layer.path;
|
||||
|
||||
// this should be done for the layer
|
||||
return self.process_params(layer, req, res, function(err) {
|
||||
return self.process_params(layer, paramcalled, req, res, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -213,56 +231,58 @@ proto.handle = function(req, res, done) {
|
||||
}
|
||||
|
||||
function trim_prefix() {
|
||||
var c = path[layer.path.length];
|
||||
var c = path[layerPath.length];
|
||||
if (c && '/' != c && '.' != c) return next(err);
|
||||
|
||||
// Trim off the part of the url that matches the route
|
||||
// middleware (.use stuff) needs to have the path stripped
|
||||
debug('trim prefix (%s) from url %s', removed, req.url);
|
||||
removed = layer.path;
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
removed = layerPath;
|
||||
if (removed.length) {
|
||||
debug('trim prefix (%s) from url %s', layerPath, req.url);
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
}
|
||||
|
||||
// Ensure leading slash
|
||||
if (!fqdn && '/' != req.url[0]) {
|
||||
if (!fqdn && req.url[0] !== '/') {
|
||||
req.url = '/' + req.url;
|
||||
slashAdded = true;
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.handle.name || 'anonymous', layer.path, req.originalUrl);
|
||||
// Setup base URL (no trailing slash)
|
||||
if (removed.length && removed.substr(-1) === '/') {
|
||||
req.baseUrl = parentUrl + removed.substring(0, removed.length - 1);
|
||||
} else {
|
||||
req.baseUrl = parentUrl + removed;
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.handle.name || 'anonymous', layerPath, req.originalUrl);
|
||||
var arity = layer.handle.length;
|
||||
if (err) {
|
||||
if (arity === 4) {
|
||||
try {
|
||||
if (err && arity === 4) {
|
||||
layer.handle(err, req, res, next);
|
||||
} else if (!err && arity < 4) {
|
||||
layer.handle(req, res, next);
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
} else if (arity < 4) {
|
||||
layer.handle(req, res, next);
|
||||
} else {
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function wrap(old, fn) {
|
||||
return function () {
|
||||
var args = [old].concat(slice.call(arguments));
|
||||
fn.apply(this, args);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Process any parameters for the route.
|
||||
* Process any parameters for the layer.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
proto.process_params = function(route, req, res, done) {
|
||||
proto.process_params = function(layer, called, req, res, done) {
|
||||
var params = this.params;
|
||||
|
||||
// captured parameters from the route, keys and values
|
||||
var keys = route.keys;
|
||||
// captured parameters from the layer, keys and values
|
||||
var keys = layer.keys;
|
||||
|
||||
// fast track
|
||||
if (!keys || keys.length === 0) {
|
||||
@@ -270,10 +290,12 @@ proto.process_params = function(route, req, res, done) {
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var name;
|
||||
var paramIndex = 0;
|
||||
var key;
|
||||
var paramVal;
|
||||
var paramCallbacks;
|
||||
var paramCalled;
|
||||
|
||||
// process params in order
|
||||
// param callbacks can be async
|
||||
@@ -288,26 +310,58 @@ proto.process_params = function(route, req, res, done) {
|
||||
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
paramVal = key && req.params[key.name];
|
||||
paramCallbacks = key && params[key.name];
|
||||
|
||||
if (!key) {
|
||||
return done();
|
||||
}
|
||||
|
||||
name = key.name;
|
||||
paramVal = req.params[name];
|
||||
paramCallbacks = params[name];
|
||||
paramCalled = called[name];
|
||||
|
||||
if (paramVal === undefined || !paramCallbacks) {
|
||||
return param();
|
||||
}
|
||||
|
||||
// param previously called with same value or error occurred
|
||||
if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
|
||||
// restore value
|
||||
req.params[name] = paramCalled.value;
|
||||
|
||||
// next param
|
||||
return param(paramCalled.error);
|
||||
}
|
||||
|
||||
called[name] = paramCalled = {
|
||||
error: null,
|
||||
match: paramVal,
|
||||
value: paramVal
|
||||
};
|
||||
|
||||
try {
|
||||
if (paramCallbacks && undefined !== paramVal) {
|
||||
return paramCallback();
|
||||
} else if (key) {
|
||||
return param();
|
||||
}
|
||||
return paramCallback();
|
||||
} catch (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
if (err || !fn) return param(err);
|
||||
|
||||
// store updated value
|
||||
paramCalled.value = req.params[key.name];
|
||||
|
||||
if (err) {
|
||||
// store error
|
||||
paramCalled.error = err;
|
||||
param(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fn) return param();
|
||||
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
}
|
||||
|
||||
@@ -399,3 +453,67 @@ methods.concat('all').forEach(function(method){
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
// merge params with parent params
|
||||
function mergeParams(params, parent) {
|
||||
if (typeof parent !== 'object' || !parent) {
|
||||
return params;
|
||||
}
|
||||
|
||||
// make copy of parent for base
|
||||
var obj = mixin({}, parent);
|
||||
|
||||
// simple non-numeric merging
|
||||
if (!(0 in params) || !(0 in parent)) {
|
||||
return mixin(obj, params);
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var o = 0;
|
||||
|
||||
// determine numeric gaps
|
||||
while (i === o || o in parent) {
|
||||
if (i in params) i++;
|
||||
if (o in parent) o++;
|
||||
}
|
||||
|
||||
// offset numeric indices in params before merge
|
||||
for (i--; i >= 0; i--) {
|
||||
params[i + o] = params[i];
|
||||
|
||||
// create holes for the merge when necessary
|
||||
if (i < o) {
|
||||
delete params[i];
|
||||
}
|
||||
}
|
||||
|
||||
return mixin(parent, params);
|
||||
}
|
||||
|
||||
// restore obj props after function
|
||||
function restore(fn, obj) {
|
||||
var props = new Array(arguments.length - 2);
|
||||
var vals = new Array(arguments.length - 2);
|
||||
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
props[i] = arguments[i + 2];
|
||||
vals[i] = obj[props[i]];
|
||||
}
|
||||
|
||||
return function(err){
|
||||
// restore vals
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
obj[props[i]] = vals[i];
|
||||
}
|
||||
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
// wrap a function
|
||||
function wrap(old, fn) {
|
||||
return function () {
|
||||
var args = [old].concat(slice.call(arguments));
|
||||
fn.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,16 +45,7 @@ Layer.prototype.match = function(path){
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
key = keys[i - 1];
|
||||
|
||||
try {
|
||||
val = 'string' == typeof m[i]
|
||||
? decodeURIComponent(m[i])
|
||||
: m[i];
|
||||
} catch(e) {
|
||||
var err = new Error("Failed to decode param '" + m[i] + "'");
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
val = decode_param(m[i]);
|
||||
|
||||
if (key) {
|
||||
params[key.name] = val;
|
||||
@@ -65,3 +56,25 @@ Layer.prototype.match = function(path){
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode param value.
|
||||
*
|
||||
* @param {string} val
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function decode_param(val){
|
||||
if (typeof val !== 'string') {
|
||||
return val;
|
||||
}
|
||||
|
||||
try {
|
||||
return decodeURIComponent(val);
|
||||
} catch (e) {
|
||||
var err = new TypeError("Failed to decode param '" + val + "'");
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,30 @@ module.exports = Route;
|
||||
function Route(path) {
|
||||
debug('new %s', path);
|
||||
this.path = path;
|
||||
this.stack = undefined;
|
||||
this.stack = [];
|
||||
|
||||
// route handlers for various http methods
|
||||
this.methods = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Route.prototype._handles_method = function _handles_method(method) {
|
||||
if (this.methods._all) {
|
||||
return true;
|
||||
}
|
||||
|
||||
method = method.toLowerCase();
|
||||
|
||||
if (method === 'head' && !this.methods['head']) {
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
return Boolean(this.methods[method]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Array} supported HTTP methods
|
||||
* @api private
|
||||
@@ -46,25 +64,17 @@ Route.prototype._options = function(){
|
||||
*/
|
||||
|
||||
Route.prototype.dispatch = function(req, res, done){
|
||||
var self = this;
|
||||
var method = req.method.toLowerCase();
|
||||
var stack = this.stack;
|
||||
if (stack.length === 0) {
|
||||
return done();
|
||||
}
|
||||
|
||||
var method = req.method.toLowerCase();
|
||||
if (method === 'head' && !this.methods['head']) {
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
req.route = self;
|
||||
|
||||
// single middleware route case
|
||||
if (typeof this.stack === 'function') {
|
||||
this.stack(req, res, done);
|
||||
return;
|
||||
}
|
||||
|
||||
var stack = self.stack;
|
||||
if (!stack) {
|
||||
return done();
|
||||
}
|
||||
req.route = this;
|
||||
|
||||
var idx = 0;
|
||||
(function next_layer(err) {
|
||||
@@ -145,15 +155,8 @@ Route.prototype.all = function(){
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
if (!self.stack) {
|
||||
self.stack = fn;
|
||||
}
|
||||
else if (typeof self.stack === 'function') {
|
||||
self.stack = [{ handle: self.stack }, { handle: fn }];
|
||||
}
|
||||
else {
|
||||
self.stack.push({ handle: fn });
|
||||
}
|
||||
self.methods._all = true;
|
||||
self.stack.push({ handle: fn });
|
||||
});
|
||||
|
||||
return self;
|
||||
@@ -177,13 +180,6 @@ methods.forEach(function(method){
|
||||
self.methods[method] = true;
|
||||
}
|
||||
|
||||
if (!self.stack) {
|
||||
self.stack = [];
|
||||
}
|
||||
else if (typeof self.stack === 'function') {
|
||||
self.stack = [{ handle: self.stack }];
|
||||
}
|
||||
|
||||
self.stack.push({ method: method, handle: fn });
|
||||
});
|
||||
return self;
|
||||
|
||||
144
lib/utils.js
144
lib/utils.js
@@ -4,34 +4,53 @@
|
||||
|
||||
var mime = require('send').mime;
|
||||
var crc32 = require('buffer-crc32');
|
||||
var crypto = require('crypto');
|
||||
var basename = require('path').basename;
|
||||
var deprecate = require('util').deprecate;
|
||||
var proxyaddr = require('proxy-addr');
|
||||
var typer = require('media-typer');
|
||||
|
||||
/**
|
||||
* Deprecate function, like core `util.deprecate`
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @param {String} msg
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.deprecate = function(fn, msg){
|
||||
return 'test' !== process.env.NODE_ENV
|
||||
? deprecate(fn, 'express: ' + msg)
|
||||
: fn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return ETag for `body`.
|
||||
* Return strong ETag for `body`.
|
||||
*
|
||||
* @param {String|Buffer} body
|
||||
* @param {String} [encoding]
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.etag = function(body){
|
||||
return '"' + crc32.signed(body) + '"';
|
||||
exports.etag = function etag(body, encoding){
|
||||
if (body.length === 0) {
|
||||
// fast-path empty body
|
||||
return '"1B2M2Y8AsgTpgAmY7PhCfg=="'
|
||||
}
|
||||
|
||||
var hash = crypto
|
||||
.createHash('md5')
|
||||
.update(body, encoding)
|
||||
.digest('base64')
|
||||
return '"' + hash + '"'
|
||||
};
|
||||
|
||||
/**
|
||||
* Return weak ETag for `body`.
|
||||
*
|
||||
* @param {String|Buffer} body
|
||||
* @param {String} [encoding]
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.wetag = function wetag(body, encoding){
|
||||
if (body.length === 0) {
|
||||
// fast-path empty body
|
||||
return 'W/"0-0"'
|
||||
}
|
||||
|
||||
var buf = Buffer.isBuffer(body)
|
||||
? body
|
||||
: new Buffer(body, encoding)
|
||||
var len = buf.length
|
||||
return 'W/"' + len.toString(16) + '-' + crc32.unsigned(buf) + '"'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -116,7 +135,7 @@ exports.contentDisposition = function(filename){
|
||||
filename = basename(filename);
|
||||
// if filename contains non-ascii characters, add a utf-8 version ala RFC 5987
|
||||
ret = /[^\040-\176]/.test(filename)
|
||||
? 'attachment; filename=' + encodeURI(filename) + '; filename*=UTF-8\'\'' + encodeURI(filename)
|
||||
? 'attachment; filename="' + encodeURI(filename) + '"; filename*=UTF-8\'\'' + encodeURI(filename)
|
||||
: 'attachment; filename="' + filename + '"';
|
||||
}
|
||||
|
||||
@@ -148,3 +167,88 @@ function acceptParams(str, index) {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile "etag" value to function.
|
||||
*
|
||||
* @param {Boolean|String|Function} val
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.compileETag = function(val) {
|
||||
var fn;
|
||||
|
||||
if (typeof val === 'function') {
|
||||
return val;
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case true:
|
||||
fn = exports.wetag;
|
||||
break;
|
||||
case false:
|
||||
break;
|
||||
case 'strong':
|
||||
fn = exports.etag;
|
||||
break;
|
||||
case 'weak':
|
||||
fn = exports.wetag;
|
||||
break;
|
||||
default:
|
||||
throw new TypeError('unknown value for etag function: ' + val);
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile "proxy trust" value to function.
|
||||
*
|
||||
* @param {Boolean|String|Number|Array|Function} val
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.compileTrust = function(val) {
|
||||
if (typeof val === 'function') return val;
|
||||
|
||||
if (val === true) {
|
||||
// Support plain true/false
|
||||
return function(){ return true };
|
||||
}
|
||||
|
||||
if (typeof val === 'number') {
|
||||
// Support trusting hop count
|
||||
return function(a, i){ return i < val };
|
||||
}
|
||||
|
||||
if (typeof val === 'string') {
|
||||
// Support comma-separated values
|
||||
val = val.split(/ *, */);
|
||||
}
|
||||
|
||||
return proxyaddr.compile(val || []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the charset in a given Content-Type string.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {String} charset
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.setCharset = function(type, charset){
|
||||
if (!type || !charset) return type;
|
||||
|
||||
// parse type
|
||||
var parsed = typer.parse(type);
|
||||
|
||||
// set charset
|
||||
parsed.parameters.charset = charset;
|
||||
|
||||
// format type
|
||||
return typer.format(parsed);
|
||||
};
|
||||
|
||||
129
package.json
129
package.json
@@ -1,74 +1,16 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Sinatra inspired web development framework",
|
||||
"version": "4.2.0",
|
||||
"description": "Fast, unopinionated, minimalist web framework",
|
||||
"version": "4.5.0",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "TJ Holowaychuk",
|
||||
"email": "tj@vision-media.ca"
|
||||
},
|
||||
{
|
||||
"name": "Aaron Heckmann",
|
||||
"email": "aaron.heckmann+github@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Ciaran Jessup",
|
||||
"email": "ciaranj@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Douglas Christopher Wilson",
|
||||
"email": "doug@somethingdoug.com"
|
||||
},
|
||||
{
|
||||
"name": "Guillermo Rauch",
|
||||
"email": "rauchg@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Ong",
|
||||
"email": "me@jongleberry.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Shtylman",
|
||||
"email": "shtylman+expressjs@gmail.com"
|
||||
}
|
||||
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
|
||||
"Ciaran Jessup <ciaranj@gmail.com>",
|
||||
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||
"Guillermo Rauch <rauchg@gmail.com>",
|
||||
"Jonathan Ong <me@jongleberry.com>",
|
||||
"Roman Shtylman <shtylman+expressjs@gmail.com"
|
||||
],
|
||||
"dependencies": {
|
||||
"parseurl": "1.0.1",
|
||||
"accepts": "1.0.1",
|
||||
"type-is": "1.1.0",
|
||||
"range-parser": "1.0.0",
|
||||
"cookie": "0.1.2",
|
||||
"buffer-crc32": "0.2.1",
|
||||
"fresh": "0.2.2",
|
||||
"methods": "1.0.0",
|
||||
"send": "0.3.0",
|
||||
"cookie-signature": "1.0.3",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"utils-merge": "1.0.0",
|
||||
"escape-html": "1.0.1",
|
||||
"qs": "0.6.6",
|
||||
"serve-static": "1.1.0",
|
||||
"path-to-regexp": "0.1.2",
|
||||
"debug": "0.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.18.2",
|
||||
"body-parser": "~1.1.2",
|
||||
"connect-redis": "~2.0.0",
|
||||
"ejs": "~1.0.0",
|
||||
"jade": "~0.35.0",
|
||||
"marked": "0.3.2",
|
||||
"multiparty": "~3.2.4",
|
||||
"hjs": "~0.0.6",
|
||||
"should": "~3.3.1",
|
||||
"supertest": "~0.12.0",
|
||||
"method-override": "1.0.0",
|
||||
"cookie-parser": "1.0.1",
|
||||
"express-session": "1.0.4",
|
||||
"morgan": "1.0.1",
|
||||
"vhost": "1.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"express",
|
||||
"framework",
|
||||
@@ -80,13 +22,58 @@
|
||||
"app",
|
||||
"api"
|
||||
],
|
||||
"repository": "git://github.com/visionmedia/express",
|
||||
"scripts": {
|
||||
"prepublish": "npm prune",
|
||||
"test": "make test"
|
||||
"repository": "visionmedia/express",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.0.7",
|
||||
"buffer-crc32": "0.2.3",
|
||||
"debug": "1.0.2",
|
||||
"depd": "0.3.0",
|
||||
"escape-html": "1.0.1",
|
||||
"finalhandler": "0.0.2",
|
||||
"media-typer": "0.2.0",
|
||||
"methods": "1.0.1",
|
||||
"parseurl": "1.0.1",
|
||||
"proxy-addr": "1.0.1",
|
||||
"range-parser": "1.0.0",
|
||||
"send": "0.5.0",
|
||||
"serve-static": "~1.3.0",
|
||||
"type-is": "~1.3.2",
|
||||
"vary": "0.1.0",
|
||||
"cookie": "0.1.2",
|
||||
"fresh": "0.2.2",
|
||||
"cookie-signature": "1.0.4",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"utils-merge": "1.0.0",
|
||||
"qs": "0.6.6",
|
||||
"path-to-regexp": "0.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.1",
|
||||
"istanbul": "0.2.14",
|
||||
"mocha": "~1.20.1",
|
||||
"should": "~4.0.4",
|
||||
"supertest": "~0.13.0",
|
||||
"connect-redis": "~2.0.0",
|
||||
"ejs": "~1.0.0",
|
||||
"jade": "~1.3.1",
|
||||
"marked": "0.3.2",
|
||||
"hjs": "~0.0.6",
|
||||
"body-parser": "~1.4.3",
|
||||
"cookie-parser": "~1.3.1",
|
||||
"express-session": "~1.6.1",
|
||||
"method-override": "2.0.2",
|
||||
"multiparty": "~3.3.0",
|
||||
"morgan": "1.1.1",
|
||||
"vhost": "2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
"scripts": {
|
||||
"prepublish": "npm prune",
|
||||
"test": "mocha --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
|
||||
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
|
||||
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
var should = require('should');
|
||||
var express = require('../')
|
||||
, Route = express.Route
|
||||
, methods = require('methods')
|
||||
@@ -167,5 +168,49 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch({ method: 'get' }, {});
|
||||
});
|
||||
|
||||
it('should handle throwing inside error handlers', function(done) {
|
||||
var route = new Route('');
|
||||
|
||||
route.get(function(req, res, next){
|
||||
throw new Error('boom!');
|
||||
});
|
||||
|
||||
route.get(function(err, req, res, next){
|
||||
throw new Error('oops');
|
||||
});
|
||||
|
||||
route.get(function(err, req, res, next){
|
||||
assert.equal(err.message, 'oops');
|
||||
done();
|
||||
});
|
||||
|
||||
route.dispatch({ url: '/', method: 'GET' }, {});
|
||||
});
|
||||
|
||||
it('should handle throw in .all', function(done) {
|
||||
var route = new Route('');
|
||||
|
||||
route.all(function(req, res, next){
|
||||
throw new Error('boom!');
|
||||
});
|
||||
|
||||
route.dispatch({ url: '/', method: 'GET' }, {}, function(err){
|
||||
should(err).be.ok;
|
||||
err.message.should.equal('boom!');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle single error handler', function(done) {
|
||||
var route = new Route('');
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
// this should not execute
|
||||
true.should.be.false;
|
||||
});
|
||||
|
||||
route.dispatch({ url: '/', method: 'GET' }, {}, done);
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
136
test/Router.js
136
test/Router.js
@@ -1,4 +1,5 @@
|
||||
|
||||
var after = require('after');
|
||||
var express = require('../')
|
||||
, Router = express.Router
|
||||
, methods = require('methods')
|
||||
@@ -128,7 +129,48 @@ describe('Router', function(){
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/foo/2', method: 'GET' }, {}, done);
|
||||
router.handle({ url: '/foo/2', method: 'GET' }, {}, function() {});
|
||||
});
|
||||
|
||||
it('should handle throwing in handler after async param', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
router.param('user', function(req, res, next, val){
|
||||
process.nextTick(function(){
|
||||
req.user = val;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
router.use('/:user', function(req, res, next){
|
||||
throw new Error('oh no!');
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
assert.equal(err.message, 'oh no!');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/bob', method: 'GET' }, {}, function() {});
|
||||
});
|
||||
|
||||
it('should handle throwing inside error handlers', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
router.use(function(req, res, next){
|
||||
throw new Error('boom!');
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
throw new Error('oops');
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
assert.equal(err.message, 'oops');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/', method: 'GET' }, {}, done);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -183,5 +225,97 @@ describe('Router', function(){
|
||||
|
||||
router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done);
|
||||
});
|
||||
|
||||
it('should only call once per request', function(done) {
|
||||
var count = 0;
|
||||
var req = { url: '/foo/bob/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('user', function(req, res, next, user) {
|
||||
count++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/foo/:user/', new Router());
|
||||
router.use('/foo/:user/', sub);
|
||||
|
||||
router.handle(req, {}, function(err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(count, 1);
|
||||
assert.equal(req.user, 'bob');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call when values differ', function(done) {
|
||||
var count = 0;
|
||||
var req = { url: '/foo/bob/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('user', function(req, res, next, user) {
|
||||
count++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/foo/:user/', new Router());
|
||||
router.use('/:user/bob/', sub);
|
||||
|
||||
router.handle(req, {}, function(err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(count, 2);
|
||||
assert.equal(req.user, 'foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parallel requests', function() {
|
||||
it('should not mix requests', function(done) {
|
||||
var req1 = { url: '/foo/50/bar', method: 'get' };
|
||||
var req2 = { url: '/foo/10/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
done = after(2, done);
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('ms', function(req, res, next, ms) {
|
||||
ms = parseInt(ms, 10);
|
||||
req.ms = ms;
|
||||
setTimeout(next, ms);
|
||||
});
|
||||
|
||||
router.use('/foo/:ms/', new Router());
|
||||
router.use('/foo/:ms/', sub);
|
||||
|
||||
router.handle(req1, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(req1.ms, 50);
|
||||
assert.equal(req1.originalUrl, '/foo/50/bar');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle(req2, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(req2.ms, 10);
|
||||
assert.equal(req2.originalUrl, '/foo/10/bar');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
var app = require('../../examples/auth/app')
|
||||
, request = require('supertest');
|
||||
|
||||
function redirects(to, fn){
|
||||
return function(err, res){
|
||||
res.statusCode.should.equal(302)
|
||||
res.headers.should.have.property('location').match(to);
|
||||
fn()
|
||||
}
|
||||
}
|
||||
var request = require('supertest')
|
||||
|
||||
function getCookie(res) {
|
||||
return res.headers['set-cookie'][0].split(';')[0];
|
||||
@@ -18,25 +10,93 @@ describe('auth', function(){
|
||||
it('should redirect to /login', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(redirects(/login$/, done))
|
||||
.expect('Location', '/login')
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /restricted (w/o cookie)',function(){
|
||||
it('should redirect to /login', function(done){
|
||||
describe('GET /login',function(){
|
||||
it('should render login form', function(done){
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.end(redirects(/login$/,done))
|
||||
.get('/login')
|
||||
.expect(200, /<form/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /login', function(){
|
||||
it('should fail without proper credentials', function(done){
|
||||
it('should display login error', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=not-tj&password=foobar')
|
||||
.end(redirects(/login$/, done))
|
||||
.expect('Location', '/login')
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/login')
|
||||
.set('Cookie', getCookie(res))
|
||||
.expect(200, /Authentication failed/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /logout',function(){
|
||||
it('should redirect to /', function(done){
|
||||
request(app)
|
||||
.get('/logout')
|
||||
.expect('Location', '/')
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /restricted',function(){
|
||||
it('should redirect to /login without cookie', function(done){
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.expect('Location', '/login')
|
||||
.expect(302, done)
|
||||
})
|
||||
|
||||
it('should succeed with proper cookie', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=tj&password=foobar')
|
||||
.expect('Location', '/')
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.set('Cookie', getCookie(res))
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /login', function(){
|
||||
it('should fail without proper username', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=not-tj&password=foobar')
|
||||
.expect('Location', '/login')
|
||||
.expect(302, done)
|
||||
})
|
||||
|
||||
it('should fail without proper password', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=tj&password=baz')
|
||||
.expect('Location', '/login')
|
||||
.expect(302, done)
|
||||
})
|
||||
|
||||
it('should succeed with proper credentials', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=tj&password=foobar')
|
||||
.expect('Location', '/')
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,16 +7,43 @@ describe('content-negotiation', function(){
|
||||
it('should default to text/html', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>')
|
||||
.end(done);
|
||||
.expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)
|
||||
})
|
||||
|
||||
it('should accept to text/plain', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'text/plain')
|
||||
.expect(' - Tobi\n - Loki\n - Jane\n')
|
||||
.end(done);
|
||||
.expect(200, ' - Tobi\n - Loki\n - Jane\n', done)
|
||||
})
|
||||
|
||||
it('should accept to application/json', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users', function(){
|
||||
it('should default to text/html', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)
|
||||
})
|
||||
|
||||
it('should accept to text/plain', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.set('Accept', 'text/plain')
|
||||
.expect(200, ' - Tobi\n - Loki\n - Jane\n', done)
|
||||
})
|
||||
|
||||
it('should accept to application/json', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,17 +18,59 @@ describe('cookies', function(){
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should respond to cookie', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('urlencoded')
|
||||
.send({ remember: 1 })
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Cookie', res.headers['set-cookie'][0])
|
||||
.expect(200, /Remembered/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /forget', function(){
|
||||
it('should clear cookie', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('urlencoded')
|
||||
.send({ remember: 1 })
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/forget')
|
||||
.set('Cookie', res.headers['set-cookie'][0])
|
||||
.expect('Set-Cookie', /remember=;/)
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /', function(){
|
||||
it('should set a cookie', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.type('urlencoded')
|
||||
.send({ remember: 1 })
|
||||
.end(function(err, res){
|
||||
.expect(302, function(err, res){
|
||||
res.headers.should.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should no set cookie w/o reminder', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.send({})
|
||||
.expect(302, function(err, res){
|
||||
res.headers.should.not.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -30,4 +30,4 @@ describe('downloads', function(){
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,14 +7,11 @@ describe('ejs', function(){
|
||||
it('should respond with html', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.should.have.status(200);
|
||||
res.should.have.header('Content-Type', 'text/html; charset=utf-8');
|
||||
res.text.should.include('<li>tobi <tobi@learnboost.com></li>');
|
||||
res.text.should.include('<li>loki <loki@learnboost.com></li>');
|
||||
res.text.should.include('<li>jane <jane@learnboost.com></li>');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'text/html; charset=utf-8')
|
||||
.expect(/<li>tobi <tobi@learnboost\.com><\/li>/)
|
||||
.expect(/<li>loki <loki@learnboost\.com><\/li>/)
|
||||
.expect(/<li>jane <jane@learnboost\.com><\/li>/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
var app = require('../../examples/markdown')
|
||||
, request = require('supertest');
|
||||
var request = require('supertest')
|
||||
|
||||
describe('markdown', function(){
|
||||
describe('GET /', function(){
|
||||
@@ -18,4 +18,4 @@ describe('markdown', function(){
|
||||
.expect(500,done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,10 +7,39 @@ describe('mvc', function(){
|
||||
it('should redirect to /users', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', '/users')
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /pet/0', function(){
|
||||
it('should get pet', function(done){
|
||||
request(app)
|
||||
.get('/pet/0')
|
||||
.expect(200, /Tobi/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /pet/0/edit', function(){
|
||||
it('should get pet edit page', function(done){
|
||||
request(app)
|
||||
.get('/pet/0/edit')
|
||||
.expect(/<form/)
|
||||
.expect(200, /Tobi/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /pet/2', function(){
|
||||
it('should update the pet', function(done){
|
||||
request(app)
|
||||
.put('/pet/3')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ pet: { name: 'Boots' } })
|
||||
.end(function(err, res){
|
||||
res.should.have.status(302);
|
||||
res.headers.location.should.include('/users');
|
||||
done();
|
||||
if (err) return done(err);
|
||||
request(app)
|
||||
.get('/pet/3/edit')
|
||||
.expect(200, /Boots/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -19,13 +48,11 @@ describe('mvc', function(){
|
||||
it('should display a list of users', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>Users</h1>');
|
||||
res.text.should.include('>TJ<');
|
||||
res.text.should.include('>Guillermo<');
|
||||
res.text.should.include('>Nathan<');
|
||||
done();
|
||||
})
|
||||
.expect(/<h1>Users<\/h1>/)
|
||||
.expect(/>TJ</)
|
||||
.expect(/>Guillermo</)
|
||||
.expect(/>Nathan</)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -34,21 +61,16 @@ describe('mvc', function(){
|
||||
it('should display the user', function(done){
|
||||
request(app)
|
||||
.get('/user/0')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>TJ <a href="/user/0/edit">edit');
|
||||
done();
|
||||
})
|
||||
.expect(200, /<h1>TJ <a href="\/user\/0\/edit">edit/, done)
|
||||
})
|
||||
|
||||
it('should display the users pets', function(done){
|
||||
request(app)
|
||||
.get('/user/0')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('/pet/0">Tobi');
|
||||
res.text.should.include('/pet/1">Loki');
|
||||
res.text.should.include('/pet/2">Jane');
|
||||
done();
|
||||
})
|
||||
.expect(/\/pet\/0">Tobi/)
|
||||
.expect(/\/pet\/1">Loki/)
|
||||
.expect(/\/pet\/2">Jane/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -65,27 +87,46 @@ describe('mvc', function(){
|
||||
it('should display the edit form', function(done){
|
||||
request(app)
|
||||
.get('/user/1/edit')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>Guillermo</h1>');
|
||||
res.text.should.include('value="put"');
|
||||
done();
|
||||
})
|
||||
.expect(/Guillermo/)
|
||||
.expect(200, /<form/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /user/:id', function(){
|
||||
it('should 500 on error', function(done){
|
||||
request(app)
|
||||
.put('/user/1')
|
||||
.send({})
|
||||
.expect(500, done)
|
||||
})
|
||||
|
||||
it('should update the user', function(done){
|
||||
request(app)
|
||||
.put('/user/1')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ user: { name: 'Tobo' }})
|
||||
.end(function(err, res){
|
||||
if (err) return done(err);
|
||||
request(app)
|
||||
.get('/user/1/edit')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>Tobo</h1>');
|
||||
done();
|
||||
})
|
||||
.expect(200, /Tobo/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /user/:id/pet', function(){
|
||||
it('should create a pet for user', function(done){
|
||||
request(app)
|
||||
.post('/user/2/pet')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ pet: { name: 'Snickers' }})
|
||||
.expect('Location', '/user/2')
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/user/2')
|
||||
.expect(200, /Snickers/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var app = require('../../examples/params/app')
|
||||
, request = require('supertest');
|
||||
var request = require('supertest')
|
||||
|
||||
describe('params', function(){
|
||||
describe('GET /', function(){
|
||||
@@ -18,6 +18,14 @@ describe('params', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/9', function(){
|
||||
it('should fail to find user', function(done){
|
||||
request(app)
|
||||
.get('/user/9')
|
||||
.expect(/failed to find user/,done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/0-2', function(){
|
||||
it('should respond with three users', function(done){
|
||||
request(app)
|
||||
@@ -25,4 +33,12 @@ describe('params', function(){
|
||||
.expect(/users tj, tobi/,done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/foo-bar', function(){
|
||||
it('should fail integer parsing', function(done){
|
||||
request(app)
|
||||
.get('/users/foo-bar')
|
||||
.expect(/failed to parseInt foo/,done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var app = require('../../examples/resource/app')
|
||||
, request = require('supertest');
|
||||
var request = require('supertest')
|
||||
|
||||
describe('resource', function(){
|
||||
describe('GET /', function(){
|
||||
@@ -26,6 +26,14 @@ describe('resource', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/9', function(){
|
||||
it('should respond with error', function(done){
|
||||
request(app)
|
||||
.get('/users/9')
|
||||
.expect('{"error":"Cannot find user"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/1..3', function(){
|
||||
it('should respond with users 1 through 3', function(done){
|
||||
request(app)
|
||||
@@ -35,13 +43,21 @@ describe('resource', function(){
|
||||
})
|
||||
|
||||
describe('DELETE /users/1', function(){
|
||||
it('should respond with users 1 through 3', function(done){
|
||||
it('should delete user 1', function(done){
|
||||
request(app)
|
||||
.del('/users/1')
|
||||
.expect(/^destroyed/,done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('DELETE /users/9', function(){
|
||||
it('should fail', function(done){
|
||||
request(app)
|
||||
.del('/users/9')
|
||||
.expect('Cannot find user', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/1..3.json', function(){
|
||||
it('should respond with users 2 and 3 as json', function(done){
|
||||
request(app)
|
||||
@@ -49,4 +65,4 @@ describe('resource', function(){
|
||||
.expect(/^\[null,{"name":"aaron"},{"name":"guillermo"}\]/,done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
46
test/acceptance/vhost.js
Normal file
46
test/acceptance/vhost.js
Normal file
@@ -0,0 +1,46 @@
|
||||
var app = require('../../examples/vhost')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('vhost', function(){
|
||||
describe('example.com', function(){
|
||||
describe('GET /', function(){
|
||||
it('should say hello', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'example.com')
|
||||
.expect(200, /hello/i, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /foo', function(){
|
||||
it('should say foo', function(done){
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.set('Host', 'example.com')
|
||||
.expect(200, 'requested foo', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('foo.example.com', function(){
|
||||
describe('GET /', function(){
|
||||
it('should redirect to /foo', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'foo.example.com')
|
||||
.expect(302, /Redirecting to http:\/\/example.com:3000\/foo/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('bar.example.com', function(){
|
||||
describe('GET /', function(){
|
||||
it('should redirect to /bar', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'bar.example.com')
|
||||
.expect(302, /Redirecting to http:\/\/example.com:3000\/bar/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -24,11 +24,72 @@ describe('web-service', function(){
|
||||
it('should respond users json', function(done){
|
||||
request(app)
|
||||
.get('/api/users?api-key=foo')
|
||||
.end(function(err, res){
|
||||
res.should.be.json;
|
||||
res.text.should.equal('[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/repos', function(){
|
||||
describe('without an api key', function(){
|
||||
it('should respond with 400 bad request', function(done){
|
||||
request(app)
|
||||
.get('/api/repos')
|
||||
.expect(400, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with an invalid api key', function(){
|
||||
it('should respond with 401 unauthorized', function(done){
|
||||
request(app)
|
||||
.get('/api/repos?api-key=rawr')
|
||||
.expect(401, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a valid api key', function(){
|
||||
it('should respond repos json', function(done){
|
||||
request(app)
|
||||
.get('/api/repos?api-key=foo')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(/"name":"express"/)
|
||||
.expect(/"url":"http:\/\/github.com\/visionmedia\/express"/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/user/:name/repos', function(){
|
||||
describe('without an api key', function(){
|
||||
it('should respond with 400 bad request', function(done){
|
||||
request(app)
|
||||
.get('/api/user/loki/repos')
|
||||
.expect(400, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with an invalid api key', function(){
|
||||
it('should respond with 401 unauthorized', function(done){
|
||||
request(app)
|
||||
.get('/api/user/loki/repos?api-key=rawr')
|
||||
.expect(401, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a valid api key', function(){
|
||||
it('should respond user repos json', function(done){
|
||||
request(app)
|
||||
.get('/api/user/loki/repos?api-key=foo')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(/"name":"stylus"/)
|
||||
.expect(/"url":"http:\/\/github.com\/learnboost\/stylus"/)
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should 404 with unknown user', function(done){
|
||||
request(app)
|
||||
.get('/api/user/bob/repos?api-key=foo')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -37,12 +98,8 @@ describe('web-service', function(){
|
||||
it('should respond with 404 json', function(done){
|
||||
request(app)
|
||||
.get('/api/something?api-key=bar')
|
||||
.end(function(err, res){
|
||||
res.should.have.status(404);
|
||||
res.should.be.json;
|
||||
res.text.should.equal('{"error":"Lame, can\'t find that"}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(404, '{"error":"Lame, can\'t find that"}', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -34,6 +34,8 @@ describe('HEAD', function(){
|
||||
.get('/tobi')
|
||||
.expect(200, function(err, res){
|
||||
if (err) return done(err);
|
||||
delete headers.date;
|
||||
delete res.headers.date;
|
||||
assert.deepEqual(res.headers, headers);
|
||||
done();
|
||||
});
|
||||
|
||||
25
test/app.js
25
test/app.js
@@ -1,6 +1,7 @@
|
||||
|
||||
var express = require('../')
|
||||
, assert = require('assert');
|
||||
var assert = require('assert')
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('app', function(){
|
||||
it('should inherit from event emitter', function(done){
|
||||
@@ -8,6 +9,17 @@ describe('app', function(){
|
||||
app.on('foo', done);
|
||||
app.emit('foo');
|
||||
})
|
||||
|
||||
it('should be callable', function(){
|
||||
var app = express();
|
||||
assert(typeof app, 'function');
|
||||
})
|
||||
|
||||
it('should 404 without routes', function(done){
|
||||
request(express())
|
||||
.get('/')
|
||||
.expect(404, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('app.parent', function(){
|
||||
@@ -84,3 +96,12 @@ describe('in production', function(){
|
||||
process.env.NODE_ENV = 'test';
|
||||
})
|
||||
})
|
||||
|
||||
describe('without NODE_ENV', function(){
|
||||
it('should default to development', function(){
|
||||
process.env.NODE_ENV = '';
|
||||
var app = express();
|
||||
app.get('env').should.equal('development');
|
||||
process.env.NODE_ENV = 'test';
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,6 +16,24 @@ describe('OPTIONS', function(){
|
||||
.expect('Allow', 'GET,PUT', done);
|
||||
})
|
||||
|
||||
it('should not be affected by app.all', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(){});
|
||||
app.get('/users', function(req, res){});
|
||||
app.put('/users', function(req, res){});
|
||||
app.all('/users', function(req, res, next){
|
||||
res.setHeader('x-hit', '1');
|
||||
next();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.options('/users')
|
||||
.expect('x-hit', '1')
|
||||
.expect('allow', 'GET,PUT')
|
||||
.expect(200, 'GET,PUT', done);
|
||||
})
|
||||
|
||||
it('should not respond if the path is not defined', function(done){
|
||||
var app = express();
|
||||
|
||||
|
||||
@@ -37,6 +37,11 @@ describe('app', function(){
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
it('should fail if not given fn', function(){
|
||||
var app = express();
|
||||
app.param.bind(app, ':name', 'bob').should.throw();
|
||||
})
|
||||
})
|
||||
|
||||
describe('.param(names, fn)', function(){
|
||||
@@ -95,5 +100,187 @@ describe('app', function(){
|
||||
.get('/user/123')
|
||||
.expect('123', done);
|
||||
})
|
||||
|
||||
it('should only call once per request', function(done) {
|
||||
var app = express();
|
||||
var called = 0;
|
||||
var count = 0;
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
called++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.use(function(req, res) {
|
||||
res.end([count, called, req.user].join(' '));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bob')
|
||||
.expect('2 1 bob', done);
|
||||
})
|
||||
|
||||
it('should call when values differ', function(done) {
|
||||
var app = express();
|
||||
var called = 0;
|
||||
var count = 0;
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
called++;
|
||||
req.users = (req.users || []).concat(user);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/:user/bob', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.use(function(req, res) {
|
||||
res.end([count, called, req.users.join(',')].join(' '));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bob')
|
||||
.expect('2 2 foo,bob', done);
|
||||
})
|
||||
|
||||
it('should support altering req.params across routes', function(done) {
|
||||
var app = express();
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
req.params.user = 'loki';
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/:user', function(req, res, next) {
|
||||
next('route');
|
||||
});
|
||||
app.get('/:user', function(req, res, next) {
|
||||
res.send(req.params.user);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/bob')
|
||||
.expect('loki', done);
|
||||
})
|
||||
|
||||
it('should not invoke without route handler', function(done) {
|
||||
var app = express();
|
||||
|
||||
app.param('thing', function(req, res, next, thing) {
|
||||
req.thing = thing;
|
||||
next();
|
||||
});
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
next(new Error('invalid invokation'));
|
||||
});
|
||||
|
||||
app.post('/:user', function(req, res, next) {
|
||||
res.send(req.params.user);
|
||||
});
|
||||
|
||||
app.get('/:thing', function(req, res, next) {
|
||||
res.send(req.thing);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/bob')
|
||||
.expect(200, 'bob', done);
|
||||
})
|
||||
|
||||
it('should work with encoded values', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('name', function(req, res, next, name){
|
||||
req.params.name = name;
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user/:name', function(req, res){
|
||||
var name = req.params.name;
|
||||
res.send('' + name);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/foo%25bar')
|
||||
.expect('foo%bar', done);
|
||||
})
|
||||
|
||||
it('should catch thrown error', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('id', function(req, res, next, id){
|
||||
throw new Error('err!');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
var id = req.params.id;
|
||||
res.send('' + id);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.expect(500, done);
|
||||
})
|
||||
|
||||
it('should defer to next route', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('id', function(req, res, next, id){
|
||||
next('route');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
var id = req.params.id;
|
||||
res.send('' + id);
|
||||
});
|
||||
|
||||
app.get('/:name/123', function(req, res){
|
||||
res.send('name');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.expect('name', done);
|
||||
})
|
||||
|
||||
it('should defer all the param routes', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('id', function(req, res, next, val){
|
||||
if (val === 'new') return next('route');
|
||||
return next();
|
||||
});
|
||||
|
||||
app.all('/user/:id', function(req, res){
|
||||
res.send('all.id');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
res.send('get.id');
|
||||
});
|
||||
|
||||
app.get('/user/new', function(req, res){
|
||||
res.send('get.new');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/new')
|
||||
.expect('get.new', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -54,6 +54,27 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle render error throws', function(done){
|
||||
var app = express();
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'fale';
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
throw new Error('err!');
|
||||
};
|
||||
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', function(err, str){
|
||||
err.should.be.ok;
|
||||
err.message.should.equal('err!');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the file does not exist', function(){
|
||||
it('should provide a helpful error', function(done){
|
||||
var app = express();
|
||||
@@ -132,6 +153,68 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('caching', function(){
|
||||
it('should always lookup view without cache', function(done){
|
||||
var app = express();
|
||||
var count = 0;
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'fake';
|
||||
count++;
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
fn(null, 'abstract engine');
|
||||
};
|
||||
|
||||
app.set('view cache', false);
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(2);
|
||||
str.should.equal('abstract engine');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should cache with "view cache" setting', function(done){
|
||||
var app = express();
|
||||
var count = 0;
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'fake';
|
||||
count++;
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
fn(null, 'abstract engine');
|
||||
};
|
||||
|
||||
app.set('view cache', true);
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.render(name, options, fn)', function(){
|
||||
@@ -175,5 +258,37 @@ describe('app', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('caching', function(){
|
||||
it('should cache with cache option', function(done){
|
||||
var app = express();
|
||||
var count = 0;
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'fake';
|
||||
count++;
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
fn(null, 'abstract engine');
|
||||
};
|
||||
|
||||
app.set('view cache', false);
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', {cache: true}, function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
app.render('something', {cache: true}, function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -49,4 +49,14 @@ describe('app.route', function(){
|
||||
.get('/test')
|
||||
.expect('test', done);
|
||||
});
|
||||
|
||||
it('should not error on empty routes', function(done){
|
||||
var app = express();
|
||||
|
||||
app.route('/:foo');
|
||||
|
||||
request(app)
|
||||
.get('/test')
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,33 @@ var express = require('../')
|
||||
, methods = require('methods');
|
||||
|
||||
describe('app.router', function(){
|
||||
it('should restore req.params after leaving router', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router();
|
||||
|
||||
function handler1(req, res, next){
|
||||
res.setHeader('x-user-id', req.params.id);
|
||||
next()
|
||||
}
|
||||
|
||||
function handler2(req, res){
|
||||
res.send(req.params.id);
|
||||
}
|
||||
|
||||
router.use(function(req, res, next){
|
||||
res.setHeader('x-router', req.params.id);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user/:id', handler1, router, handler2);
|
||||
|
||||
request(app)
|
||||
.get('/user/1')
|
||||
.expect('x-router', 'undefined')
|
||||
.expect('x-user-id', '1')
|
||||
.expect(200, '1', done);
|
||||
})
|
||||
|
||||
describe('methods supported', function(){
|
||||
methods.concat('del').forEach(function(method){
|
||||
if (method === 'connect') return;
|
||||
@@ -25,6 +52,11 @@ describe('app.router', function(){
|
||||
[method]('/foo')
|
||||
.expect('head' == method ? '' : method, done);
|
||||
})
|
||||
|
||||
it('should reject numbers for app.' + method, function(){
|
||||
var app = express();
|
||||
app[method].bind(app, '/', 3).should.throw(/Number/);
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
@@ -178,6 +210,106 @@ describe('app.router', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('params', function(){
|
||||
it('should overwrite existing req.params by default', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router();
|
||||
|
||||
router.get('/:action', function(req, res){
|
||||
res.send(req.params);
|
||||
});
|
||||
|
||||
app.use('/user/:user', router);
|
||||
|
||||
request(app)
|
||||
.get('/user/1/get')
|
||||
.expect(200, '{"action":"get"}', done);
|
||||
})
|
||||
|
||||
it('should allow merging existing req.params', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/:action', function(req, res){
|
||||
var keys = Object.keys(req.params).sort();
|
||||
res.send(keys.map(function(k){ return [k, req.params[k]] }));
|
||||
});
|
||||
|
||||
app.use('/user/:user', router);
|
||||
|
||||
request(app)
|
||||
.get('/user/tj/get')
|
||||
.expect(200, '[["action","get"],["user","tj"]]', done);
|
||||
})
|
||||
|
||||
it('should use params from router', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/:thing', function(req, res){
|
||||
var keys = Object.keys(req.params).sort();
|
||||
res.send(keys.map(function(k){ return [k, req.params[k]] }));
|
||||
});
|
||||
|
||||
app.use('/user/:thing', router);
|
||||
|
||||
request(app)
|
||||
.get('/user/tj/get')
|
||||
.expect(200, '[["thing","get"]]', done);
|
||||
})
|
||||
|
||||
it('should merge numeric indices req.params', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/*.*', function(req, res){
|
||||
var keys = Object.keys(req.params).sort();
|
||||
res.send(keys.map(function(k){ return [k, req.params[k]] }));
|
||||
});
|
||||
|
||||
app.use('/user/id:(\\d+)', router);
|
||||
|
||||
request(app)
|
||||
.get('/user/id:10/profile.json')
|
||||
.expect(200, '[["0","10"],["1","profile"],["2","json"]]', done);
|
||||
})
|
||||
|
||||
it('should merge numeric indices req.params when more in parent', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/*', function(req, res){
|
||||
var keys = Object.keys(req.params).sort();
|
||||
res.send(keys.map(function(k){ return [k, req.params[k]] }));
|
||||
});
|
||||
|
||||
app.use('/user/id:(\\d+)/name:(\\w+)', router);
|
||||
|
||||
request(app)
|
||||
.get('/user/id:10/name:tj/profile')
|
||||
.expect(200, '[["0","10"],["1","tj"],["2","profile"]]', done);
|
||||
})
|
||||
|
||||
it('should ignore invalid incoming req.params', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/:name', function(req, res){
|
||||
var keys = Object.keys(req.params).sort();
|
||||
res.send(keys.map(function(k){ return [k, req.params[k]] }));
|
||||
});
|
||||
|
||||
app.use('/user/', function (req, res, next) {
|
||||
req.params = 3; // wat?
|
||||
router(req, res, next);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/tj')
|
||||
.expect(200, '[["name","tj"]]', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('trailing slashes', function(){
|
||||
it('should be optional by default', function(done){
|
||||
var app = express();
|
||||
@@ -534,6 +666,30 @@ describe('app.router', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('when next("route") is called', function(){
|
||||
it('should jump to next route', function(done){
|
||||
var app = express()
|
||||
|
||||
function fn(req, res, next){
|
||||
res.set('X-Hit', '1')
|
||||
next('route')
|
||||
}
|
||||
|
||||
app.get('/foo', fn, function(req, res, next){
|
||||
res.end('failure')
|
||||
});
|
||||
|
||||
app.get('/foo', function(req, res){
|
||||
res.end('success')
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('X-Hit', '1')
|
||||
.expect(200, 'success', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when next(err) is called', function(){
|
||||
it('should break out of app.router', function(done){
|
||||
var app = express()
|
||||
@@ -588,6 +744,45 @@ describe('app.router', function(){
|
||||
.expect('editing user 12', done);
|
||||
})
|
||||
|
||||
it('should run in order added', function(done){
|
||||
var app = express();
|
||||
var path = [];
|
||||
|
||||
app.get('*', function(req, res, next){
|
||||
path.push(0);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res, next){
|
||||
path.push(1);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(req, res, next){
|
||||
path.push(2);
|
||||
next();
|
||||
});
|
||||
|
||||
app.all('/user/:id', function(req, res, next){
|
||||
path.push(3);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('*', function(req, res, next){
|
||||
path.push(4);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(req, res, next){
|
||||
path.push(5);
|
||||
res.end(path.join(','))
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/1')
|
||||
.expect(200, '0,1,2,3,4,5', done);
|
||||
})
|
||||
|
||||
it('should be chainable', function(){
|
||||
var app = express();
|
||||
app.get('/', function(){}).should.equal(app);
|
||||
|
||||
@@ -15,6 +15,11 @@ describe('app', function(){
|
||||
app.use(blog);
|
||||
})
|
||||
|
||||
it('should reject numbers', function(){
|
||||
var app = express();
|
||||
app.use.bind(app, 3).should.throw(/Number/);
|
||||
})
|
||||
|
||||
describe('.use(app)', function(){
|
||||
it('should mount the app', function(done){
|
||||
var blog = express()
|
||||
|
||||
@@ -13,6 +13,29 @@ describe('config', function(){
|
||||
var app = express();
|
||||
app.set('foo', undefined).should.equal(app);
|
||||
})
|
||||
|
||||
describe('"etag"', function(){
|
||||
it('should throw on bad value', function(){
|
||||
var app = express()
|
||||
app.set.bind(app, 'etag', 42).should.throw(/unknown value/)
|
||||
})
|
||||
|
||||
it('should set "etag fn"', function(){
|
||||
var app = express()
|
||||
var fn = function(){}
|
||||
app.set('etag', fn)
|
||||
app.get('etag fn').should.equal(fn)
|
||||
})
|
||||
})
|
||||
|
||||
describe('"trust proxy"', function(){
|
||||
it('should set "trust proxy fn"', function(){
|
||||
var app = express()
|
||||
var fn = function(){}
|
||||
app.set('trust proxy', fn)
|
||||
app.get('trust proxy fn').should.equal(fn)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.get()', function(){
|
||||
@@ -91,4 +114,4 @@ describe('config', function(){
|
||||
app.disabled('foo').should.be.false;
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
var express = require('../');
|
||||
var request = require('supertest');
|
||||
var assert = require('assert');
|
||||
var should = require('should');
|
||||
|
||||
describe('exports', function(){
|
||||
it('should expose Router', function(){
|
||||
@@ -50,4 +50,12 @@ describe('exports', function(){
|
||||
.get('/')
|
||||
.expect('bar', done);
|
||||
})
|
||||
|
||||
it('should throw on old middlewares', function(){
|
||||
var error;
|
||||
try { express.bodyParser; } catch (e) { error = e; }
|
||||
should(error).have.property('message');
|
||||
error.message.should.containEql('middleware');
|
||||
error.message.should.containEql('bodyParser');
|
||||
})
|
||||
})
|
||||
|
||||
@@ -41,4 +41,4 @@ describe('middleware', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('req', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
|
||||
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -23,7 +23,7 @@ describe('req', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
|
||||
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -36,7 +36,7 @@ describe('req', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
|
||||
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
|
||||
});
|
||||
|
||||
request(app)
|
||||
|
||||
49
test/req.acceptsCharsets.js
Normal file
49
test/req.acceptsCharsets.js
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptsCharsets(type)', function(){
|
||||
describe('when Accept-Charset is not present', function(){
|
||||
it('should return true', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('yes', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when Accept-Charset is not present', function(){
|
||||
it('should return true when present', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Charset', 'foo, bar, utf-8')
|
||||
.expect('yes', done);
|
||||
})
|
||||
|
||||
it('should return false otherwise', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Charset', 'foo, bar')
|
||||
.expect('no', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
36
test/req.acceptsEncoding.js
Normal file
36
test/req.acceptsEncoding.js
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptsEncoding', function(){
|
||||
it('should be true if encoding accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsEncoding('gzip').should.be.ok;
|
||||
req.acceptsEncoding('deflate').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', ' gzip, deflate')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should be false if encoding not accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsEncoding('bogus').should.not.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', ' gzip, deflate')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
36
test/req.acceptsEncodings.js
Normal file
36
test/req.acceptsEncodings.js
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptsEncodingss', function(){
|
||||
it('should be true if encoding accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsEncodings('gzip').should.be.ok;
|
||||
req.acceptsEncodings('deflate').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', ' gzip, deflate')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should be false if encoding not accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsEncodings('bogus').should.not.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', ' gzip, deflate')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
53
test/req.acceptsLanguage.js
Normal file
53
test/req.acceptsLanguage.js
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptsLanguage', function(){
|
||||
it('should be true if language accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguage('en-us').should.be.ok;
|
||||
req.acceptsLanguage('en').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Language', 'en;q=.5, en-us')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should be false if language not accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguage('es').should.not.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Language', 'en;q=.5, en-us')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
describe('when Accept-Language is not present', function(){
|
||||
it('should always return true', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguage('en').should.be.ok;
|
||||
req.acceptsLanguage('es').should.be.ok;
|
||||
req.acceptsLanguage('jp').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
53
test/req.acceptsLanguages.js
Normal file
53
test/req.acceptsLanguages.js
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptsLanguages', function(){
|
||||
it('should be true if language accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguages('en-us').should.be.ok;
|
||||
req.acceptsLanguages('en').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Language', 'en;q=.5, en-us')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should be false if language not accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguages('es').should.not.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Language', 'en;q=.5, en-us')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
describe('when Accept-Language is not present', function(){
|
||||
it('should always return true', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguages('en').should.be.ok;
|
||||
req.acceptsLanguages('es').should.be.ok;
|
||||
req.acceptsLanguages('jp').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
87
test/req.baseUrl.js
Normal file
87
test/req.baseUrl.js
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('req', function(){
|
||||
describe('.baseUrl', function(){
|
||||
it('should be empty for top-level route', function(done){
|
||||
var app = express()
|
||||
|
||||
app.get('/:a', function(req, res){
|
||||
res.end(req.baseUrl)
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect(200, '', done)
|
||||
})
|
||||
|
||||
it('should contain lower path', function(done){
|
||||
var app = express()
|
||||
var sub = express.Router()
|
||||
|
||||
sub.get('/:b', function(req, res){
|
||||
res.end(req.baseUrl)
|
||||
})
|
||||
app.use('/:a', sub)
|
||||
|
||||
request(app)
|
||||
.get('/foo/bar')
|
||||
.expect(200, '/foo', done);
|
||||
})
|
||||
|
||||
it('should contain full lower path', function(done){
|
||||
var app = express()
|
||||
var sub1 = express.Router()
|
||||
var sub2 = express.Router()
|
||||
var sub3 = express.Router()
|
||||
|
||||
sub3.get('/:d', function(req, res){
|
||||
res.end(req.baseUrl)
|
||||
})
|
||||
sub2.use('/:c', sub3)
|
||||
sub1.use('/:b', sub2)
|
||||
app.use('/:a', sub1)
|
||||
|
||||
request(app)
|
||||
.get('/foo/bar/baz/zed')
|
||||
.expect(200, '/foo/bar/baz', done);
|
||||
})
|
||||
|
||||
it('should travel through routers correctly', function(done){
|
||||
var urls = []
|
||||
var app = express()
|
||||
var sub1 = express.Router()
|
||||
var sub2 = express.Router()
|
||||
var sub3 = express.Router()
|
||||
|
||||
sub3.get('/:d', function(req, res, next){
|
||||
urls.push('0@' + req.baseUrl)
|
||||
next()
|
||||
})
|
||||
sub2.use('/:c', sub3)
|
||||
sub1.use('/', function(req, res, next){
|
||||
urls.push('1@' + req.baseUrl)
|
||||
next()
|
||||
})
|
||||
sub1.use('/bar', sub2)
|
||||
sub1.use('/bar', function(req, res, next){
|
||||
urls.push('2@' + req.baseUrl)
|
||||
next()
|
||||
})
|
||||
app.use(function(req, res, next){
|
||||
urls.push('3@' + req.baseUrl)
|
||||
next()
|
||||
})
|
||||
app.use('/:a', sub1)
|
||||
app.use(function(req, res, next){
|
||||
urls.push('4@' + req.baseUrl)
|
||||
res.end(urls.join(','))
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/foo/bar/baz/zed')
|
||||
.expect(200, '3@,1@/foo,0@/foo/bar/baz,2@/foo/bar,4@', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -6,15 +6,16 @@ describe('req', function(){
|
||||
describe('.fresh', function(){
|
||||
it('should return true when the resource is not modified', function(done){
|
||||
var app = express();
|
||||
var etag = '"12345"';
|
||||
|
||||
app.use(function(req, res){
|
||||
res.set('ETag', '12345');
|
||||
res.set('ETag', etag);
|
||||
res.send(req.fresh);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('If-None-Match', '12345')
|
||||
.set('If-None-Match', etag)
|
||||
.expect(304, done);
|
||||
})
|
||||
|
||||
@@ -22,14 +23,14 @@ describe('req', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.set('ETag', '123');
|
||||
res.set('ETag', '"123"');
|
||||
res.send(req.fresh);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('If-None-Match', '12345')
|
||||
.expect('false', done);
|
||||
.set('If-None-Match', '"12345"')
|
||||
.expect(200, 'false', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -69,5 +69,70 @@ describe('req', function(){
|
||||
.set('Host', '[::1]:3000')
|
||||
.expect('[::1]', done);
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is enabled', function(){
|
||||
it('should respect X-Forwarded-Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'example.com')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
|
||||
it('should ignore X-Forwarded-Host if socket addr not trusted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', '10.0.0.1');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'example.com')
|
||||
.expect('localhost', done);
|
||||
})
|
||||
|
||||
it('should default to Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'example.com')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
it('should ignore X-Forwarded-Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'evil')
|
||||
.expect('localhost', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
138
test/req.hostname.js
Normal file
138
test/req.hostname.js
Normal file
@@ -0,0 +1,138 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest')
|
||||
, assert = require('assert');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.hostname', function(){
|
||||
it('should return the Host when present', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.hostname);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Host', 'example.com')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
|
||||
it('should strip port number', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.hostname);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Host', 'example.com:3000')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
|
||||
it('should return undefined otherwise', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.headers.host = null;
|
||||
res.end(String(req.hostname));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.expect('undefined', done);
|
||||
})
|
||||
|
||||
it('should work with IPv6 Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.hostname);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Host', '[::1]')
|
||||
.expect('[::1]', done);
|
||||
})
|
||||
|
||||
it('should work with IPv6 Host and port', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.hostname);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Host', '[::1]:3000')
|
||||
.expect('[::1]', done);
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is enabled', function(){
|
||||
it('should respect X-Forwarded-Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.hostname);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'example.com:3000')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
|
||||
it('should ignore X-Forwarded-Host if socket addr not trusted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', '10.0.0.1');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.hostname);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'example.com')
|
||||
.expect('localhost', done);
|
||||
})
|
||||
|
||||
it('should default to Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.hostname);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'example.com')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
it('should ignore X-Forwarded-Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.hostname);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'evil')
|
||||
.expect('localhost', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -20,6 +20,21 @@ describe('req', function(){
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('client', done);
|
||||
})
|
||||
|
||||
it('should return the addr after trusted proxy', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', 2);
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ip);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('p1', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
|
||||
@@ -20,6 +20,21 @@ describe('req', function(){
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('["client","p1","p2"]', done);
|
||||
})
|
||||
|
||||
it('should stop at first untrusted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', 2);
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ips);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('["p1","p2"]', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('req', function(){
|
||||
it('should check req.body', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(bodyParser());
|
||||
app.use(bodyParser.json());
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.param('name'));
|
||||
|
||||
@@ -32,6 +32,21 @@ describe('req', function(){
|
||||
.expect('https', done);
|
||||
})
|
||||
|
||||
it('should ignore X-Forwarded-Proto if socket addr not trusted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', '10.0.0.1');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.protocol);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-Proto', 'https')
|
||||
.expect('http', done);
|
||||
})
|
||||
|
||||
it('should default to http', function(done){
|
||||
var app = express();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
var express = require('../');
|
||||
var assert = require('assert');
|
||||
var express = require('..');
|
||||
|
||||
function req(ret) {
|
||||
return {
|
||||
@@ -27,5 +28,11 @@ describe('req', function(){
|
||||
ret.type = 'users';
|
||||
req('users=0-').range(Infinity).should.eql(ret);
|
||||
})
|
||||
|
||||
it('should return undefined if no range', function(){
|
||||
var ret = [{ start: 0, end: 50 }, { start: 60, end: 100 }];
|
||||
ret.type = 'bytes';
|
||||
assert(req('').range(120) === undefined);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,15 +6,16 @@ describe('req', function(){
|
||||
describe('.stale', function(){
|
||||
it('should return false when the resource is not modified', function(done){
|
||||
var app = express();
|
||||
var etag = '"12345"';
|
||||
|
||||
app.use(function(req, res){
|
||||
res.set('ETag', '12345');
|
||||
res.set('ETag', etag);
|
||||
res.send(req.stale);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('If-None-Match', '12345')
|
||||
.set('If-None-Match', etag)
|
||||
.expect(304, done);
|
||||
})
|
||||
|
||||
@@ -22,14 +23,14 @@ describe('req', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.set('ETag', '123');
|
||||
res.set('ETag', '"123"');
|
||||
res.send(req.stale);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('If-None-Match', '12345')
|
||||
.expect('true', done);
|
||||
.set('If-None-Match', '"12345"')
|
||||
.expect(200, 'true', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('res', function(){
|
||||
|
||||
app.use(function(req, res){
|
||||
res.attachment('/path/to/image.png');
|
||||
res.send('foo');
|
||||
res.send(new Buffer(4));
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -57,7 +57,7 @@ describe('res', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Disposition', 'attachment;' +
|
||||
' filename=%E6%97%A5%E6%9C%AC%E8%AA%9E.txt;' +
|
||||
' filename="%E6%97%A5%E6%9C%AC%E8%AA%9E.txt";' +
|
||||
' filename*=UTF-8\'\'%E6%97%A5%E6%9C%AC%E8%AA%9E.txt',
|
||||
done);
|
||||
})
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('res', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers['set-cookie'][0].should.not.include('Thu, 01 Jan 1970 00:00:01 GMT');
|
||||
res.headers['set-cookie'][0].should.not.containEql('Thu, 01 Jan 1970 00:00:01 GMT');
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -106,10 +106,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers['set-cookie'][0].should.include('Max-Age=1');
|
||||
done();
|
||||
})
|
||||
.expect('Set-Cookie', /Max-Age=1/, done)
|
||||
})
|
||||
|
||||
it('should not mutate the options object', function(done){
|
||||
|
||||
@@ -14,12 +14,9 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.should.have.header('Content-Type', 'text/html; charset=UTF-8');
|
||||
res.should.have.header('Content-Disposition', 'attachment; filename="user.html"');
|
||||
res.text.should.equal('<p>{{user.name}}</p>');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'text/html; charset=UTF-8')
|
||||
.expect('Content-Disposition', 'attachment; filename="user.html"')
|
||||
.expect(200, '<p>{{user.name}}</p>', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -33,11 +30,9 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.should.have.header('Content-Type', 'text/html; charset=UTF-8');
|
||||
res.should.have.header('Content-Disposition', 'attachment; filename="document"');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'text/html; charset=UTF-8')
|
||||
.expect('Content-Disposition', 'attachment; filename="document"')
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -52,10 +47,11 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.should.have.header('Content-Type', 'text/html; charset=UTF-8');
|
||||
res.should.have.header('Content-Disposition', 'attachment; filename="user.html"');
|
||||
});
|
||||
.expect('Content-Type', 'text/html; charset=UTF-8')
|
||||
.expect('Content-Disposition', 'attachment; filename="user.html"')
|
||||
.expect(200, function(err){
|
||||
assert.ifError(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -70,10 +66,11 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.should.have.header('Content-Type', 'text/html; charset=UTF-8');
|
||||
res.should.have.header('Content-Disposition', 'attachment; filename="document"');
|
||||
});
|
||||
.expect('Content-Type', 'text/html; charset=UTF-8')
|
||||
.expect('Content-Disposition', 'attachment; filename="document"')
|
||||
.expect(200, function(err){
|
||||
assert.ifError(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -77,6 +77,24 @@ describe('res', function(){
|
||||
test(app2);
|
||||
})
|
||||
|
||||
describe('with parameters', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.format({
|
||||
'text/plain; charset=utf-8': function(){ res.send('hey') },
|
||||
'text/html; foo=bar; bar=baz': function(){ res.send('<p>hey</p>') },
|
||||
'application/json; q=0.5': function(){ res.send({ message: 'hey' }) }
|
||||
});
|
||||
});
|
||||
|
||||
app.use(function(err, req, res, next){
|
||||
res.send(err.status, 'Supports: ' + err.types.join(', '));
|
||||
});
|
||||
|
||||
test(app);
|
||||
})
|
||||
|
||||
describe('given .default', function(){
|
||||
it('should be invoked instead of auto-responding', function(done){
|
||||
request(app3)
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/vnd.example+json')
|
||||
.expect('Content-Type', 'application/vnd.example+json; charset=utf-8')
|
||||
.expect(200, '{"hello":"world"}', done);
|
||||
})
|
||||
|
||||
@@ -41,11 +41,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('null');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, 'null', done)
|
||||
})
|
||||
|
||||
it('should respond with json for Number', function(done){
|
||||
@@ -57,12 +54,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(200);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('300');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '300', done)
|
||||
})
|
||||
|
||||
it('should respond with json for String', function(done){
|
||||
@@ -74,12 +67,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(200);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('"str"');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '"str"', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -93,11 +82,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('["foo","bar","baz"]');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '["foo","bar","baz"]', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -111,11 +97,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -135,10 +118,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -159,10 +140,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('{\n "name": "tobi",\n "age": 2\n}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{\n "name": "tobi",\n "age": 2\n}', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -177,12 +156,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"id":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '{"id":1}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -196,12 +171,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"id":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '{"id":1}', done)
|
||||
})
|
||||
|
||||
it('should use status as second number for backwards compat', function(done){
|
||||
@@ -213,12 +184,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('200');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '200', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -46,11 +46,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/?callback[a]=something')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"count":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"count":1}', done)
|
||||
})
|
||||
|
||||
it('should allow renaming callback', function(done){
|
||||
@@ -129,7 +126,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/vnd.example+json')
|
||||
.expect('Content-Type', 'application/vnd.example+json; charset=utf-8')
|
||||
.expect(200, '{"hello":"world"}', done);
|
||||
})
|
||||
|
||||
@@ -157,11 +154,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('null');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, 'null', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -175,11 +169,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('["foo","bar","baz"]');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '["foo","bar","baz"]', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -193,11 +184,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -211,11 +199,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('null');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, 'null', done)
|
||||
})
|
||||
|
||||
it('should respond with json for Number', function(done){
|
||||
@@ -227,12 +212,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(200);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('300');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '300', done)
|
||||
})
|
||||
|
||||
it('should respond with json for String', function(done){
|
||||
@@ -244,12 +225,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(200);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('"str"');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '"str"', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -269,10 +246,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -293,10 +268,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('{\n "name": "tobi",\n "age": 2\n}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{\n "name": "tobi",\n "age": 2\n}', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -311,12 +284,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"id":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '{"id":1}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -330,12 +299,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"id":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '{"id":1}', done)
|
||||
})
|
||||
|
||||
it('should use status as second number for backwards compat', function(done){
|
||||
@@ -347,12 +312,22 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('200');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '200', done)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not override previous Content-Types', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.type('application/vnd.example+json');
|
||||
res.jsonp({ hello: 'world' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('content-type', 'application/vnd.example+json; charset=utf-8')
|
||||
.expect(200, '{"hello":"world"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user