mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
248 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b78bd3d1fd | ||
|
|
4aa2801054 | ||
|
|
4405b849a9 | ||
|
|
5d74a553d6 | ||
|
|
262b60537f | ||
|
|
e77e644224 | ||
|
|
ce89c00cd9 | ||
|
|
4d8093302f | ||
|
|
4370908674 | ||
|
|
f0b679d02d | ||
|
|
d5ad34b0e9 | ||
|
|
c24463d829 | ||
|
|
ebfa00a9c0 | ||
|
|
d23417e6e8 | ||
|
|
91824514ce | ||
|
|
656e214937 | ||
|
|
4b26bbde2d | ||
|
|
7fcc8b190d | ||
|
|
b326ae89df | ||
|
|
869ddd775c | ||
|
|
997fd74e8c | ||
|
|
e3e41a1118 | ||
|
|
6c8bcd5c4e | ||
|
|
95e63ec287 | ||
|
|
ebca5887cc | ||
|
|
8535d3a990 | ||
|
|
13184c4379 | ||
|
|
eaba4eeb70 | ||
|
|
ac56cf4606 | ||
|
|
6dea32cd18 | ||
|
|
5fab60bc6c | ||
|
|
881e1ba660 | ||
|
|
0e488c19df | ||
|
|
2262a18900 | ||
|
|
28c6952d1c | ||
|
|
01e3530a31 | ||
|
|
77c83d0c57 | ||
|
|
b4eaf89186 | ||
|
|
e4debea297 | ||
|
|
1c96e18d20 | ||
|
|
8bb013ec95 | ||
|
|
6c0031bfd8 | ||
|
|
ab6c189504 | ||
|
|
a12ae729bd | ||
|
|
d53a0cd91e | ||
|
|
eabd4564aa | ||
|
|
d40dc651f3 | ||
|
|
68290ee87a | ||
|
|
6614352563 | ||
|
|
0e5f2f84ea | ||
|
|
b1d0c19ca1 | ||
|
|
dfa7ee4732 | ||
|
|
e9539fc780 | ||
|
|
5f7a37ee51 | ||
|
|
ff3a368b2f | ||
|
|
ccc45a74f8 | ||
|
|
cd9d2ec6a9 | ||
|
|
ce7bbae007 | ||
|
|
6a5dd52deb | ||
|
|
6c2f7fb48d | ||
|
|
4dd970578a | ||
|
|
88dfd36eaa | ||
|
|
5759b3e9f5 | ||
|
|
c939a771c0 | ||
|
|
af1043844f | ||
|
|
dd763ec5b8 | ||
|
|
9c2c21aaaf | ||
|
|
366000184f | ||
|
|
4d1ee23f84 | ||
|
|
6f31218ecc | ||
|
|
6cd4859035 | ||
|
|
4f1cd4f73c | ||
|
|
f15bba7309 | ||
|
|
6f0302fb78 | ||
|
|
0e5613363f | ||
|
|
7a7f18c20b | ||
|
|
b766aad112 | ||
|
|
7488e27609 | ||
|
|
bc9d854763 | ||
|
|
2e20a85810 | ||
|
|
a706408208 | ||
|
|
6f91416020 | ||
|
|
2c5ed88c90 | ||
|
|
6d39d0f8a8 | ||
|
|
159ea67713 | ||
|
|
33959ed350 | ||
|
|
be478d348c | ||
|
|
b0e4e641f9 | ||
|
|
94f10c26cb | ||
|
|
efd2dfb8c8 | ||
|
|
3f2454e3df | ||
|
|
61da3c7d0e | ||
|
|
2c140961ab | ||
|
|
6aa4a450ed | ||
|
|
9f292d873e | ||
|
|
ef3e95ca73 | ||
|
|
f45bd632df | ||
|
|
cc18da5cdf | ||
|
|
5603f86edd | ||
|
|
daadf6033b | ||
|
|
590c919204 | ||
|
|
3bcba79e2d | ||
|
|
b8c8ecebb7 | ||
|
|
43e2cd79cb | ||
|
|
653270bb43 | ||
|
|
734bdf5ca1 | ||
|
|
341c1919d9 | ||
|
|
8e46af1b1d | ||
|
|
e4fc09423e | ||
|
|
0300b61fdd | ||
|
|
b09afad7b1 | ||
|
|
0e0b259556 | ||
|
|
bc38d896ea | ||
|
|
b2518fe135 | ||
|
|
63286e1192 | ||
|
|
c00f2f8596 | ||
|
|
91891e3aee | ||
|
|
728917164c | ||
|
|
bf1980f1b4 | ||
|
|
3c1a964362 | ||
|
|
947fb8b274 | ||
|
|
c5193536e5 | ||
|
|
d08fd64190 | ||
|
|
2470ae6c72 | ||
|
|
916a75cf19 | ||
|
|
daacb749c1 | ||
|
|
f29399c4e1 | ||
|
|
f6ac068ab0 | ||
|
|
7eb65eeca2 | ||
|
|
178fe15091 | ||
|
|
381f278d0a | ||
|
|
534fa181c6 | ||
|
|
80847d8c82 | ||
|
|
bb8abf1f90 | ||
|
|
cf41a8f254 | ||
|
|
1716e3b067 | ||
|
|
12f92a50dc | ||
|
|
51d33edb79 | ||
|
|
2a0c35a108 | ||
|
|
d4de82b853 | ||
|
|
e2102263ce | ||
|
|
d2b1a89b4a | ||
|
|
d9937c628a | ||
|
|
276db8c49a | ||
|
|
1768d94a1a | ||
|
|
6bc7574ab5 | ||
|
|
3dca534995 | ||
|
|
4b1b8e420f | ||
|
|
70767b19ac | ||
|
|
7d277c1c15 | ||
|
|
fa1fcd9fec | ||
|
|
2de6514b4b | ||
|
|
d07c06363f | ||
|
|
4e97533fd2 | ||
|
|
d7d6219a1e | ||
|
|
9b18461bbc | ||
|
|
b77aa38c98 | ||
|
|
cbb251377e | ||
|
|
d6ed469de3 | ||
|
|
49284c236b | ||
|
|
be18487f7d | ||
|
|
094ff11949 | ||
|
|
d2d0afff64 | ||
|
|
33bb8fc4b6 | ||
|
|
b97f6eb506 | ||
|
|
621d074bd8 | ||
|
|
d7e7b2e7d7 | ||
|
|
2d6b735b4f | ||
|
|
a3115882d4 | ||
|
|
3d188fe13e | ||
|
|
8327708ec2 | ||
|
|
c8640b3465 | ||
|
|
3ce5f9b493 | ||
|
|
46f0bfc65f | ||
|
|
0b49e7f1fd | ||
|
|
f6f47f428c | ||
|
|
20635d03fc | ||
|
|
4d032cda05 | ||
|
|
4127ba10b0 | ||
|
|
b6ae091bdf | ||
|
|
a206b4e273 | ||
|
|
2b2733c235 | ||
|
|
7fb7bcc0f7 | ||
|
|
0299bee8fa | ||
|
|
6a581c9961 | ||
|
|
4986b1cb4c | ||
|
|
3de4e4276d | ||
|
|
27f195374d | ||
|
|
ff0de5eb27 | ||
|
|
f4ddef1570 | ||
|
|
9eafaa23d8 | ||
|
|
0b12cc0cac | ||
|
|
fdd0ccabe8 | ||
|
|
8c36eab679 | ||
|
|
5c145b5490 | ||
|
|
d7bef52591 | ||
|
|
1576a95e87 | ||
|
|
7f92fe66e0 | ||
|
|
0cf02d4667 | ||
|
|
ef52b80d75 | ||
|
|
1ca01c0c47 | ||
|
|
fbceae2716 | ||
|
|
ad3ca25c58 | ||
|
|
666ffc62d8 | ||
|
|
6680132392 | ||
|
|
f13f4652da | ||
|
|
059c068c7b | ||
|
|
49947f1476 | ||
|
|
0dddd772c0 | ||
|
|
0f87c6f392 | ||
|
|
1643ae442c | ||
|
|
2594f3103b | ||
|
|
8473b3c338 | ||
|
|
59cb99e9be | ||
|
|
7119f2b16d | ||
|
|
a57efea173 | ||
|
|
4a4ca7347a | ||
|
|
570f60d36e | ||
|
|
d13e613584 | ||
|
|
9204e1f42a | ||
|
|
22ca953e96 | ||
|
|
7989c883fe | ||
|
|
e05a52078a | ||
|
|
ddac571fdf | ||
|
|
982d24b475 | ||
|
|
552b441f8a | ||
|
|
e8f8ea7e05 | ||
|
|
4f5b27dd81 | ||
|
|
cca88a7c47 | ||
|
|
ea427c1bb4 | ||
|
|
0bd6c311cf | ||
|
|
7f606ebf29 | ||
|
|
a3b5adcf4a | ||
|
|
1150ca7264 | ||
|
|
4aea02310a | ||
|
|
17cea29013 | ||
|
|
8449f23f0d | ||
|
|
2cb029f896 | ||
|
|
7e32fa1be6 | ||
|
|
1168d0bb8b | ||
|
|
7d0f1c3db9 | ||
|
|
19abf7684b | ||
|
|
c652cf7eed | ||
|
|
19fd6f85b0 | ||
|
|
b6c5b0511f | ||
|
|
0e42a37edd | ||
|
|
b24ed15878 | ||
|
|
12e070e39a |
38
.gitignore
vendored
38
.gitignore
vendored
@@ -1,16 +1,26 @@
|
||||
coverage/
|
||||
.DS_Store
|
||||
*.seed
|
||||
# OS X
|
||||
.DS_Store*
|
||||
Icon?
|
||||
._*
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Linux
|
||||
.directory
|
||||
*~
|
||||
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
*.log
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.swp
|
||||
*.swo
|
||||
*.gz
|
||||
|
||||
|
||||
# Coveralls
|
||||
coverage
|
||||
|
||||
# Benchmarking
|
||||
benchmarks/graphs
|
||||
testing
|
||||
node_modules/
|
||||
testing
|
||||
test.js
|
||||
.idea
|
||||
|
||||
11
.npmignore
11
.npmignore
@@ -1,11 +0,0 @@
|
||||
.git*
|
||||
benchmarks/
|
||||
coverage/
|
||||
docs/
|
||||
examples/
|
||||
support/
|
||||
test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
.travis.yml
|
||||
Contributing.md
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
## Website Issues
|
||||
|
||||
Issues for the expressjs.com website go here https://github.com/visionmedia/expressjs.com
|
||||
Issues for the expressjs.com website go here https://github.com/strongloop/expressjs.com
|
||||
|
||||
## PRs and Code contributions
|
||||
|
||||
|
||||
576
History.md
576
History.md
@@ -1,3 +1,270 @@
|
||||
4.10.6 / 2014-12-12
|
||||
===================
|
||||
|
||||
* Fix exception in `req.fresh`/`req.stale` without response headers
|
||||
|
||||
4.10.5 / 2014-12-10
|
||||
===================
|
||||
|
||||
* Fix `res.send` double-calling `res.end` for `HEAD` requests
|
||||
* deps: accepts@~1.1.4
|
||||
- deps: mime-types@~2.0.4
|
||||
* deps: type-is@~1.5.4
|
||||
- deps: mime-types@~2.0.4
|
||||
|
||||
4.10.4 / 2014-11-24
|
||||
===================
|
||||
|
||||
* Fix `res.sendfile` logging standard write errors
|
||||
|
||||
4.10.3 / 2014-11-23
|
||||
===================
|
||||
|
||||
* Fix `res.sendFile` logging standard write errors
|
||||
* deps: etag@~1.5.1
|
||||
* deps: proxy-addr@~1.0.4
|
||||
- deps: ipaddr.js@0.1.5
|
||||
* deps: qs@2.3.3
|
||||
- Fix `arrayLimit` behavior
|
||||
|
||||
4.10.2 / 2014-11-09
|
||||
===================
|
||||
|
||||
* Correctly invoke async router callback asynchronously
|
||||
* deps: accepts@~1.1.3
|
||||
- deps: mime-types@~2.0.3
|
||||
* deps: type-is@~1.5.3
|
||||
- deps: mime-types@~2.0.3
|
||||
|
||||
4.10.1 / 2014-10-28
|
||||
===================
|
||||
|
||||
* Fix handling of URLs containing `://` in the path
|
||||
* deps: qs@2.3.2
|
||||
- Fix parsing of mixed objects and values
|
||||
|
||||
4.10.0 / 2014-10-23
|
||||
===================
|
||||
|
||||
* Add support for `app.set('views', array)`
|
||||
- Views are looked up in sequence in array of directories
|
||||
* Fix `res.send(status)` to mention `res.sendStatus(status)`
|
||||
* Fix handling of invalid empty URLs
|
||||
* Use `content-disposition` module for `res.attachment`/`res.download`
|
||||
- Sends standards-compliant `Content-Disposition` header
|
||||
- Full Unicode support
|
||||
* Use `path.resolve` in view lookup
|
||||
* deps: debug@~2.1.0
|
||||
- Implement `DEBUG_FD` env variable support
|
||||
* deps: depd@~1.0.0
|
||||
* deps: etag@~1.5.0
|
||||
- Improve string performance
|
||||
- Slightly improve speed for weak ETags over 1KB
|
||||
* deps: finalhandler@0.3.2
|
||||
- Terminate in progress response only on error
|
||||
- Use `on-finished` to determine request status
|
||||
- deps: debug@~2.1.0
|
||||
- deps: on-finished@~2.1.1
|
||||
* deps: on-finished@~2.1.1
|
||||
- Fix handling of pipelined requests
|
||||
* deps: qs@2.3.0
|
||||
- Fix parsing of mixed implicit and explicit arrays
|
||||
* deps: send@0.10.1
|
||||
- deps: debug@~2.1.0
|
||||
- deps: depd@~1.0.0
|
||||
- deps: etag@~1.5.0
|
||||
- deps: on-finished@~2.1.1
|
||||
* deps: serve-static@~1.7.1
|
||||
- deps: send@0.10.1
|
||||
|
||||
4.9.8 / 2014-10-17
|
||||
==================
|
||||
|
||||
* Fix `res.redirect` body when redirect status specified
|
||||
* deps: accepts@~1.1.2
|
||||
- Fix error when media type has invalid parameter
|
||||
- deps: negotiator@0.4.9
|
||||
|
||||
4.9.7 / 2014-10-10
|
||||
==================
|
||||
|
||||
* Fix using same param name in array of paths
|
||||
|
||||
4.9.6 / 2014-10-08
|
||||
==================
|
||||
|
||||
* deps: accepts@~1.1.1
|
||||
- deps: mime-types@~2.0.2
|
||||
- deps: negotiator@0.4.8
|
||||
* deps: serve-static@~1.6.4
|
||||
- Fix redirect loop when index file serving disabled
|
||||
* deps: type-is@~1.5.2
|
||||
- deps: mime-types@~2.0.2
|
||||
|
||||
4.9.5 / 2014-09-24
|
||||
==================
|
||||
|
||||
* deps: etag@~1.4.0
|
||||
* deps: proxy-addr@~1.0.3
|
||||
- Use `forwarded` npm module
|
||||
* deps: send@0.9.3
|
||||
- deps: etag@~1.4.0
|
||||
* deps: serve-static@~1.6.3
|
||||
- deps: send@0.9.3
|
||||
|
||||
4.9.4 / 2014-09-19
|
||||
==================
|
||||
|
||||
* deps: qs@2.2.4
|
||||
- Fix issue with object keys starting with numbers truncated
|
||||
|
||||
4.9.3 / 2014-09-18
|
||||
==================
|
||||
|
||||
* deps: proxy-addr@~1.0.2
|
||||
- Fix a global leak when multiple subnets are trusted
|
||||
- deps: ipaddr.js@0.1.3
|
||||
|
||||
4.9.2 / 2014-09-17
|
||||
==================
|
||||
|
||||
* Fix regression for empty string `path` in `app.use`
|
||||
* Fix `router.use` to accept array of middleware without path
|
||||
* Improve error message for bad `app.use` arguments
|
||||
|
||||
4.9.1 / 2014-09-16
|
||||
==================
|
||||
|
||||
* Fix `app.use` to accept array of middleware without path
|
||||
* deps: depd@0.4.5
|
||||
* deps: etag@~1.3.1
|
||||
* deps: send@0.9.2
|
||||
- deps: depd@0.4.5
|
||||
- deps: etag@~1.3.1
|
||||
- deps: range-parser@~1.0.2
|
||||
* deps: serve-static@~1.6.2
|
||||
- deps: send@0.9.2
|
||||
|
||||
4.9.0 / 2014-09-08
|
||||
==================
|
||||
|
||||
* Add `res.sendStatus`
|
||||
* Invoke callback for sendfile when client aborts
|
||||
- Applies to `res.sendFile`, `res.sendfile`, and `res.download`
|
||||
- `err` will be populated with request aborted error
|
||||
* Support IP address host in `req.subdomains`
|
||||
* Use `etag` to generate `ETag` headers
|
||||
* deps: accepts@~1.1.0
|
||||
- update `mime-types`
|
||||
* deps: cookie-signature@1.0.5
|
||||
* deps: debug@~2.0.0
|
||||
* deps: finalhandler@0.2.0
|
||||
- Set `X-Content-Type-Options: nosniff` header
|
||||
- deps: debug@~2.0.0
|
||||
* deps: fresh@0.2.4
|
||||
* deps: media-typer@0.3.0
|
||||
- Throw error when parameter format invalid on parse
|
||||
* deps: qs@2.2.3
|
||||
- Fix issue where first empty value in array is discarded
|
||||
* deps: range-parser@~1.0.2
|
||||
* deps: send@0.9.1
|
||||
- Add `lastModified` option
|
||||
- Use `etag` to generate `ETag` header
|
||||
- deps: debug@~2.0.0
|
||||
- deps: fresh@0.2.4
|
||||
* deps: serve-static@~1.6.1
|
||||
- Add `lastModified` option
|
||||
- deps: send@0.9.1
|
||||
* deps: type-is@~1.5.1
|
||||
- fix `hasbody` to be true for `content-length: 0`
|
||||
- deps: media-typer@0.3.0
|
||||
- deps: mime-types@~2.0.1
|
||||
* deps: vary@~1.0.0
|
||||
- Accept valid `Vary` header string as `field`
|
||||
|
||||
4.8.8 / 2014-09-04
|
||||
==================
|
||||
|
||||
* deps: send@0.8.5
|
||||
- Fix a path traversal issue when using `root`
|
||||
- Fix malicious path detection for empty string path
|
||||
* deps: serve-static@~1.5.4
|
||||
- deps: send@0.8.5
|
||||
|
||||
4.8.7 / 2014-08-29
|
||||
==================
|
||||
|
||||
* deps: qs@2.2.2
|
||||
- Remove unnecessary cloning
|
||||
|
||||
4.8.6 / 2014-08-27
|
||||
==================
|
||||
|
||||
* deps: qs@2.2.0
|
||||
- Array parsing fix
|
||||
- Performance improvements
|
||||
|
||||
4.8.5 / 2014-08-18
|
||||
==================
|
||||
|
||||
* deps: send@0.8.3
|
||||
- deps: destroy@1.0.3
|
||||
- deps: on-finished@2.1.0
|
||||
* deps: serve-static@~1.5.3
|
||||
- deps: send@0.8.3
|
||||
|
||||
4.8.4 / 2014-08-14
|
||||
==================
|
||||
|
||||
* deps: qs@1.2.2
|
||||
* deps: send@0.8.2
|
||||
- Work around `fd` leak in Node.js 0.10 for `fs.ReadStream`
|
||||
* deps: serve-static@~1.5.2
|
||||
- deps: send@0.8.2
|
||||
|
||||
4.8.3 / 2014-08-10
|
||||
==================
|
||||
|
||||
* deps: parseurl@~1.3.0
|
||||
* deps: qs@1.2.1
|
||||
* deps: serve-static@~1.5.1
|
||||
- Fix parsing of weird `req.originalUrl` values
|
||||
- deps: parseurl@~1.3.0
|
||||
- deps: utils-merge@1.0.0
|
||||
|
||||
4.8.2 / 2014-08-07
|
||||
==================
|
||||
|
||||
* deps: qs@1.2.0
|
||||
- Fix parsing array of objects
|
||||
|
||||
4.8.1 / 2014-08-06
|
||||
==================
|
||||
|
||||
* fix incorrect deprecation warnings on `res.download`
|
||||
* deps: qs@1.1.0
|
||||
- Accept urlencoded square brackets
|
||||
- Accept empty values in implicit array notation
|
||||
|
||||
4.8.0 / 2014-08-05
|
||||
==================
|
||||
|
||||
* add `res.sendFile`
|
||||
- accepts a file system path instead of a URL
|
||||
- requires an absolute path or `root` option specified
|
||||
* deprecate `res.sendfile` -- use `res.sendFile` instead
|
||||
* support mounted app as any argument to `app.use()`
|
||||
* deps: qs@1.0.2
|
||||
- Complete rewrite
|
||||
- Limits array length to 20
|
||||
- Limits object depth to 5
|
||||
- Limits parameters to 1,000
|
||||
* deps: send@0.8.1
|
||||
- Add `extensions` option
|
||||
* deps: serve-static@~1.5.0
|
||||
- Add `extensions` option
|
||||
- deps: send@0.8.1
|
||||
|
||||
4.7.4 / 2014-08-04
|
||||
==================
|
||||
|
||||
@@ -346,6 +613,311 @@
|
||||
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
|
||||
- Router & Route - public API
|
||||
|
||||
3.18.6 / 2014-12-12
|
||||
===================
|
||||
|
||||
* Fix exception in `req.fresh`/`req.stale` without response headers
|
||||
|
||||
3.18.5 / 2014-12-11
|
||||
===================
|
||||
|
||||
* deps: connect@2.27.6
|
||||
- deps: compression@~1.2.2
|
||||
- deps: express-session@~1.9.3
|
||||
- deps: http-errors@~1.2.8
|
||||
- deps: serve-index@~1.5.3
|
||||
- deps: type-is@~1.5.4
|
||||
|
||||
3.18.4 / 2014-11-23
|
||||
===================
|
||||
|
||||
* deps: connect@2.27.4
|
||||
- deps: body-parser@~1.9.3
|
||||
- deps: compression@~1.2.1
|
||||
- deps: errorhandler@~1.2.3
|
||||
- deps: express-session@~1.9.2
|
||||
- deps: qs@2.3.3
|
||||
- deps: serve-favicon@~2.1.7
|
||||
- deps: serve-static@~1.5.1
|
||||
- deps: type-is@~1.5.3
|
||||
* deps: etag@~1.5.1
|
||||
* deps: proxy-addr@~1.0.4
|
||||
- deps: ipaddr.js@0.1.5
|
||||
|
||||
3.18.3 / 2014-11-09
|
||||
===================
|
||||
|
||||
* deps: connect@2.27.3
|
||||
- Correctly invoke async callback asynchronously
|
||||
- deps: csurf@~1.6.3
|
||||
|
||||
3.18.2 / 2014-10-28
|
||||
===================
|
||||
|
||||
* deps: connect@2.27.2
|
||||
- Fix handling of URLs containing `://` in the path
|
||||
- deps: body-parser@~1.9.2
|
||||
- deps: qs@2.3.2
|
||||
|
||||
3.18.1 / 2014-10-22
|
||||
===================
|
||||
|
||||
* Fix internal `utils.merge` deprecation warnings
|
||||
* deps: connect@2.27.1
|
||||
- deps: body-parser@~1.9.1
|
||||
- deps: express-session@~1.9.1
|
||||
- deps: finalhandler@0.3.2
|
||||
- deps: morgan@~1.4.1
|
||||
- deps: qs@2.3.0
|
||||
- deps: serve-static@~1.7.1
|
||||
* deps: send@0.10.1
|
||||
- deps: on-finished@~2.1.1
|
||||
|
||||
3.18.0 / 2014-10-17
|
||||
===================
|
||||
|
||||
* Use `content-disposition` module for `res.attachment`/`res.download`
|
||||
- Sends standards-compliant `Content-Disposition` header
|
||||
- Full Unicode support
|
||||
* Use `etag` module to generate `ETag` headers
|
||||
* deps: connect@2.27.0
|
||||
- Use `http-errors` module for creating errors
|
||||
- Use `utils-merge` module for merging objects
|
||||
- deps: body-parser@~1.9.0
|
||||
- deps: compression@~1.2.0
|
||||
- deps: connect-timeout@~1.4.0
|
||||
- deps: debug@~2.1.0
|
||||
- deps: depd@~1.0.0
|
||||
- deps: express-session@~1.9.0
|
||||
- deps: finalhandler@0.3.1
|
||||
- deps: method-override@~2.3.0
|
||||
- deps: morgan@~1.4.0
|
||||
- deps: response-time@~2.2.0
|
||||
- deps: serve-favicon@~2.1.6
|
||||
- deps: serve-index@~1.5.0
|
||||
- deps: serve-static@~1.7.0
|
||||
* deps: debug@~2.1.0
|
||||
- Implement `DEBUG_FD` env variable support
|
||||
* deps: depd@~1.0.0
|
||||
* deps: send@0.10.0
|
||||
- deps: debug@~2.1.0
|
||||
- deps: depd@~1.0.0
|
||||
- deps: etag@~1.5.0
|
||||
|
||||
3.17.8 / 2014-10-15
|
||||
===================
|
||||
|
||||
* deps: connect@2.26.6
|
||||
- deps: compression@~1.1.2
|
||||
- deps: csurf@~1.6.2
|
||||
- deps: errorhandler@~1.2.2
|
||||
|
||||
3.17.7 / 2014-10-08
|
||||
===================
|
||||
|
||||
* deps: connect@2.26.5
|
||||
- Fix accepting non-object arguments to `logger`
|
||||
- deps: serve-static@~1.6.4
|
||||
|
||||
3.17.6 / 2014-10-02
|
||||
===================
|
||||
|
||||
* deps: connect@2.26.4
|
||||
- deps: morgan@~1.3.2
|
||||
- deps: type-is@~1.5.2
|
||||
|
||||
3.17.5 / 2014-09-24
|
||||
===================
|
||||
|
||||
* deps: connect@2.26.3
|
||||
- deps: body-parser@~1.8.4
|
||||
- deps: serve-favicon@~2.1.5
|
||||
- deps: serve-static@~1.6.3
|
||||
* deps: proxy-addr@~1.0.3
|
||||
- Use `forwarded` npm module
|
||||
* deps: send@0.9.3
|
||||
- deps: etag@~1.4.0
|
||||
|
||||
3.17.4 / 2014-09-19
|
||||
===================
|
||||
|
||||
* deps: connect@2.26.2
|
||||
- deps: body-parser@~1.8.3
|
||||
- deps: qs@2.2.4
|
||||
|
||||
3.17.3 / 2014-09-18
|
||||
===================
|
||||
|
||||
* deps: proxy-addr@~1.0.2
|
||||
- Fix a global leak when multiple subnets are trusted
|
||||
- deps: ipaddr.js@0.1.3
|
||||
|
||||
3.17.2 / 2014-09-15
|
||||
===================
|
||||
|
||||
* Use `crc` instead of `buffer-crc32` for speed
|
||||
* deps: connect@2.26.1
|
||||
- deps: body-parser@~1.8.2
|
||||
- deps: depd@0.4.5
|
||||
- deps: express-session@~1.8.2
|
||||
- deps: morgan@~1.3.1
|
||||
- deps: serve-favicon@~2.1.3
|
||||
- deps: serve-static@~1.6.2
|
||||
* deps: depd@0.4.5
|
||||
* deps: send@0.9.2
|
||||
- deps: depd@0.4.5
|
||||
- deps: etag@~1.3.1
|
||||
- deps: range-parser@~1.0.2
|
||||
|
||||
3.17.1 / 2014-09-08
|
||||
===================
|
||||
|
||||
* Fix error in `req.subdomains` on empty host
|
||||
|
||||
3.17.0 / 2014-09-08
|
||||
===================
|
||||
|
||||
* Support `X-Forwarded-Host` in `req.subdomains`
|
||||
* Support IP address host in `req.subdomains`
|
||||
* deps: connect@2.26.0
|
||||
- deps: body-parser@~1.8.1
|
||||
- deps: compression@~1.1.0
|
||||
- deps: connect-timeout@~1.3.0
|
||||
- deps: cookie-parser@~1.3.3
|
||||
- deps: cookie-signature@1.0.5
|
||||
- deps: csurf@~1.6.1
|
||||
- deps: debug@~2.0.0
|
||||
- deps: errorhandler@~1.2.0
|
||||
- deps: express-session@~1.8.1
|
||||
- deps: finalhandler@0.2.0
|
||||
- deps: fresh@0.2.4
|
||||
- deps: media-typer@0.3.0
|
||||
- deps: method-override@~2.2.0
|
||||
- deps: morgan@~1.3.0
|
||||
- deps: qs@2.2.3
|
||||
- deps: serve-favicon@~2.1.3
|
||||
- deps: serve-index@~1.2.1
|
||||
- deps: serve-static@~1.6.1
|
||||
- deps: type-is@~1.5.1
|
||||
- deps: vhost@~3.0.0
|
||||
* deps: cookie-signature@1.0.5
|
||||
* deps: debug@~2.0.0
|
||||
* deps: fresh@0.2.4
|
||||
* deps: media-typer@0.3.0
|
||||
- Throw error when parameter format invalid on parse
|
||||
* deps: range-parser@~1.0.2
|
||||
* deps: send@0.9.1
|
||||
- Add `lastModified` option
|
||||
- Use `etag` to generate `ETag` header
|
||||
- deps: debug@~2.0.0
|
||||
- deps: fresh@0.2.4
|
||||
* deps: vary@~1.0.0
|
||||
- Accept valid `Vary` header string as `field`
|
||||
|
||||
3.16.10 / 2014-09-04
|
||||
====================
|
||||
|
||||
* deps: connect@2.25.10
|
||||
- deps: serve-static@~1.5.4
|
||||
* deps: send@0.8.5
|
||||
- Fix a path traversal issue when using `root`
|
||||
- Fix malicious path detection for empty string path
|
||||
|
||||
3.16.9 / 2014-08-29
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.9
|
||||
- deps: body-parser@~1.6.7
|
||||
- deps: qs@2.2.2
|
||||
|
||||
3.16.8 / 2014-08-27
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.8
|
||||
- deps: body-parser@~1.6.6
|
||||
- deps: csurf@~1.4.1
|
||||
- deps: qs@2.2.0
|
||||
|
||||
3.16.7 / 2014-08-18
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.7
|
||||
- deps: body-parser@~1.6.5
|
||||
- deps: express-session@~1.7.6
|
||||
- deps: morgan@~1.2.3
|
||||
- deps: serve-static@~1.5.3
|
||||
* deps: send@0.8.3
|
||||
- deps: destroy@1.0.3
|
||||
- deps: on-finished@2.1.0
|
||||
|
||||
3.16.6 / 2014-08-14
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.6
|
||||
- deps: body-parser@~1.6.4
|
||||
- deps: qs@1.2.2
|
||||
- deps: serve-static@~1.5.2
|
||||
* deps: send@0.8.2
|
||||
- Work around `fd` leak in Node.js 0.10 for `fs.ReadStream`
|
||||
|
||||
3.16.5 / 2014-08-11
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.5
|
||||
- Fix backwards compatibility in `logger`
|
||||
|
||||
3.16.4 / 2014-08-10
|
||||
===================
|
||||
|
||||
* Fix original URL parsing in `res.location`
|
||||
* deps: connect@2.25.4
|
||||
- Fix `query` middleware breaking with argument
|
||||
- deps: body-parser@~1.6.3
|
||||
- deps: compression@~1.0.11
|
||||
- deps: connect-timeout@~1.2.2
|
||||
- deps: express-session@~1.7.5
|
||||
- deps: method-override@~2.1.3
|
||||
- deps: on-headers@~1.0.0
|
||||
- deps: parseurl@~1.3.0
|
||||
- deps: qs@1.2.1
|
||||
- deps: response-time@~2.0.1
|
||||
- deps: serve-index@~1.1.6
|
||||
- deps: serve-static@~1.5.1
|
||||
* deps: parseurl@~1.3.0
|
||||
|
||||
3.16.3 / 2014-08-07
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.3
|
||||
- deps: multiparty@3.3.2
|
||||
|
||||
3.16.2 / 2014-08-07
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.2
|
||||
- deps: body-parser@~1.6.2
|
||||
- deps: qs@1.2.0
|
||||
|
||||
3.16.1 / 2014-08-06
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.1
|
||||
- deps: body-parser@~1.6.1
|
||||
- deps: qs@1.1.0
|
||||
|
||||
3.16.0 / 2014-08-05
|
||||
===================
|
||||
|
||||
* deps: connect@2.25.0
|
||||
- deps: body-parser@~1.6.0
|
||||
- deps: compression@~1.0.10
|
||||
- deps: csurf@~1.4.0
|
||||
- deps: express-session@~1.7.4
|
||||
- deps: qs@1.0.2
|
||||
- deps: serve-static@~1.5.0
|
||||
* deps: send@0.8.1
|
||||
- Add `extensions` option
|
||||
|
||||
3.15.3 / 2014-08-04
|
||||
===================
|
||||
|
||||
@@ -1728,7 +2300,7 @@ Shaw]
|
||||
* Added "encoding" option to Request#render(). Closes #299
|
||||
* Added "dump exceptions" setting, which is enabled by default.
|
||||
* Added simple ejs template engine support
|
||||
* Added error reponse support for text/plain, application/json. Closes #297
|
||||
* Added error response support for text/plain, application/json. Closes #297
|
||||
* Added callback function param to Request#error()
|
||||
* Added Request#sendHead()
|
||||
* Added Request#stream()
|
||||
@@ -1942,7 +2514,7 @@ Shaw]
|
||||
* Updated sample chat app to show messages on load
|
||||
* Updated libxmljs parseString -> parseHtmlString
|
||||
* Fixed `make init` to work with older versions of git
|
||||
* Fixed specs can now run independant specs for those who cant build deps. Closes #127
|
||||
* Fixed specs can now run independent specs for those who cant build deps. Closes #127
|
||||
* Fixed issues introduced by the node url module changes. Closes 126.
|
||||
* Fixed two assertions failing due to Collection#keys() returning strings
|
||||
* Fixed faulty Collection#toArray() spec due to keys() returning strings
|
||||
|
||||
103
Readme.md
103
Readme.md
@@ -1,11 +1,11 @@
|
||||
[](https://expressjs.com/)
|
||||
[](http://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
|
||||
[](https://badge.fury.io/js/express)
|
||||
[](https://travis-ci.org/visionmedia/express)
|
||||
[](https://coveralls.io/r/visionmedia/express)
|
||||
[](https://www.gittip.com/dougwilson/)
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
```js
|
||||
var express = require('express')
|
||||
@@ -18,14 +18,34 @@ app.get('/', function (req, res) {
|
||||
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).
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
$ npm install express
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
* Focus on high performance
|
||||
* Super-high test coverage
|
||||
* HTTP helpers (redirection, caching, etc)
|
||||
* View system supporting 14+ template engines
|
||||
* Content negotiation
|
||||
* Executable for generating applications quickly
|
||||
|
||||
## Docs & Community
|
||||
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/strongloop/expressjs.com)]
|
||||
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
|
||||
* [Github Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||
* Visit the [Wiki](https://github.com/strongloop/express/wiki)
|
||||
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
* [한국어 문서](http://expressjs.kr) - [[website repo](https://github.com/Hanul/expressjs.kr)]
|
||||
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/strongloop/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/strongloop/express/wiki/New-features-in-4.x).
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:
|
||||
@@ -54,16 +74,6 @@ $ npm install
|
||||
$ npm start
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
* HTTP helpers (redirection, caching, etc)
|
||||
* View system supporting 14+ template engines
|
||||
* Content negotiation
|
||||
* Focus on high performance
|
||||
* Executable for generating applications quickly
|
||||
* High test coverage
|
||||
|
||||
## Philosophy
|
||||
|
||||
The Express philosophy is to provide small, robust tooling for HTTP servers, making
|
||||
@@ -71,58 +81,55 @@ $ npm start
|
||||
HTTP APIs.
|
||||
|
||||
Express does not force you to use any specific ORM or template engine. With support for over
|
||||
14 template engines via [Consolidate.js](https://github.com/visionmedia/consolidate.js),
|
||||
14 template engines via [Consolidate.js](https://github.com/tj/consolidate.js),
|
||||
you can quickly craft your perfect framework.
|
||||
|
||||
## More Information
|
||||
## Examples
|
||||
|
||||
* [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/)
|
||||
* [한국어 문서](http://expressjs.kr) - [[website repo](https://github.com/Hanul/expressjs.kr)]
|
||||
* 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:
|
||||
To view the examples, clone the Express repo & install the dependancies:
|
||||
|
||||
```bash
|
||||
$ git clone git://github.com/visionmedia/express.git --depth 1
|
||||
$ git clone git://github.com/strongloop/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Then run whichever example you want:
|
||||
|
||||
$ node examples/content-negotiation
|
||||
```bash
|
||||
$ node examples/content-negotiation
|
||||
```
|
||||
|
||||
You can also view live examples here:
|
||||
## Tests
|
||||
|
||||
<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 install the dependancies, then run `npm test`:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Then run the tests:
|
||||
|
||||
```bash
|
||||
$ npm test
|
||||
```
|
||||
|
||||
### Contributors
|
||||
### People
|
||||
|
||||
* 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)
|
||||
The original author of Express is [TJ Holowaychuk](https://github.com/tj) [![TJ's Gratipay][gratipay-image-visionmedia]][gratipay-url-visionmedia]
|
||||
|
||||
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson) [![Doug's Gratipay][gratipay-image-dougwilson]][gratipay-url-dougwilson]
|
||||
|
||||
[List of all contributors](https://github.com/strongloop/express/graphs/contributors)
|
||||
|
||||
### License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[npm-image]: https://img.shields.io/npm/v/express.svg?style=flat
|
||||
[npm-url]: https://npmjs.org/package/express
|
||||
[downloads-image]: https://img.shields.io/npm/dm/express.svg?style=flat
|
||||
[downloads-url]: https://npmjs.org/package/express
|
||||
[travis-image]: https://img.shields.io/travis/strongloop/express.svg?style=flat
|
||||
[travis-url]: https://travis-ci.org/strongloop/express
|
||||
[coveralls-image]: https://img.shields.io/coveralls/strongloop/express.svg?style=flat
|
||||
[coveralls-url]: https://coveralls.io/r/strongloop/express?branch=master
|
||||
[gratipay-image-visionmedia]: https://img.shields.io/gratipay/visionmedia.svg?style=flat
|
||||
[gratipay-url-visionmedia]: https://gratipay.com/visionmedia/
|
||||
[gratipay-image-dougwilson]: https://img.shields.io/gratipay/dougwilson.svg?style=flat
|
||||
[gratipay-url-dougwilson]: https://gratipay.com/dougwilson/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
// check out https://github.com/visionmedia/node-pwd
|
||||
// check out https://github.com/tj/node-pwd
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var app = express();
|
||||
var bodyParser = require('body-parser');
|
||||
var api = express();
|
||||
|
||||
// app middleware
|
||||
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// api middleware
|
||||
|
||||
api.use(logger('dev'));
|
||||
api.use(bodyParser.json());
|
||||
|
||||
/**
|
||||
* CORS support.
|
||||
*/
|
||||
|
||||
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', '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);
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT an existing user.
|
||||
*/
|
||||
|
||||
api.put('/user/:id', function(req, res){
|
||||
console.log(req.body);
|
||||
res.send(204);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
api.listen(3001);
|
||||
|
||||
console.log('app listening on 3000');
|
||||
console.log('api listening on 3001');
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var req = new XMLHttpRequest;
|
||||
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>
|
||||
@@ -9,7 +9,7 @@ var silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
// general config
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// our custom "verbose errors" setting
|
||||
// which we can use in the templates
|
||||
@@ -25,7 +25,7 @@ silent || app.use(logger('dev'));
|
||||
// Routes
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
res.render('index.ejs');
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res, next){
|
||||
@@ -96,7 +96,6 @@ app.use(function(err, req, res, next){
|
||||
res.render('500', { error: err });
|
||||
});
|
||||
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
|
||||
3
examples/error-pages/views/404.ejs
Normal file
3
examples/error-pages/views/404.ejs
Normal file
@@ -0,0 +1,3 @@
|
||||
<% include error_header %>
|
||||
<h2>Cannot find <%= url %></h2>
|
||||
<% include footer %>
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
extends error
|
||||
|
||||
block content
|
||||
h2 Cannot find #{url}
|
||||
8
examples/error-pages/views/500.ejs
Normal file
8
examples/error-pages/views/500.ejs
Normal file
@@ -0,0 +1,8 @@
|
||||
<% include error_header %>
|
||||
<h2>Error: <%= error.message %></h2>
|
||||
<% if (settings['verbose errors']) { %>
|
||||
<pre><%= error.stack %></pre>
|
||||
<% } else { %>
|
||||
<p>An error occurred!</p>
|
||||
<% } %>
|
||||
<% include footer %>
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
// note that we extend a different
|
||||
// layout with jade for 4xx & 5xx
|
||||
// responses
|
||||
|
||||
extends error
|
||||
|
||||
block content
|
||||
h1 Error: #{error.message}
|
||||
if settings['verbose errors']
|
||||
pre= error.stack
|
||||
else
|
||||
p An error ocurred!
|
||||
@@ -1,6 +0,0 @@
|
||||
html
|
||||
head
|
||||
title Error
|
||||
body
|
||||
h1 An error occurred!
|
||||
block content
|
||||
8
examples/error-pages/views/error_header.ejs
Normal file
8
examples/error-pages/views/error_header.ejs
Normal file
@@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Error</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>An error occurred!</h1>
|
||||
2
examples/error-pages/views/footer.ejs
Normal file
2
examples/error-pages/views/footer.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
18
examples/error-pages/views/index.ejs
Normal file
18
examples/error-pages/views/index.ejs
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Pages Example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>My Site</h1>
|
||||
<h2>Pages Example</h2>
|
||||
|
||||
<ul>
|
||||
<li>visit <a href="/500">500</a></li>
|
||||
<li>visit <a href="/404">404</a></li>
|
||||
<li>visit <a href="/403">403</a></li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h2 Pages Example
|
||||
ul
|
||||
li
|
||||
| visit
|
||||
a(href="/500") 500
|
||||
li
|
||||
| visit
|
||||
a(href="/404") 404
|
||||
li
|
||||
| visit
|
||||
a(href='/403') 403
|
||||
@@ -1,6 +0,0 @@
|
||||
html
|
||||
head
|
||||
title Custom Pages Example
|
||||
body
|
||||
h1 My Site
|
||||
block content
|
||||
@@ -20,7 +20,8 @@ function error(err, req, res, next) {
|
||||
if (!test) console.error(err.stack);
|
||||
|
||||
// respond with 500 "Internal Server Error".
|
||||
res.send(500);
|
||||
res.status(500);
|
||||
res.send('Internal Server Error');
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
|
||||
13
examples/multi-router/controllers/api_v1.js
Normal file
13
examples/multi-router/controllers/api_v1.js
Normal file
@@ -0,0 +1,13 @@
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv1 = express.Router();
|
||||
|
||||
apiv1.get('/', function(req, res) {
|
||||
res.send('Hello from APIv1 root route.');
|
||||
});
|
||||
|
||||
apiv1.get('/users', function(req, res) {
|
||||
res.send('List of APIv1 users.');
|
||||
});
|
||||
|
||||
module.exports = apiv1;
|
||||
13
examples/multi-router/controllers/api_v2.js
Normal file
13
examples/multi-router/controllers/api_v2.js
Normal file
@@ -0,0 +1,13 @@
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv2 = express.Router();
|
||||
|
||||
apiv2.get('/', function(req, res) {
|
||||
res.send('Hello from APIv2 root route.');
|
||||
});
|
||||
|
||||
apiv2.get('/users', function(req, res) {
|
||||
res.send('List of APIv2 users.');
|
||||
});
|
||||
|
||||
module.exports = apiv2;
|
||||
16
examples/multi-router/index.js
Normal file
16
examples/multi-router/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
var express = require('../..');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
app.use('/api/v1', require('./controllers/api_v1'));
|
||||
app.use('/api/v2', require('./controllers/api_v2'));
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.send('Hello form root route.');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'ejs';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var pet = db.pets[req.params.pet_id];
|
||||
if (!pet) return next('route');
|
||||
|
||||
15
examples/mvc/controllers/pet/views/edit.ejs
Normal file
15
examples/mvc/controllers/pet/views/edit.ejs
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Edit <%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><%= pet.name %></h1>
|
||||
<form action="/pet/<%= pet.id %>?_method=put" method="post">
|
||||
<label>Name: <input type="text" name="user[name]" value="<%= pet.name %>"></label>
|
||||
<input type="submit" value="Update">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= pet.name
|
||||
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')
|
||||
13
examples/mvc/controllers/pet/views/show.ejs
Normal file
13
examples/mvc/controllers/pet/views/show.ejs
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title><%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><%= pet.name %> <a href="/pet/<%= pet.id %>/edit">edit</a></h1>
|
||||
|
||||
<p>You are viewing <%= pet.name %></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
|
||||
h1= pet.name
|
||||
a(href='/pet/#{pet.id}/edit') edit
|
||||
|
||||
p You are viewing #{pet.name}
|
||||
@@ -7,15 +7,23 @@ var app = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
var site = require('./site');
|
||||
var post = require('./post');
|
||||
var user = require('./user');
|
||||
|
||||
module.exports = app;
|
||||
|
||||
// Config
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', __dirname + '/views');
|
||||
app.use(logger('dev'));
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.use(express.logger('dev'));
|
||||
}
|
||||
|
||||
app.use(methodOverride('_method'));
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
@@ -27,7 +35,7 @@ app.get('/', site.index);
|
||||
|
||||
// User
|
||||
|
||||
app.all('/users', user.list);
|
||||
app.get('/users', user.list);
|
||||
app.all('/user/:id/:op?', user.load);
|
||||
app.get('/user/:id', user.view);
|
||||
app.get('/user/:id/view', user.view);
|
||||
|
||||
@@ -15,7 +15,9 @@ exports.load = function(req, res, next){
|
||||
if (req.user) {
|
||||
next();
|
||||
} else {
|
||||
next(new Error('cannot find user ' + id));
|
||||
var err = new Error('cannot find user ' + id);
|
||||
err.status = 404;
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -40,4 +42,4 @@ exports.update = function(req, res){
|
||||
req.user.name = user.name;
|
||||
req.user.email = user.email;
|
||||
res.redirect('back');
|
||||
};
|
||||
};
|
||||
|
||||
@@ -47,14 +47,14 @@ app.get('/search/:query?', function(req, res){
|
||||
});
|
||||
|
||||
/**
|
||||
* GET client javascript. Here we use sendfile()
|
||||
* GET client javascript. Here we use sendFile()
|
||||
* because serving __dirname with the static() middleware
|
||||
* would also mean serving our server "index.js" and the "search.jade"
|
||||
* template.
|
||||
*/
|
||||
|
||||
app.get('/client.js', function(req, res){
|
||||
res.sendfile(__dirname + '/client.js');
|
||||
res.sendFile(__dirname + '/client.js');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -34,13 +34,13 @@ function GithubView(name, options){
|
||||
GithubView.prototype.render = function(options, fn){
|
||||
var self = this;
|
||||
var opts = {
|
||||
host: 'rawgithub.com',
|
||||
port: 80,
|
||||
host: 'raw.githubusercontent.com',
|
||||
port: 443,
|
||||
path: this.path,
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
http.request(opts, function(res) {
|
||||
https.request(opts, function(res) {
|
||||
var buf = '';
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function(str){ buf += str });
|
||||
|
||||
@@ -23,7 +23,7 @@ app.engine('md', function(str, options, fn){
|
||||
});
|
||||
|
||||
// pointing to a particular github repo to load files from it
|
||||
app.set('views', 'visionmedia/express');
|
||||
app.set('views', 'strongloop/express');
|
||||
|
||||
// register a new view constructor
|
||||
app.set('view', GithubView);
|
||||
@@ -36,7 +36,7 @@ app.get('/', function(req, res){
|
||||
});
|
||||
|
||||
app.get('/Readme.md', function(req, res){
|
||||
// rendering a view from https://github.com/visionmedia/express/blob/master/Readme.md
|
||||
// rendering a view from https://github.com/strongloop/express/blob/master/Readme.md
|
||||
res.render('Readme.md');
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ function error(status, msg) {
|
||||
app.use('/api', function(req, res, next){
|
||||
var key = req.query['api-key'];
|
||||
|
||||
// key isnt present
|
||||
// key isn't present
|
||||
if (!key) return next(error(400, 'api key required'));
|
||||
|
||||
// key is invalid
|
||||
@@ -49,7 +49,7 @@ var apiKeys = ['foo', 'bar', 'baz'];
|
||||
// these two objects will serve as our faux database
|
||||
|
||||
var repos = [
|
||||
{ name: 'express', url: 'http://github.com/visionmedia/express' }
|
||||
{ name: 'express', url: 'http://github.com/strongloop/express' }
|
||||
, { name: 'stylus', url: 'http://github.com/learnboost/stylus' }
|
||||
, { name: 'cluster', url: 'http://github.com/learnboost/cluster' }
|
||||
];
|
||||
@@ -93,14 +93,16 @@ app.get('/api/user/:name/repos', function(req, res, next){
|
||||
app.use(function(err, req, res, next){
|
||||
// whatever you want here, feel free to populate
|
||||
// properties on `err` to treat it differently in here.
|
||||
res.send(err.status || 500, { error: err.message });
|
||||
res.status(err.status || 500);
|
||||
res.send({ error: err.message });
|
||||
});
|
||||
|
||||
// our custom JSON 404 middleware. Since it's placed last
|
||||
// it will be the last middleware called, if all others
|
||||
// invoke next() and do not respond.
|
||||
app.use(function(req, res){
|
||||
res.send(404, { error: "Lame, can't find that" });
|
||||
res.status(404);
|
||||
res.send({ error: "Lame, can't find that" });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
var finalhandler = require('finalhandler');
|
||||
var mixin = require('utils-merge');
|
||||
var flatten = require('./utils').flatten;
|
||||
var Router = require('./router');
|
||||
var methods = require('methods');
|
||||
var middleware = require('./middleware/init');
|
||||
@@ -15,7 +15,9 @@ var compileETag = require('./utils').compileETag;
|
||||
var compileQueryParser = require('./utils').compileQueryParser;
|
||||
var compileTrust = require('./utils').compileTrust;
|
||||
var deprecate = require('depd')('express');
|
||||
var merge = require('utils-merge');
|
||||
var resolve = require('path').resolve;
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
@@ -149,34 +151,50 @@ app.handle = function(req, res, done) {
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.use = function use(path, fn) {
|
||||
var mount_app;
|
||||
var mount_path;
|
||||
app.use = function use(fn) {
|
||||
var offset = 0;
|
||||
var path = '/';
|
||||
|
||||
// check for .use(path, app) or .use(app) signature
|
||||
if (arguments.length <= 2) {
|
||||
mount_path = typeof path === 'string'
|
||||
? path
|
||||
: '/';
|
||||
mount_app = typeof path === 'function'
|
||||
? path
|
||||
: fn;
|
||||
// default path to '/'
|
||||
// disambiguate app.use([fn])
|
||||
if (typeof fn !== 'function') {
|
||||
var arg = fn;
|
||||
|
||||
while (Array.isArray(arg) && arg.length !== 0) {
|
||||
arg = arg[0];
|
||||
}
|
||||
|
||||
// first arg is the path
|
||||
if (typeof arg !== 'function') {
|
||||
offset = 1;
|
||||
path = fn;
|
||||
}
|
||||
}
|
||||
|
||||
var fns = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (fns.length === 0) {
|
||||
throw new TypeError('app.use() requires middleware functions');
|
||||
}
|
||||
|
||||
// setup router
|
||||
this.lazyrouter();
|
||||
var router = this._router;
|
||||
|
||||
// express app
|
||||
if (mount_app && mount_app.handle && mount_app.set) {
|
||||
debug('.use app under %s', mount_path);
|
||||
mount_app.mountpath = mount_path;
|
||||
mount_app.parent = this;
|
||||
fns.forEach(function (fn) {
|
||||
// non-express app
|
||||
if (!fn || !fn.handle || !fn.set) {
|
||||
return router.use(path, fn);
|
||||
}
|
||||
|
||||
debug('.use app under %s', path);
|
||||
fn.mountpath = path;
|
||||
fn.parent = this;
|
||||
|
||||
// restore .app property on req and res
|
||||
router.use(mount_path, function mounted_app(req, res, next) {
|
||||
router.use(path, function mounted_app(req, res, next) {
|
||||
var orig = req.app;
|
||||
mount_app.handle(req, res, function(err) {
|
||||
fn.handle(req, res, function (err) {
|
||||
req.__proto__ = orig.request;
|
||||
res.__proto__ = orig.response;
|
||||
next(err);
|
||||
@@ -184,13 +202,8 @@ app.use = function use(path, fn) {
|
||||
});
|
||||
|
||||
// mounted an app
|
||||
mount_app.emit('mount', this);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// pass-through use
|
||||
router.use.apply(router, arguments);
|
||||
fn.emit('mount', this);
|
||||
}, this);
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -233,7 +246,7 @@ app.route = function(path){
|
||||
* so if you're using ".ejs" extensions you dont need to do anything.
|
||||
*
|
||||
* Some template engines do not follow this convention, the
|
||||
* [Consolidate.js](https://github.com/visionmedia/consolidate.js)
|
||||
* [Consolidate.js](https://github.com/tj/consolidate.js)
|
||||
* library was created to map all of node's popular template
|
||||
* engines to follow this convention, thus allowing them to
|
||||
* work seamlessly within Express.
|
||||
@@ -264,17 +277,16 @@ app.engine = function(ext, fn){
|
||||
*/
|
||||
|
||||
app.param = function(name, fn){
|
||||
var self = this;
|
||||
self.lazyrouter();
|
||||
this.lazyrouter();
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function(key) {
|
||||
self.param(key, fn);
|
||||
});
|
||||
this.param(key, fn);
|
||||
}, this);
|
||||
return this;
|
||||
}
|
||||
|
||||
self._router.param(name, fn);
|
||||
this._router.param(name, fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -414,7 +426,7 @@ methods.forEach(function(method){
|
||||
this.lazyrouter();
|
||||
|
||||
var route = this._router.route(path);
|
||||
route[method].apply(route, [].slice.call(arguments, 1));
|
||||
route[method].apply(route, slice.call(arguments, 1));
|
||||
return this;
|
||||
};
|
||||
});
|
||||
@@ -433,7 +445,7 @@ app.all = function(path){
|
||||
this.lazyrouter();
|
||||
|
||||
var route = this._router.route(path);
|
||||
var args = [].slice.call(arguments, 1);
|
||||
var args = slice.call(arguments, 1);
|
||||
methods.forEach(function(method){
|
||||
route[method].apply(route, args);
|
||||
});
|
||||
@@ -474,13 +486,15 @@ app.render = function(name, options, fn){
|
||||
}
|
||||
|
||||
// merge app.locals
|
||||
mixin(opts, this.locals);
|
||||
merge(opts, this.locals);
|
||||
|
||||
// merge options._locals
|
||||
if (options._locals) mixin(opts, options._locals);
|
||||
if (options._locals) {
|
||||
merge(opts, options._locals);
|
||||
}
|
||||
|
||||
// merge options
|
||||
mixin(opts, options);
|
||||
merge(opts, options);
|
||||
|
||||
// set .cache unless explicitly provided
|
||||
opts.cache = null == opts.cache
|
||||
@@ -499,7 +513,10 @@ app.render = function(name, options, fn){
|
||||
});
|
||||
|
||||
if (!view.path) {
|
||||
var err = new Error('Failed to lookup view "' + name + '" in views directory "' + view.root + '"');
|
||||
var dirs = Array.isArray(view.root) && view.root.length > 1
|
||||
? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'
|
||||
: 'directory "' + view.root + '"'
|
||||
var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
|
||||
err.view = view;
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var mixin = require('utils-merge');
|
||||
var mixin = require('merge-descriptors');
|
||||
var proto = require('./application');
|
||||
var Route = require('./router/route');
|
||||
var Router = require('./router');
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
var accepts = require('accepts');
|
||||
var deprecate = require('depd')('express');
|
||||
var isIP = require('net').isIP;
|
||||
var typeis = require('type-is');
|
||||
var http = require('http');
|
||||
var fresh = require('fresh');
|
||||
@@ -332,11 +333,16 @@ defineGetter(req, 'ips', function ips() {
|
||||
*/
|
||||
|
||||
defineGetter(req, 'subdomains', function subdomains() {
|
||||
var hostname = this.hostname;
|
||||
|
||||
if (!hostname) return [];
|
||||
|
||||
var offset = this.app.get('subdomain offset');
|
||||
return (this.hostname || '')
|
||||
.split('.')
|
||||
.reverse()
|
||||
.slice(offset);
|
||||
var subdomains = !isIP(hostname)
|
||||
? hostname.split('.').reverse()
|
||||
: [hostname];
|
||||
|
||||
return subdomains.slice(offset);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -406,7 +412,7 @@ defineGetter(req, 'fresh', function(){
|
||||
|
||||
// 2xx or 304 as per rfc2616 14.26
|
||||
if ((s >= 200 && s < 300) || 304 == s) {
|
||||
return fresh(this.headers, this.res._headers);
|
||||
return fresh(this.headers, (this.res._headers || {}));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
286
lib/response.js
286
lib/response.js
@@ -2,22 +2,24 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var contentDisposition = require('content-disposition');
|
||||
var deprecate = require('depd')('express');
|
||||
var escapeHtml = require('escape-html');
|
||||
var http = require('http');
|
||||
var isAbsolute = require('./utils').isAbsolute;
|
||||
var onFinished = require('on-finished');
|
||||
var path = require('path');
|
||||
var mixin = require('utils-merge');
|
||||
var merge = require('utils-merge');
|
||||
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 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 resolve = path.resolve;
|
||||
var vary = require('vary');
|
||||
|
||||
/**
|
||||
@@ -107,7 +109,7 @@ res.send = function send(body) {
|
||||
this.type('txt');
|
||||
}
|
||||
|
||||
deprecate('res.send(status): Use res.status(status).end() instead');
|
||||
deprecate('res.send(status): Use res.sendStatus(status) instead');
|
||||
this.statusCode = chunk;
|
||||
chunk = http.STATUS_CODES[chunk];
|
||||
}
|
||||
@@ -180,14 +182,14 @@ res.send = function send(body) {
|
||||
chunk = '';
|
||||
}
|
||||
|
||||
// skip body for HEAD
|
||||
if (isHead) {
|
||||
// skip body for HEAD
|
||||
this.end();
|
||||
} else {
|
||||
// respond
|
||||
this.end(chunk, encoding);
|
||||
}
|
||||
|
||||
// respond
|
||||
this.end(chunk, encoding);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -301,6 +303,108 @@ res.jsonp = function jsonp(obj) {
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send given HTTP status code.
|
||||
*
|
||||
* Sets the response status to `statusCode` and the body of the
|
||||
* response to the standard description from node's http.STATUS_CODES
|
||||
* or the statusCode number if no description.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.sendStatus(200);
|
||||
*
|
||||
* @param {number} statusCode
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.sendStatus = function sendStatus(statusCode) {
|
||||
var body = http.STATUS_CODES[statusCode] || String(statusCode);
|
||||
|
||||
this.statusCode = statusCode;
|
||||
this.type('txt');
|
||||
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
* Automatically sets the _Content-Type_ response header field.
|
||||
* The callback `fn(err)` is invoked when the transfer is complete
|
||||
* or when an error occurs. Be sure to check `res.sentHeader`
|
||||
* if you wish to attempt responding, as the header and some data
|
||||
* may have already been transferred.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` defaulting to 0 (can be string converted by `ms`)
|
||||
* - `root` root directory for relative filenames
|
||||
* - `headers` object of headers to serve with file
|
||||
* - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
|
||||
*
|
||||
* Other options are passed along to `send`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* The following example illustrates how `res.sendFile()` may
|
||||
* be used as an alternative for the `static()` middleware for
|
||||
* dynamic situations. The code backing `res.sendFile()` is actually
|
||||
* the same code, so HTTP cache support etc is identical.
|
||||
*
|
||||
* app.get('/user/:uid/photos/:file', function(req, res){
|
||||
* var uid = req.params.uid
|
||||
* , file = req.params.file;
|
||||
*
|
||||
* req.user.mayViewFilesFrom(uid, function(yes){
|
||||
* if (yes) {
|
||||
* res.sendFile('/uploads/' + uid + '/' + file);
|
||||
* } else {
|
||||
* res.send(403, 'Sorry! you cant see that.');
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.sendFile = function sendFile(path, options, fn) {
|
||||
var req = this.req;
|
||||
var res = this;
|
||||
var next = req.next;
|
||||
|
||||
if (!path) {
|
||||
throw new TypeError('path argument is required to res.sendFile');
|
||||
}
|
||||
|
||||
// support function as second arg
|
||||
if (typeof options === 'function') {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.root && !isAbsolute(path)) {
|
||||
throw new TypeError('path must be absolute or specify root to res.sendFile');
|
||||
}
|
||||
|
||||
// create file stream
|
||||
var pathname = encodeURI(path);
|
||||
var file = send(req, pathname, options);
|
||||
|
||||
// transfer
|
||||
sendfile(res, file, options, function (err) {
|
||||
if (fn) return fn(err);
|
||||
if (err && err.code === 'EISDIR') return next();
|
||||
|
||||
// next() all but write errors
|
||||
if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
@@ -343,73 +447,36 @@ res.jsonp = function jsonp(obj) {
|
||||
*/
|
||||
|
||||
res.sendfile = function(path, options, fn){
|
||||
options = options || {};
|
||||
var self = this;
|
||||
var req = self.req;
|
||||
var next = this.req.next;
|
||||
var done;
|
||||
|
||||
var req = this.req;
|
||||
var res = this;
|
||||
var next = req.next;
|
||||
|
||||
// support function as second arg
|
||||
if ('function' == typeof options) {
|
||||
if (typeof options === 'function') {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// socket errors
|
||||
req.socket.on('error', error);
|
||||
options = options || {};
|
||||
|
||||
// errors
|
||||
function error(err) {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
// clean up
|
||||
cleanup();
|
||||
|
||||
// callback available
|
||||
if (fn) return fn(err);
|
||||
|
||||
// delegate
|
||||
next(err);
|
||||
}
|
||||
|
||||
// streaming
|
||||
function stream(stream) {
|
||||
if (done) return;
|
||||
cleanup();
|
||||
if (fn) stream.on('end', fn);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
function cleanup() {
|
||||
req.socket.removeListener('error', error);
|
||||
}
|
||||
// create file stream
|
||||
var file = send(req, path, options);
|
||||
|
||||
// transfer
|
||||
var file = send(req, path, options);
|
||||
file.on('error', error);
|
||||
file.on('directory', next);
|
||||
file.on('stream', stream);
|
||||
sendfile(res, file, options, function (err) {
|
||||
if (fn) return fn(err);
|
||||
if (err && err.code === 'EISDIR') return next();
|
||||
|
||||
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);
|
||||
// next() all but write errors
|
||||
if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
res.sendfile = deprecate.function(res.sendfile,
|
||||
'res.sendfile: Use res.sendFile instead');
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path` as an attachment.
|
||||
*
|
||||
@@ -423,9 +490,9 @@ res.sendfile = function(path, options, fn){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.download = function(path, filename, fn){
|
||||
res.download = function download(path, filename, fn) {
|
||||
// support function as second arg
|
||||
if ('function' == typeof filename) {
|
||||
if (typeof filename === 'function') {
|
||||
fn = filename;
|
||||
filename = null;
|
||||
}
|
||||
@@ -437,7 +504,10 @@ res.download = function(path, filename, fn){
|
||||
'Content-Disposition': contentDisposition(filename)
|
||||
};
|
||||
|
||||
return this.sendfile(path, { headers: headers }, fn);
|
||||
// Resolve the full path for sendFile
|
||||
var fullPath = resolve(path);
|
||||
|
||||
return this.sendFile(fullPath, { headers: headers }, fn);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -556,9 +626,13 @@ res.format = function(obj){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.attachment = function(filename){
|
||||
if (filename) this.type(extname(filename));
|
||||
res.attachment = function attachment(filename) {
|
||||
if (filename) {
|
||||
this.type(extname(filename));
|
||||
}
|
||||
|
||||
this.set('Content-Disposition', contentDisposition(filename));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -622,7 +696,7 @@ res.get = function(field){
|
||||
res.clearCookie = function(name, options){
|
||||
var opts = { expires: new Date(1), path: '/' };
|
||||
return this.cookie(name, '', options
|
||||
? mixin(opts, options)
|
||||
? merge(opts, options)
|
||||
: opts);
|
||||
};
|
||||
|
||||
@@ -651,7 +725,7 @@ res.clearCookie = function(name, options){
|
||||
*/
|
||||
|
||||
res.cookie = function(name, val, options){
|
||||
options = mixin({}, options);
|
||||
options = merge({}, options);
|
||||
var secret = this.req.secret;
|
||||
var signed = options.signed;
|
||||
if (signed && !secret) throw new Error('cookieParser("secret") required for signed cookies');
|
||||
@@ -736,7 +810,7 @@ res.redirect = function redirect(url) {
|
||||
status = arguments[0];
|
||||
address = arguments[1];
|
||||
} else {
|
||||
deprecate('res.redirect(ur, status): Use res.redirect(status, url) instead');
|
||||
deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
|
||||
status = arguments[1];
|
||||
}
|
||||
}
|
||||
@@ -748,11 +822,11 @@ res.redirect = function redirect(url) {
|
||||
// Support text/{plain,html} by default
|
||||
this.format({
|
||||
text: function(){
|
||||
body = statusCodes[status] + '. Redirecting to ' + encodeURI(url);
|
||||
body = statusCodes[status] + '. Redirecting to ' + encodeURI(address);
|
||||
},
|
||||
|
||||
html: function(){
|
||||
var u = escapeHtml(url);
|
||||
var u = escapeHtml(address);
|
||||
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
|
||||
},
|
||||
|
||||
@@ -829,3 +903,69 @@ res.render = function(view, options, fn){
|
||||
// render
|
||||
app.render(view, options, fn);
|
||||
};
|
||||
|
||||
// pipe the send file stream
|
||||
function sendfile(res, file, options, callback) {
|
||||
var done = false;
|
||||
|
||||
// directory
|
||||
function ondirectory() {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
var err = new Error('EISDIR, read');
|
||||
err.code = 'EISDIR';
|
||||
callback(err);
|
||||
}
|
||||
|
||||
// errors
|
||||
function onerror(err) {
|
||||
if (done) return;
|
||||
done = true;
|
||||
callback(err);
|
||||
}
|
||||
|
||||
// ended
|
||||
function onend() {
|
||||
if (done) return;
|
||||
done = true;
|
||||
callback();
|
||||
}
|
||||
|
||||
// finished
|
||||
function onfinish(err) {
|
||||
if (err) return onerror(err);
|
||||
if (done) return;
|
||||
|
||||
setImmediate(function () {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
// response finished before end of file
|
||||
var err = new Error('Request aborted');
|
||||
err.code = 'ECONNABORT';
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
file.on('end', onend);
|
||||
file.on('error', onerror);
|
||||
file.on('directory', ondirectory);
|
||||
onFinished(res, onfinish);
|
||||
|
||||
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(res);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -8,9 +9,15 @@ var methods = require('methods');
|
||||
var mixin = require('utils-merge');
|
||||
var debug = require('debug')('express:router');
|
||||
var parseUrl = require('parseurl');
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
*/
|
||||
|
||||
var objectRegExp = /^\[object (\S+)\]$/;
|
||||
var slice = Array.prototype.slice;
|
||||
var toString = Object.prototype.toString;
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
@@ -119,7 +126,7 @@ proto.handle = function(req, res, done) {
|
||||
|
||||
var search = 1 + req.url.indexOf('?');
|
||||
var pathlength = search ? search - 1 : req.url.length;
|
||||
var fqdn = 1 + req.url.substr(0, pathlength).indexOf('://');
|
||||
var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://');
|
||||
var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
|
||||
var idx = 0;
|
||||
var removed = '';
|
||||
@@ -176,7 +183,8 @@ proto.handle = function(req, res, done) {
|
||||
}
|
||||
|
||||
if (!layer) {
|
||||
return done(layerError);
|
||||
setImmediate(done, layerError);
|
||||
return;
|
||||
}
|
||||
|
||||
self.match_layer(layer, req, res, function (err, path) {
|
||||
@@ -402,40 +410,47 @@ proto.process_params = function(layer, called, req, res, done) {
|
||||
proto.use = function use(fn) {
|
||||
var offset = 0;
|
||||
var path = '/';
|
||||
var self = this;
|
||||
|
||||
// default path to '/'
|
||||
// disambiguate router.use([fn])
|
||||
if (typeof fn !== 'function') {
|
||||
offset = 1;
|
||||
path = fn;
|
||||
var arg = fn;
|
||||
|
||||
while (Array.isArray(arg) && arg.length !== 0) {
|
||||
arg = arg[0];
|
||||
}
|
||||
|
||||
// first arg is the path
|
||||
if (typeof arg !== 'function') {
|
||||
offset = 1;
|
||||
path = fn;
|
||||
}
|
||||
}
|
||||
|
||||
var callbacks = utils.flatten(slice.call(arguments, offset));
|
||||
|
||||
if (callbacks.length === 0) {
|
||||
throw new TypeError('Router.use() requires callback function');
|
||||
throw new TypeError('Router.use() requires middleware functions');
|
||||
}
|
||||
|
||||
callbacks.forEach(function (fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
var type = toString.call(fn);
|
||||
var msg = 'Router.use() requires callback function but got a ' + type;
|
||||
throw new TypeError(msg);
|
||||
throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
|
||||
}
|
||||
|
||||
// add the middleware
|
||||
debug('use %s %s', path, fn.name || '<anonymous>');
|
||||
|
||||
var layer = new Layer(path, {
|
||||
sensitive: self.caseSensitive,
|
||||
sensitive: this.caseSensitive,
|
||||
strict: false,
|
||||
end: false
|
||||
}, fn);
|
||||
|
||||
layer.route = undefined;
|
||||
|
||||
self.stack.push(layer);
|
||||
});
|
||||
this.stack.push(layer);
|
||||
}, this);
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -477,6 +492,19 @@ methods.concat('all').forEach(function(method){
|
||||
};
|
||||
});
|
||||
|
||||
// get type for error message
|
||||
function gettype(obj) {
|
||||
var type = typeof obj;
|
||||
|
||||
if (type !== 'object') {
|
||||
return type;
|
||||
}
|
||||
|
||||
// inspect [[Class]] for objects
|
||||
return toString.call(obj)
|
||||
.replace(objectRegExp, '$1');
|
||||
}
|
||||
|
||||
// merge params with parent params
|
||||
function mergeParams(params, parent) {
|
||||
if (typeof parent !== 'object' || !parent) {
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
var pathRegexp = require('path-to-regexp');
|
||||
var debug = require('debug')('express:router:layer');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
*/
|
||||
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Expose `Layer`.
|
||||
*/
|
||||
@@ -89,6 +95,13 @@ Layer.prototype.handle_request = function handle(req, res, next) {
|
||||
*/
|
||||
|
||||
Layer.prototype.match = function match(path) {
|
||||
if (path == null) {
|
||||
// no path, nothing matches
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.regexp.fast_slash) {
|
||||
// fast path non-ending match for / (everything matches)
|
||||
this.params = {};
|
||||
@@ -110,18 +123,20 @@ Layer.prototype.match = function match(path) {
|
||||
|
||||
var keys = this.keys;
|
||||
var params = this.params;
|
||||
var prop;
|
||||
var n = 0;
|
||||
var key;
|
||||
var val;
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
key = keys[i - 1];
|
||||
prop = key
|
||||
? key.name
|
||||
: n++;
|
||||
val = decode_param(m[i]);
|
||||
|
||||
if (key) {
|
||||
params[key.name] = val;
|
||||
} else {
|
||||
params[n++] = val;
|
||||
if (val !== undefined || !(hasOwnProperty.call(params, prop))) {
|
||||
params[prop] = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var pathRegexp = require('path-to-regexp');
|
||||
|
||||
/**
|
||||
* Expose `Layer`.
|
||||
*/
|
||||
|
||||
module.exports = Match;
|
||||
|
||||
function Match(layer, path, params) {
|
||||
this.layer = layer;
|
||||
this.params = {};
|
||||
this.path = path || '';
|
||||
|
||||
if (!params) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var keys = layer.keys;
|
||||
var n = 0;
|
||||
var prop;
|
||||
var key;
|
||||
var val;
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
key = keys[i];
|
||||
val = decode_param(params[i]);
|
||||
prop = key
|
||||
? key.name
|
||||
: n++;
|
||||
|
||||
this.params[prop] = val;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,6 @@ Route.prototype.dispatch = function(req, res, done){
|
||||
*/
|
||||
|
||||
Route.prototype.all = function(){
|
||||
var self = this;
|
||||
var callbacks = utils.flatten([].slice.call(arguments));
|
||||
callbacks.forEach(function(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
@@ -143,16 +142,15 @@ Route.prototype.all = function(){
|
||||
var layer = Layer('/', {}, fn);
|
||||
layer.method = undefined;
|
||||
|
||||
self.methods._all = true;
|
||||
self.stack.push(layer);
|
||||
});
|
||||
this.methods._all = true;
|
||||
this.stack.push(layer);
|
||||
}, this);
|
||||
|
||||
return self;
|
||||
return this;
|
||||
};
|
||||
|
||||
methods.forEach(function(method){
|
||||
Route.prototype[method] = function(){
|
||||
var self = this;
|
||||
var callbacks = utils.flatten([].slice.call(arguments));
|
||||
|
||||
callbacks.forEach(function(fn) {
|
||||
@@ -162,14 +160,14 @@ methods.forEach(function(method){
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
debug('%s %s', method, self.path);
|
||||
debug('%s %s', method, this.path);
|
||||
|
||||
var layer = Layer('/', {}, fn);
|
||||
layer.method = method;
|
||||
|
||||
self.methods[method] = true;
|
||||
self.stack.push(layer);
|
||||
});
|
||||
return self;
|
||||
this.methods[method] = true;
|
||||
this.stack.push(layer);
|
||||
}, this);
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
47
lib/utils.js
47
lib/utils.js
@@ -2,10 +2,11 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var contentDisposition = require('content-disposition');
|
||||
var deprecate = require('depd')('express');
|
||||
var mime = require('send').mime;
|
||||
var crc32 = require('buffer-crc32');
|
||||
var crypto = require('crypto');
|
||||
var basename = require('path').basename;
|
||||
var etag = require('etag');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
var qs = require('qs');
|
||||
var querystring = require('querystring');
|
||||
@@ -20,17 +21,12 @@ var typer = require('media-typer');
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.etag = function etag(body, encoding){
|
||||
if (body.length === 0) {
|
||||
// fast-path empty body
|
||||
return '"1B2M2Y8AsgTpgAmY7PhCfg=="'
|
||||
}
|
||||
exports.etag = function (body, encoding) {
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
: body;
|
||||
|
||||
var hash = crypto
|
||||
.createHash('md5')
|
||||
.update(body, encoding)
|
||||
.digest('base64')
|
||||
return '"' + hash + '"'
|
||||
return etag(buf, {weak: false});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -43,16 +39,11 @@ exports.etag = function etag(body, encoding){
|
||||
*/
|
||||
|
||||
exports.wetag = function wetag(body, encoding){
|
||||
if (body.length === 0) {
|
||||
// fast-path empty body
|
||||
return 'W/"0-0"'
|
||||
}
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
: body;
|
||||
|
||||
var buf = Buffer.isBuffer(body)
|
||||
? body
|
||||
: new Buffer(body, encoding)
|
||||
var len = buf.length
|
||||
return 'W/"' + len.toString(16) + '-' + crc32.unsigned(buf) + '"'
|
||||
return etag(buf, {weak: true});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -131,18 +122,8 @@ exports.normalizeTypes = function(types){
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.contentDisposition = function(filename){
|
||||
var ret = 'attachment';
|
||||
if (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="' + filename + '"';
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
exports.contentDisposition = deprecate.function(contentDisposition,
|
||||
'utils.contentDisposition: use content-disposition npm module instead');
|
||||
|
||||
/**
|
||||
* Parse accept params `str` returning an
|
||||
|
||||
89
lib/view.js
89
lib/view.js
@@ -2,14 +2,21 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('express:view');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var dirname = path.dirname;
|
||||
var basename = path.basename;
|
||||
var extname = path.extname;
|
||||
var exists = fs.existsSync || path.existsSync;
|
||||
var join = path.join;
|
||||
var resolve = path.resolve;
|
||||
|
||||
/**
|
||||
* Expose `View`.
|
||||
@@ -45,23 +52,32 @@ function View(name, options) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup view by the given `path`
|
||||
* Lookup view by the given `name`
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String} name
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
View.prototype.lookup = function(path){
|
||||
var ext = this.ext;
|
||||
View.prototype.lookup = function lookup(name) {
|
||||
var path;
|
||||
var roots = [].concat(this.root);
|
||||
|
||||
// <path>.<engine>
|
||||
if (!utils.isAbsolute(path)) path = join(this.root, path);
|
||||
if (exists(path)) return path;
|
||||
debug('lookup "%s"', name);
|
||||
|
||||
// <path>/index.<engine>
|
||||
path = join(dirname(path), basename(path, ext), 'index' + ext);
|
||||
if (exists(path)) return path;
|
||||
for (var i = 0; i < roots.length && !path; i++) {
|
||||
var root = roots[i];
|
||||
|
||||
// resolve the path
|
||||
var loc = resolve(root, name);
|
||||
var dir = dirname(loc);
|
||||
var file = basename(loc);
|
||||
|
||||
// resolve the file
|
||||
path = this.resolve(dir, file);
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -72,6 +88,55 @@ View.prototype.lookup = function(path){
|
||||
* @api private
|
||||
*/
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
View.prototype.render = function render(options, fn) {
|
||||
debug('render "%s"', this.path);
|
||||
this.engine(this.path, options, fn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve the file within the given directory.
|
||||
*
|
||||
* @param {string} dir
|
||||
* @param {string} file
|
||||
* @private
|
||||
*/
|
||||
|
||||
View.prototype.resolve = function resolve(dir, file) {
|
||||
var ext = this.ext;
|
||||
var path;
|
||||
var stat;
|
||||
|
||||
// <path>.<ext>
|
||||
path = join(dir, file);
|
||||
stat = tryStat(path);
|
||||
|
||||
if (stat && stat.isFile()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// <path>/index.<ext>
|
||||
path = join(dir, basename(file, ext), 'index' + ext);
|
||||
stat = tryStat(path);
|
||||
|
||||
if (stat && stat.isFile()) {
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a stat, maybe.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {fs.Stats}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function tryStat(path) {
|
||||
debug('stat "%s"', path);
|
||||
|
||||
try {
|
||||
return fs.statSync(path);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
76
package.json
76
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Fast, unopinionated, minimalist web framework",
|
||||
"version": "4.7.4",
|
||||
"version": "4.10.6",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
|
||||
@@ -12,6 +12,9 @@
|
||||
"Roman Shtylman <shtylman+expressjs@gmail.com>",
|
||||
"Young Jae Sim <hanul@hanul.me>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": "strongloop/express",
|
||||
"homepage": "http://expressjs.com/",
|
||||
"keywords": [
|
||||
"express",
|
||||
"framework",
|
||||
@@ -23,58 +26,65 @@
|
||||
"app",
|
||||
"api"
|
||||
],
|
||||
"repository": "visionmedia/express",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.0.7",
|
||||
"buffer-crc32": "0.2.3",
|
||||
"debug": "1.0.4",
|
||||
"depd": "0.4.4",
|
||||
"accepts": "~1.1.4",
|
||||
"content-disposition": "0.5.0",
|
||||
"cookie-signature": "1.0.5",
|
||||
"debug": "~2.1.0",
|
||||
"depd": "~1.0.0",
|
||||
"escape-html": "1.0.1",
|
||||
"finalhandler": "0.1.0",
|
||||
"media-typer": "0.2.0",
|
||||
"etag": "~1.5.1",
|
||||
"finalhandler": "0.3.2",
|
||||
"fresh": "0.2.4",
|
||||
"media-typer": "0.3.0",
|
||||
"methods": "1.1.0",
|
||||
"parseurl": "~1.2.0",
|
||||
"on-finished": "~2.1.1",
|
||||
"parseurl": "~1.3.0",
|
||||
"path-to-regexp": "0.1.3",
|
||||
"proxy-addr": "1.0.1",
|
||||
"range-parser": "1.0.0",
|
||||
"send": "0.7.4",
|
||||
"serve-static": "~1.4.4",
|
||||
"type-is": "~1.3.2",
|
||||
"vary": "0.1.0",
|
||||
"proxy-addr": "~1.0.4",
|
||||
"qs": "2.3.3",
|
||||
"range-parser": "~1.0.2",
|
||||
"send": "0.10.1",
|
||||
"serve-static": "~1.7.1",
|
||||
"type-is": "~1.5.4",
|
||||
"vary": "~1.0.0",
|
||||
"cookie": "0.1.2",
|
||||
"fresh": "0.2.2",
|
||||
"cookie-signature": "1.0.4",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"qs": "0.6.6",
|
||||
"utils-merge": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.1",
|
||||
"istanbul": "0.3.0",
|
||||
"mocha": "~1.21.0",
|
||||
"should": "~4.0.4",
|
||||
"supertest": "~0.13.0",
|
||||
"connect-redis": "~2.0.0",
|
||||
"istanbul": "0.3.5",
|
||||
"mocha": "~2.0.0",
|
||||
"should": "~4.3.1",
|
||||
"supertest": "~0.15.0",
|
||||
"ejs": "~1.0.0",
|
||||
"marked": "0.3.2",
|
||||
"hjs": "~0.0.6",
|
||||
"body-parser": "~1.5.2",
|
||||
"cookie-parser": "~1.3.1",
|
||||
"express-session": "~1.7.2",
|
||||
"jade": "~1.5.0",
|
||||
"method-override": "~2.1.1",
|
||||
"morgan": "~1.2.2",
|
||||
"multiparty": "~3.3.1",
|
||||
"vhost": "2.0.0"
|
||||
"body-parser": "~1.9.3",
|
||||
"connect-redis": "~2.1.0",
|
||||
"cookie-parser": "~1.3.3",
|
||||
"express-session": "~1.9.2",
|
||||
"jade": "~1.7.0",
|
||||
"method-override": "~2.3.0",
|
||||
"morgan": "~1.5.0",
|
||||
"multiparty": "~4.0.0",
|
||||
"vhost": "~3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"History.md",
|
||||
"Readme.md",
|
||||
"index.js",
|
||||
"lib/"
|
||||
],
|
||||
"scripts": {
|
||||
"prepublish": "npm prune",
|
||||
"test": "mocha --require test/support/env --reporter spec --bail --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-tap": "mocha --require test/support/env --reporter tap --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,65 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../');
|
||||
|
||||
var app = express()
|
||||
, blog = express()
|
||||
, admin = express();
|
||||
|
||||
blog.use('/admin', admin);
|
||||
app.use('/blog', blog);
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
app.locals.self = true;
|
||||
|
||||
app.get('/render', function(req, res){
|
||||
res.render('hello');
|
||||
});
|
||||
|
||||
admin.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
blog.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
app.get('/json', function(req, res){
|
||||
res.send({ name: 'Tobi', role: 'admin' });
|
||||
});
|
||||
|
||||
app.get('/json/:n', function(req, res){
|
||||
var n = ~~req.params.n;
|
||||
var arr = [];
|
||||
var obj = { name: 'Tobi', role: 'admin' };
|
||||
while (n--) arr.push(obj);
|
||||
res.send(arr);
|
||||
});
|
||||
|
||||
function foo(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
app.get('/middleware', foo, foo, foo, foo, function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
var n = 100;
|
||||
while (n--) {
|
||||
app.get('/foo', foo, foo, function(req, res){
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/match', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
app.listen(8000);
|
||||
@@ -1 +0,0 @@
|
||||
p Hello
|
||||
@@ -43,6 +43,16 @@ describe('Router', function(){
|
||||
router.handle({ url: '/test/route', method: 'GET' }, { end: done });
|
||||
});
|
||||
|
||||
it('should handle blank URL', function(done){
|
||||
var router = new Router();
|
||||
|
||||
router.use(function (req, res) {
|
||||
false.should.be.true;
|
||||
});
|
||||
|
||||
router.handle({ url: '', method: 'GET' }, {}, done);
|
||||
});
|
||||
|
||||
describe('.handle', function(){
|
||||
it('should dispatch', function(done){
|
||||
var router = new Router();
|
||||
@@ -209,6 +219,23 @@ describe('Router', function(){
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore FQDN in path', function (done) {
|
||||
var request = { hit: 0, url: '/proxy/http://example.com/blog/post/1', method: 'GET' };
|
||||
var router = new Router();
|
||||
|
||||
router.use('/proxy', function (req, res, next) {
|
||||
assert.equal(req.hit++, 0);
|
||||
assert.equal(req.url, '/http://example.com/blog/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle(request, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(request.hit, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should adjust FQDN req.url', function (done) {
|
||||
var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };
|
||||
var router = new Router();
|
||||
@@ -297,6 +324,43 @@ describe('Router', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('.use', function() {
|
||||
it('should require arguments', function(){
|
||||
var router = new Router();
|
||||
router.use.bind(router).should.throw(/requires middleware function/)
|
||||
})
|
||||
|
||||
it('should not accept non-functions', function(){
|
||||
var router = new Router();
|
||||
router.use.bind(router, '/', 'hello').should.throw(/requires middleware function.*string/)
|
||||
router.use.bind(router, '/', 5).should.throw(/requires middleware function.*number/)
|
||||
router.use.bind(router, '/', null).should.throw(/requires middleware function.*Null/)
|
||||
router.use.bind(router, '/', new Date()).should.throw(/requires middleware function.*Date/)
|
||||
})
|
||||
|
||||
it('should accept array of middleware', function(done){
|
||||
var count = 0;
|
||||
var router = new Router();
|
||||
|
||||
function fn1(req, res, next){
|
||||
assert.equal(++count, 1);
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next){
|
||||
assert.equal(++count, 2);
|
||||
next();
|
||||
}
|
||||
|
||||
router.use([fn1, fn2], function(req, res){
|
||||
assert.equal(++count, 3);
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/foo', method: 'GET' }, {}, function(){});
|
||||
})
|
||||
})
|
||||
|
||||
describe('.param', function() {
|
||||
it('should call param function when routing VERBS', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var app = require('../../examples/auth/app')
|
||||
var app = require('../../examples/auth')
|
||||
var request = require('supertest')
|
||||
|
||||
function getCookie(res) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
var app = require('../../examples/cookies/app')
|
||||
var app = require('../../examples/cookies')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('cookies', function(){
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
var app = require('../../examples/downloads/app')
|
||||
var app = require('../../examples/downloads')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('downloads', function(){
|
||||
|
||||
44
test/acceptance/multi-router.js
Normal file
44
test/acceptance/multi-router.js
Normal file
@@ -0,0 +1,44 @@
|
||||
var app = require('../../examples/multi-router')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('multi-router', function(){
|
||||
describe('GET /',function(){
|
||||
it('should respond with root handler', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'Hello form root route.', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/v1/',function(){
|
||||
it('should respond with APIv1 root handler', function(done){
|
||||
request(app)
|
||||
.get('/api/v1/')
|
||||
.expect(200, 'Hello from APIv1 root route.', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/v1/users',function(){
|
||||
it('should respond with users from APIv1', function(done){
|
||||
request(app)
|
||||
.get('/api/v1/users')
|
||||
.expect(200, 'List of APIv1 users.', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/v2/',function(){
|
||||
it('should respond with APIv2 root handler', function(done){
|
||||
request(app)
|
||||
.get('/api/v2/')
|
||||
.expect(200, 'Hello from APIv2 root route.', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/v2/users',function(){
|
||||
it('should respond with users from APIv2', function(done){
|
||||
request(app)
|
||||
.get('/api/v2/users')
|
||||
.expect(200, 'List of APIv2 users.', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
var app = require('../../examples/params/app')
|
||||
var app = require('../../examples/params')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('params', function(){
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var app = require('../../examples/resource/app')
|
||||
var app = require('../../examples/resource')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('resource', function(){
|
||||
|
||||
97
test/acceptance/route-separation.js
Normal file
97
test/acceptance/route-separation.js
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
var app = require('../../examples/route-separation')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('route-separation', function () {
|
||||
describe('GET /', function () {
|
||||
it('should respond with index', function (done) {
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, /Route Separation Example/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users', function () {
|
||||
it('should list users', function (done) {
|
||||
request(app)
|
||||
.get('/users')
|
||||
.expect(/TJ/)
|
||||
.expect(/Tobi/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/:id', function () {
|
||||
it('should get a user', function (done) {
|
||||
request(app)
|
||||
.get('/user/0')
|
||||
.expect(200, /Viewing user TJ/, done)
|
||||
})
|
||||
|
||||
it('should 404 on missing user', function (done) {
|
||||
request(app)
|
||||
.get('/user/10')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/:id/view', function () {
|
||||
it('should get a user', function (done) {
|
||||
request(app)
|
||||
.get('/user/0/view')
|
||||
.expect(200, /Viewing user TJ/, done)
|
||||
})
|
||||
|
||||
it('should 404 on missing user', function (done) {
|
||||
request(app)
|
||||
.get('/user/10/view')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/:id/edit', function () {
|
||||
it('should get a user to edit', function (done) {
|
||||
request(app)
|
||||
.get('/user/0/edit')
|
||||
.expect(200, /Editing user TJ/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /user/:id/edit', function () {
|
||||
it('should edit a user', function (done) {
|
||||
request(app)
|
||||
.put('/user/0/edit')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ user: { name: 'TJ', email: 'tj-invalid@vision-media.ca' } })
|
||||
.expect(302, function (err) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/user/0')
|
||||
.expect(200, /tj-invalid@vision-media\.ca/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /user/:id/edit?_method=PUT', function () {
|
||||
it('should edit a user', function (done) {
|
||||
request(app)
|
||||
.post('/user/1/edit?_method=PUT')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ user: { name: 'Tobi', email: 'tobi-invalid@vision-media.ca' } })
|
||||
.expect(302, function (err) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/user/1')
|
||||
.expect(200, /tobi-invalid@vision-media\.ca/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /posts', function () {
|
||||
it('should get a list of posts', function (done) {
|
||||
request(app)
|
||||
.get('/posts')
|
||||
.expect(200, /Posts/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -53,7 +53,7 @@ describe('web-service', function(){
|
||||
.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(/"url":"http:\/\/github.com\/strongloop\/express"/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
|
||||
var express = require('../');
|
||||
var express = require('..');
|
||||
var tmpl = require('./support/tmpl');
|
||||
|
||||
describe('app', function(){
|
||||
describe('.render(name, fn)', function(){
|
||||
it('should support absolute paths', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render(__dirname + '/fixtures/user.jade', function(err, str){
|
||||
app.render(__dirname + '/fixtures/user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
@@ -16,9 +17,9 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should support absolute paths with "view engine"', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render(__dirname + '/fixtures/user', function(err, str){
|
||||
@@ -29,12 +30,12 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should expose app.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render('user.jade', function(err, str){
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
@@ -42,12 +43,12 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should support index.<engine>', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
|
||||
app.render('blog/post', function(err, str){
|
||||
app.render('blog/post', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<h1>blog post</h1>');
|
||||
done();
|
||||
@@ -77,10 +78,11 @@ describe('app', function(){
|
||||
|
||||
describe('when the file does not exist', function(){
|
||||
it('should provide a helpful error', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.render('rawr.jade', function(err){
|
||||
err.message.should.equal('Failed to lookup view "rawr.jade" in views directory "' + __dirname + '/fixtures"');
|
||||
app.render('rawr.tmpl', function (err) {
|
||||
err.message.should.equal('Failed to lookup view "rawr.tmpl" in views directory "' + __dirname + '/fixtures"');
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -88,11 +90,11 @@ describe('app', function(){
|
||||
|
||||
describe('when an error occurs', function(){
|
||||
it('should invoke the callback', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.render('user.jade', function(err, str){
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
// nextTick to prevent cyclic
|
||||
process.nextTick(function(){
|
||||
err.message.should.match(/Cannot read property '[^']+' of undefined/);
|
||||
@@ -104,11 +106,11 @@ describe('app', function(){
|
||||
|
||||
describe('when an extension is given', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.render('email.jade', function(err, str){
|
||||
app.render('email.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>This is an email</p>');
|
||||
done();
|
||||
@@ -118,9 +120,9 @@ describe('app', function(){
|
||||
|
||||
describe('when "view engine" is given', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.render('email', function(err, str){
|
||||
@@ -131,6 +133,64 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "views" is given', function(){
|
||||
it('should lookup the file in the path', function(done){
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures/default_layout');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('when array of paths', function(){
|
||||
it('should lookup the file in the path', function(done){
|
||||
var app = createApp();
|
||||
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
|
||||
|
||||
app.set('views', views);
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<span>tobi</span>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should lookup in later paths until found', function(done){
|
||||
var app = createApp();
|
||||
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
|
||||
|
||||
app.set('views', views);
|
||||
app.locals.name = 'tobi';
|
||||
|
||||
app.render('name.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should error if file does not exist', function(done){
|
||||
var app = createApp();
|
||||
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
|
||||
|
||||
app.set('views', views);
|
||||
app.locals.name = 'tobi';
|
||||
|
||||
app.render('pet.tmpl', function (err, str) {
|
||||
err.message.should.equal('Failed to lookup view "pet.tmpl" in views directories "' + __dirname + '/fixtures/local_layout" or "' + __dirname + '/fixtures/default_layout"');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when a "view" constructor is given', function(){
|
||||
it('should create an instance of it', function(done){
|
||||
var app = express();
|
||||
@@ -219,13 +279,13 @@ describe('app', function(){
|
||||
|
||||
describe('.render(name, options, fn)', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
var user = { name: 'tobi' };
|
||||
|
||||
app.render('user.jade', { user: user }, function(err, str){
|
||||
app.render('user.tmpl', { user: user }, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
@@ -233,12 +293,12 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should expose app.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render('user.jade', {}, function(err, str){
|
||||
app.render('user.tmpl', {}, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
@@ -246,13 +306,13 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should give precedence to app.render() locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
var jane = { name: 'jane' };
|
||||
|
||||
app.render('user.jade', { user: jane }, function(err, str){
|
||||
app.render('user.tmpl', { user: jane }, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>jane</p>');
|
||||
done();
|
||||
@@ -292,3 +352,11 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function createApp() {
|
||||
var app = express();
|
||||
|
||||
app.engine('.tmpl', tmpl);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -630,7 +630,7 @@ describe('app.router', function(){
|
||||
.expect('', done);
|
||||
})
|
||||
|
||||
it('should require a preceeding /', function(done){
|
||||
it('should require a preceding /', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/file/*', function(req, res){
|
||||
@@ -679,6 +679,23 @@ describe('app.router', function(){
|
||||
.get('/user/tj/edit')
|
||||
.expect('editing tj', done);
|
||||
})
|
||||
|
||||
it('should work in array of paths', function(done){
|
||||
var app = express();
|
||||
var cb = after(2, done);
|
||||
|
||||
app.get(['/user/:user/poke', '/user/:user/pokes'], function(req, res){
|
||||
res.end('poking ' + req.params.user);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/tj/poke')
|
||||
.expect('poking tj', cb);
|
||||
|
||||
request(app)
|
||||
.get('/user/tj/pokes')
|
||||
.expect('poking tj', cb);
|
||||
})
|
||||
})
|
||||
|
||||
describe(':name?', function(){
|
||||
|
||||
272
test/app.use.js
272
test/app.use.js
@@ -16,11 +16,6 @@ describe('app', function(){
|
||||
app.use(blog);
|
||||
})
|
||||
|
||||
it('should reject missing functions', function(){
|
||||
var app = express();
|
||||
app.use.bind(app, 3).should.throw(/requires callback function/);
|
||||
})
|
||||
|
||||
describe('.use(app)', function(){
|
||||
it('should mount the app', function(done){
|
||||
var blog = express()
|
||||
@@ -84,6 +79,44 @@ describe('app', function(){
|
||||
.get('/post/once-upon-a-time')
|
||||
.expect('success', done);
|
||||
})
|
||||
|
||||
it('should support mounted app anywhere', function(done){
|
||||
var cb = after(3, done);
|
||||
var blog = express()
|
||||
, other = express()
|
||||
, app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
blog.get('/', function(req, res){
|
||||
res.end('success');
|
||||
});
|
||||
|
||||
blog.once('mount', function (parent) {
|
||||
parent.should.equal(app);
|
||||
cb();
|
||||
});
|
||||
other.once('mount', function (parent) {
|
||||
parent.should.equal(app);
|
||||
cb();
|
||||
});
|
||||
|
||||
app.use('/post/:article', fn1, other, fn2, blog);
|
||||
|
||||
request(app)
|
||||
.get('/post/once-upon-a-time')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('success', cb);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.use(middleware)', function(){
|
||||
@@ -133,9 +166,106 @@ describe('app', function(){
|
||||
.post('/foo')
|
||||
.expect(200, 'saw POST /foo', cb);
|
||||
})
|
||||
|
||||
it('should accept array of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use([fn1, fn2, fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should accept multiple arrays of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use([fn1, fn2], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should accept nested arrays of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use([[fn1], fn2], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.use(path, middleware)', function(){
|
||||
it('should reject missing functions', function () {
|
||||
var app = express();
|
||||
app.use.bind(app, '/').should.throw(/requires middleware function/);
|
||||
})
|
||||
|
||||
it('should reject non-functions as middleware', function () {
|
||||
var app = express();
|
||||
app.use.bind(app, '/', 'hi').should.throw(/requires middleware function.*string/);
|
||||
app.use.bind(app, '/', 5).should.throw(/requires middleware function.*number/);
|
||||
app.use.bind(app, '/', null).should.throw(/requires middleware function.*Null/);
|
||||
app.use.bind(app, '/', new Date()).should.throw(/requires middleware function.*Date/);
|
||||
})
|
||||
|
||||
it('should strip path from req.url', function (done) {
|
||||
var app = express();
|
||||
|
||||
@@ -216,6 +346,90 @@ describe('app', function(){
|
||||
.expect(200, 'saw POST /bar', cb);
|
||||
})
|
||||
|
||||
it('should accept array of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use('/foo', [fn1, fn2, fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should accept multiple arrays of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use('/foo', [fn1, fn2], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should accept nested arrays of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use('/foo', [fn1, [fn2]], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should support array of paths', function (done) {
|
||||
var app = express();
|
||||
var cb = after(3, done);
|
||||
@@ -237,6 +451,42 @@ describe('app', function(){
|
||||
.expect(200, 'saw GET / through /bar', cb);
|
||||
})
|
||||
|
||||
it('should support array of paths with middleware array', function (done) {
|
||||
var app = express();
|
||||
var cb = after(2, done);
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);
|
||||
}
|
||||
|
||||
app.use(['/foo/', '/bar'], [[fn1], fn2], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, 'saw GET / through /foo', cb);
|
||||
|
||||
request(app)
|
||||
.get('/bar')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, 'saw GET / through /bar', cb);
|
||||
})
|
||||
|
||||
it('should support regexp path', function (done) {
|
||||
var app = express();
|
||||
var cb = after(4, done);
|
||||
@@ -261,5 +511,17 @@ describe('app', function(){
|
||||
.get('/get/zoo')
|
||||
.expect(404, cb);
|
||||
})
|
||||
|
||||
it('should support empty string path', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use('', function (req, res) {
|
||||
res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'saw GET / through /', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
1
test/fixtures/% of dogs.txt
vendored
Normal file
1
test/fixtures/% of dogs.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
20%
|
||||
1
test/fixtures/blog/post/index.jade
vendored
1
test/fixtures/blog/post/index.jade
vendored
@@ -1 +0,0 @@
|
||||
h1 blog post
|
||||
1
test/fixtures/blog/post/index.tmpl
vendored
Normal file
1
test/fixtures/blog/post/index.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<h1>blog post</h1>
|
||||
1
test/fixtures/default_layout/name.tmpl
vendored
Normal file
1
test/fixtures/default_layout/name.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>$name</p>
|
||||
1
test/fixtures/default_layout/user.tmpl
vendored
Normal file
1
test/fixtures/default_layout/user.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>$user.name</p>
|
||||
1
test/fixtures/email.jade
vendored
1
test/fixtures/email.jade
vendored
@@ -1 +0,0 @@
|
||||
p This is an email
|
||||
1
test/fixtures/email.tmpl
vendored
Normal file
1
test/fixtures/email.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>This is an email</p>
|
||||
1
test/fixtures/local_layout/user.tmpl
vendored
Normal file
1
test/fixtures/local_layout/user.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<span>$user.name</span>
|
||||
1
test/fixtures/name.jade
vendored
1
test/fixtures/name.jade
vendored
@@ -1 +0,0 @@
|
||||
p= name
|
||||
1
test/fixtures/name.tmpl
vendored
Normal file
1
test/fixtures/name.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>$name</p>
|
||||
1
test/fixtures/pet.jade
vendored
1
test/fixtures/pet.jade
vendored
@@ -1 +0,0 @@
|
||||
p #{first} #{last} is a #{species}
|
||||
1
test/fixtures/user.jade
vendored
1
test/fixtures/user.jade
vendored
@@ -1 +0,0 @@
|
||||
p= user.name
|
||||
1
test/fixtures/user.tmpl
vendored
Normal file
1
test/fixtures/user.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>$user.name</p>
|
||||
@@ -32,5 +32,18 @@ describe('req', function(){
|
||||
.set('If-None-Match', '"12345"')
|
||||
.expect(200, 'false', done);
|
||||
})
|
||||
|
||||
it('should return false without response headers', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res._headers = undefined;
|
||||
res.send(req.fresh);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'false', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -45,10 +45,9 @@ describe('req', function(){
|
||||
res.send(req.ip);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('127.0.0.1', done);
|
||||
var test = request(app).get('/')
|
||||
test.set('X-Forwarded-For', 'client, p1, p2')
|
||||
test.expect(200, getExpectedClientAddress(test._server), done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -63,10 +62,19 @@ describe('req', function(){
|
||||
res.send(req.ip);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('127.0.0.1', done);
|
||||
var test = request(app).get('/')
|
||||
test.expect(200, getExpectedClientAddress(test._server), done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Get the local client address depending on AF_NET of server
|
||||
*/
|
||||
|
||||
function getExpectedClientAddress(server) {
|
||||
return server.address().address === '::'
|
||||
? '::ffff:127.0.0.1'
|
||||
: '127.0.0.1';
|
||||
}
|
||||
|
||||
@@ -32,5 +32,18 @@ describe('req', function(){
|
||||
.set('If-None-Match', '"12345"')
|
||||
.expect(200, 'true', done);
|
||||
})
|
||||
|
||||
it('should return true without response headers', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res._headers = undefined;
|
||||
res.send(req.stale);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'true', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -15,7 +15,33 @@ describe('req', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'tobi.ferrets.example.com')
|
||||
.expect(["ferrets","tobi"], done);
|
||||
.expect(200, ['ferrets', 'tobi'], done);
|
||||
})
|
||||
|
||||
it('should work with IPv4 address', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.send(req.subdomains);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', '127.0.0.1')
|
||||
.expect(200, [], done);
|
||||
})
|
||||
|
||||
it('should work with IPv6 address', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.send(req.subdomains);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', '[::1]')
|
||||
.expect(200, [], done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -30,7 +56,7 @@ describe('req', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'example.com')
|
||||
.expect([], done);
|
||||
.expect(200, [], done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -45,7 +71,23 @@ describe('req', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect([], done);
|
||||
.expect(200, [], done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with trusted X-Forwarded-Host', function () {
|
||||
it('should return an array', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', true);
|
||||
app.use(function (req, res) {
|
||||
res.send(req.subdomains);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-Host', 'tobi.ferrets.example.com')
|
||||
.expect(200, ['ferrets', 'tobi'], done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -62,7 +104,35 @@ describe('req', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'tobi.ferrets.sub.example.com')
|
||||
.expect(["com","example","sub","ferrets","tobi"], done);
|
||||
.expect(200, ['com', 'example', 'sub', 'ferrets', 'tobi'], done);
|
||||
})
|
||||
|
||||
it('should return an array with the whole IPv4', function (done) {
|
||||
var app = express();
|
||||
app.set('subdomain offset', 0);
|
||||
|
||||
app.use(function(req, res){
|
||||
res.send(req.subdomains);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', '127.0.0.1')
|
||||
.expect(200, ['127.0.0.1'], done);
|
||||
})
|
||||
|
||||
it('should return an array with the whole IPv6', function (done) {
|
||||
var app = express();
|
||||
app.set('subdomain offset', 0);
|
||||
|
||||
app.use(function(req, res){
|
||||
res.send(req.subdomains);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', '[::1]')
|
||||
.expect(200, ['[::1]'], done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -78,7 +148,7 @@ describe('req', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'tobi.ferrets.sub.example.com')
|
||||
.expect(["ferrets","tobi"], done);
|
||||
.expect(200, ['ferrets', 'tobi'], done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -94,7 +164,7 @@ describe('req', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'sub.example.com')
|
||||
.expect([], done);
|
||||
.expect(200, [], done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -56,10 +56,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Disposition', 'attachment;' +
|
||||
' 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);
|
||||
.expect('Content-Disposition', 'attachment; filename="???.txt"; filename*=UTF-8\'\'%E6%97%A5%E6%9C%AC%E8%AA%9E.txt')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should set the Content-Type', function(done){
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest')
|
||||
, mixin = require('utils-merge')
|
||||
, cookie = require('cookie')
|
||||
, cookieParser = require('cookie-parser')
|
||||
var merge = require('utils-merge');
|
||||
|
||||
describe('res', function(){
|
||||
describe('.cookie(name, object)', function(){
|
||||
@@ -113,7 +113,7 @@ describe('res', function(){
|
||||
var app = express();
|
||||
|
||||
var options = { maxAge: 1000 };
|
||||
var optionsCopy = mixin({}, options);
|
||||
var optionsCopy = merge({}, options);
|
||||
|
||||
app.use(function(req, res){
|
||||
res.cookie('name', 'tobi', options)
|
||||
|
||||
@@ -106,6 +106,21 @@ describe('res', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should include the redirect type', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.redirect(301, 'http://google.com');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'text/html')
|
||||
.expect('Content-Type', /html/)
|
||||
.expect('Location', 'http://google.com')
|
||||
.expect(301, '<p>Moved Permanently. Redirecting to <a href="http://google.com">http://google.com</a></p>', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when accepting text', function(){
|
||||
@@ -143,6 +158,21 @@ describe('res', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should include the redirect type', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.redirect(301, 'http://google.com');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'text/plain, */*')
|
||||
.expect('Content-Type', /plain/)
|
||||
.expect('Location', 'http://google.com')
|
||||
.expect(301, 'Moved Permanently. Redirecting to http://google.com', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when accepting neither text or html', function(){
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
var express = require('..');
|
||||
var request = require('supertest');
|
||||
var tmpl = require('./support/tmpl');
|
||||
|
||||
describe('res', function(){
|
||||
describe('.render(name)', function(){
|
||||
it('should support absolute paths', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render(__dirname + '/fixtures/user.jade');
|
||||
res.render(__dirname + '/fixtures/user.tmpl');
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -19,10 +20,10 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should support absolute paths with "view engine"', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.locals.user = { name: 'tobi' };
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render(__dirname + '/fixtures/user');
|
||||
@@ -34,13 +35,13 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should expose app.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('user.jade');
|
||||
res.render('user.tmpl');
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -49,13 +50,13 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should expose app.locals with `name` property', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.name = 'tobi';
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('name.jade');
|
||||
res.render('name.tmpl');
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -64,10 +65,10 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should support index.<engine>', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('blog/post');
|
||||
@@ -80,12 +81,12 @@ describe('res', function(){
|
||||
|
||||
describe('when an error occurs', function(){
|
||||
it('should next(err)', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('user.jade');
|
||||
res.render('user.tmpl');
|
||||
});
|
||||
|
||||
app.use(function(err, req, res, next){
|
||||
@@ -100,9 +101,9 @@ describe('res', function(){
|
||||
|
||||
describe('when "view engine" is given', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.use(function(req, res){
|
||||
@@ -114,18 +115,66 @@ describe('res', function(){
|
||||
.expect('<p>This is an email</p>', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "views" is given', function(){
|
||||
it('should lookup the file in the path', function(done){
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures/default_layout');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('user.tmpl', { user: { name: 'tobi' } });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('<p>tobi</p>', done);
|
||||
})
|
||||
|
||||
describe('when array of paths', function(){
|
||||
it('should lookup the file in the path', function(done){
|
||||
var app = createApp();
|
||||
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
|
||||
|
||||
app.set('views', views);
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('user.tmpl', { user: { name: 'tobi' } });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('<span>tobi</span>', done);
|
||||
})
|
||||
|
||||
it('should lookup in later paths until found', function(done){
|
||||
var app = createApp();
|
||||
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
|
||||
|
||||
app.set('views', views);
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('name.tmpl', { name: 'tobi' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('<p>tobi</p>', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.render(name, option)', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
var user = { name: 'tobi' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('user.jade', { user: user });
|
||||
res.render('user.tmpl', { user: user });
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -134,13 +183,13 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should expose app.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('user.jade');
|
||||
res.render('user.tmpl');
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -149,13 +198,13 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should expose res.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.locals.user = { name: 'tobi' };
|
||||
res.render('user.jade');
|
||||
res.render('user.tmpl');
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -164,14 +213,14 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should give precedence to res.locals over app.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.locals.user = { name: 'jane' };
|
||||
res.render('user.jade', {});
|
||||
res.render('user.tmpl', {});
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -180,14 +229,14 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should give precedence to res.render() locals over res.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
var jane = { name: 'jane' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.locals.user = { name: 'tobi' };
|
||||
res.render('user.jade', { user: jane });
|
||||
res.render('user.tmpl', { user: jane });
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -196,14 +245,14 @@ describe('res', function(){
|
||||
})
|
||||
|
||||
it('should give precedence to res.render() locals over app.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
var jane = { name: 'jane' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('user.jade', { user: jane });
|
||||
res.render('user.tmpl', { user: jane });
|
||||
});
|
||||
|
||||
request(app)
|
||||
@@ -214,13 +263,13 @@ describe('res', function(){
|
||||
|
||||
describe('.render(name, options, fn)', function(){
|
||||
it('should pass the resulting string', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.use(function(req, res){
|
||||
var tobi = { name: 'tobi' };
|
||||
res.render('user.jade', { user: tobi }, function(err, html){
|
||||
res.render('user.tmpl', { user: tobi }, function (err, html) {
|
||||
html = html.replace('tobi', 'loki');
|
||||
res.end(html);
|
||||
});
|
||||
@@ -234,13 +283,13 @@ describe('res', function(){
|
||||
|
||||
describe('.render(name, fn)', function(){
|
||||
it('should pass the resulting string', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.locals.user = { name: 'tobi' };
|
||||
res.render('user.jade', function(err, html){
|
||||
res.render('user.tmpl', function (err, html) {
|
||||
html = html.replace('tobi', 'loki');
|
||||
res.end(html);
|
||||
});
|
||||
@@ -253,12 +302,12 @@ describe('res', function(){
|
||||
|
||||
describe('when an error occurs', function(){
|
||||
it('should pass it to the callback', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('user.jade', function(err){
|
||||
res.render('user.tmpl', function (err) {
|
||||
res.end(err.message);
|
||||
});
|
||||
});
|
||||
@@ -270,3 +319,11 @@ describe('res', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function createApp() {
|
||||
var app = express();
|
||||
|
||||
app.engine('.tmpl', tmpl);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -118,13 +118,13 @@ describe('res', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
var str = Array(1024 * 2).join('-');
|
||||
var str = Array(1000).join('-');
|
||||
res.send(str);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('ETag', 'W/"7ff-2796319984"')
|
||||
.expect('ETag', 'W/"3e7-8084ccd1"')
|
||||
.end(done);
|
||||
})
|
||||
|
||||
@@ -132,7 +132,7 @@ describe('res', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
var str = Array(1024 * 2).join('-');
|
||||
var str = Array(1000).join('-');
|
||||
res.send(str);
|
||||
});
|
||||
|
||||
@@ -207,13 +207,13 @@ describe('res', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
var str = Array(1024 * 2).join('-');
|
||||
var str = Array(1000).join('-');
|
||||
res.send(new Buffer(str));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('ETag', 'W/"7ff-2796319984"')
|
||||
.expect('ETag', 'W/"3e7-8084ccd1"')
|
||||
.end(done);
|
||||
})
|
||||
|
||||
@@ -321,15 +321,17 @@ describe('res', function(){
|
||||
|
||||
it('should respond with 304 Not Modified when fresh', function(done){
|
||||
var app = express();
|
||||
var etag = '"asdf"';
|
||||
|
||||
app.use(function(req, res){
|
||||
var str = Array(1024 * 2).join('-');
|
||||
var str = Array(1000).join('-');
|
||||
res.set('ETag', etag);
|
||||
res.send(str);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('If-None-Match', 'W/"7ff-2796319984"')
|
||||
.set('If-None-Match', etag)
|
||||
.expect(304, done);
|
||||
})
|
||||
|
||||
@@ -375,7 +377,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('etag', 'W/"c-1525560792"', done)
|
||||
.expect('etag', 'W/"c-5aee35d8"', done)
|
||||
})
|
||||
|
||||
it('should send ETag for empty string response', function(done){
|
||||
@@ -396,7 +398,7 @@ describe('res', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
var str = Array(1024 * 2).join('-');
|
||||
var str = Array(1000).join('-');
|
||||
res.send(str);
|
||||
});
|
||||
|
||||
@@ -404,7 +406,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('etag', 'W/"7ff-2796319984"', done)
|
||||
.expect('etag', 'W/"3e7-8084ccd1"', done)
|
||||
});
|
||||
|
||||
it('should not override ETag when manually set', function(done){
|
||||
@@ -445,7 +447,7 @@ describe('res', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
var str = Array(1024 * 2).join('-');
|
||||
var str = Array(1000).join('-');
|
||||
res.send(str);
|
||||
});
|
||||
|
||||
@@ -503,7 +505,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('etag', 'W/"d-1486392595"', done)
|
||||
.expect('etag', 'W/"d-58988d13"', done)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
639
test/res.sendFile.js
Normal file
639
test/res.sendFile.js
Normal file
@@ -0,0 +1,639 @@
|
||||
|
||||
var after = require('after');
|
||||
var express = require('../')
|
||||
, request = require('supertest')
|
||||
, assert = require('assert');
|
||||
var onFinished = require('on-finished');
|
||||
var path = require('path');
|
||||
var should = require('should');
|
||||
var fixtures = path.join(__dirname, 'fixtures');
|
||||
|
||||
describe('res', function(){
|
||||
describe('.sendFile(path)', function () {
|
||||
it('should error missing path', function (done) {
|
||||
var app = createApp();
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(500, /path.*required/, done);
|
||||
});
|
||||
|
||||
it('should transfer a file', function (done) {
|
||||
var app = createApp(path.resolve(fixtures, 'name.txt'));
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', done);
|
||||
});
|
||||
|
||||
it('should transfer a file with special characters in string', function (done) {
|
||||
var app = createApp(path.resolve(fixtures, '% of dogs.txt'));
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, '20%', done);
|
||||
});
|
||||
|
||||
it('should 404 for directory', function (done) {
|
||||
var app = createApp(path.resolve(fixtures, 'blog'));
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should 404 when not found', function (done) {
|
||||
var app = createApp(path.resolve(fixtures, 'does-no-exist'));
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.statusCode = 200;
|
||||
res.send('no!');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should not override manual content-types', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.contentType('application/x-bogus');
|
||||
res.sendFile(path.resolve(fixtures, 'name.txt'));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/x-bogus')
|
||||
.end(done);
|
||||
})
|
||||
|
||||
it('should not error if the client aborts', function (done) {
|
||||
var cb = after(1, done);
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
setImmediate(function () {
|
||||
res.sendFile(path.resolve(fixtures, 'name.txt'));
|
||||
cb();
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
err.code.should.be.empty;
|
||||
cb();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
describe('with "dotfiles" option', function () {
|
||||
it('should not serve dotfiles by default', function (done) {
|
||||
var app = createApp(path.resolve(__dirname, 'fixtures/.name'));
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should accept dotfiles option', function(done){
|
||||
var app = createApp(path.resolve(__dirname, 'fixtures/.name'), { dotfiles: 'allow' });
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with "headers" option', function () {
|
||||
it('should accept headers option', function (done) {
|
||||
var headers = {
|
||||
'x-success': 'sent',
|
||||
'x-other': 'done'
|
||||
};
|
||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), { headers: headers });
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-success', 'sent')
|
||||
.expect('x-other', 'done')
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should ignore headers option on 404', function (done) {
|
||||
var headers = { 'x-success': 'sent' };
|
||||
var app = createApp(path.resolve(__dirname, 'fixtures/does-not-exist'), { headers: headers });
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, function (err, res) {
|
||||
if (err) return done(err);
|
||||
res.headers.should.not.have.property('x-success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with "root" option', function () {
|
||||
it('should not transfer relative with without', function (done) {
|
||||
var app = createApp('test/fixtures/name.txt');
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(500, /must be absolute/, done);
|
||||
})
|
||||
|
||||
it('should serve relative to "root"', function (done) {
|
||||
var app = createApp('name.txt', {root: fixtures});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', done);
|
||||
})
|
||||
|
||||
it('should disallow requesting out of "root"', function (done) {
|
||||
var app = createApp('foo/../../user.html', {root: fixtures});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(403, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.sendFile(path, fn)', function () {
|
||||
it('should invoke the callback when complete', function (done) {
|
||||
var cb = after(2, done);
|
||||
var app = createApp(path.resolve(fixtures, 'name.txt'), cb);
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, cb);
|
||||
})
|
||||
|
||||
it('should invoke the callback when client aborts', function (done) {
|
||||
var cb = after(1, done);
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
setImmediate(function () {
|
||||
res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) {
|
||||
should(err).be.ok;
|
||||
err.code.should.equal('ECONNABORT');
|
||||
cb();
|
||||
});
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
it('should invoke the callback when client already aborted', function (done) {
|
||||
var cb = after(1, done);
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
onFinished(res, function () {
|
||||
res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) {
|
||||
should(err).be.ok;
|
||||
err.code.should.equal('ECONNABORT');
|
||||
cb();
|
||||
});
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
it('should invoke the callback on 404', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) {
|
||||
should(err).be.ok;
|
||||
err.status.should.equal(404);
|
||||
res.send('got it');
|
||||
});
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'got it', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.sendfile(path, fn)', function(){
|
||||
it('should invoke the callback when complete', function(done){
|
||||
var app = express();
|
||||
var cb = after(2, done);
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html', cb)
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, cb);
|
||||
})
|
||||
|
||||
it('should utilize the same options as express.static()', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html', { maxAge: 60000 });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Cache-Control', 'public, max-age=60')
|
||||
.end(done);
|
||||
})
|
||||
|
||||
it('should invoke the callback when client aborts', function (done) {
|
||||
var cb = after(1, done);
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
setImmediate(function () {
|
||||
res.sendfile('test/fixtures/name.txt', function (err) {
|
||||
should(err).be.ok;
|
||||
err.code.should.equal('ECONNABORT');
|
||||
cb();
|
||||
});
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
it('should invoke the callback when client already aborted', function (done) {
|
||||
var cb = after(1, done);
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
onFinished(res, function () {
|
||||
res.sendfile('test/fixtures/name.txt', function (err) {
|
||||
should(err).be.ok;
|
||||
err.code.should.equal('ECONNABORT');
|
||||
cb();
|
||||
});
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
it('should invoke the callback on 404', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/nope.html', function(err){
|
||||
++calls;
|
||||
assert(!res.headersSent);
|
||||
res.send(err.message);
|
||||
});
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
assert(1 == calls, 'called too many times');
|
||||
res.text.should.startWith("ENOENT, stat");
|
||||
res.statusCode.should.equal(200);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should not override manual content-types', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.contentType('txt');
|
||||
res.sendfile('test/fixtures/user.html');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'text/plain; charset=utf-8')
|
||||
.end(done);
|
||||
})
|
||||
|
||||
it('should invoke the callback on 403', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/foo/../user.html', function(err){
|
||||
assert(!res.headersSent);
|
||||
++calls;
|
||||
res.send(err.message);
|
||||
});
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Forbidden')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should invoke the callback on socket error', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html', function(err){
|
||||
assert(!res.headersSent);
|
||||
req.socket.listeners('error').should.have.length(1); // node's original handler
|
||||
done();
|
||||
});
|
||||
|
||||
req.socket.emit('error', new Error('broken!'));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(){});
|
||||
})
|
||||
})
|
||||
|
||||
describe('.sendfile(path)', function(){
|
||||
it('should not serve dotfiles', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/.name');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, done);
|
||||
})
|
||||
|
||||
it('should accept dotfiles option', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/.name', { dotfiles: 'allow' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', done);
|
||||
})
|
||||
|
||||
it('should accept headers option', function(done){
|
||||
var app = express();
|
||||
var headers = {
|
||||
'x-success': 'sent',
|
||||
'x-other': 'done'
|
||||
};
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html', { headers: headers });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-success', 'sent')
|
||||
.expect('x-other', 'done')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should ignore headers option on 404', function(done){
|
||||
var app = express();
|
||||
var headers = { 'x-success': 'sent' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.nothing', { headers: headers });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, function (err, res) {
|
||||
if (err) return done(err);
|
||||
res.headers.should.not.have.property('x-success');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should transfer a file', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.sendfile('test/fixtures/name.txt');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', done);
|
||||
});
|
||||
|
||||
it('should transfer a directory index file', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.sendfile('test/fixtures/blog/');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, '<b>index</b>', done);
|
||||
});
|
||||
|
||||
it('should 404 for directory without trailing slash', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.sendfile('test/fixtures/blog');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should transfer a file with urlencoded name', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.sendfile('test/fixtures/%25%20of%20dogs.txt');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, '20%', done);
|
||||
});
|
||||
|
||||
it('should not error if the client aborts', function (done) {
|
||||
var cb = after(1, done);
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
setImmediate(function () {
|
||||
res.sendfile(path.resolve(fixtures, 'name.txt'));
|
||||
cb();
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
err.code.should.be.empty;
|
||||
cb();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
describe('with an absolute path', function(){
|
||||
it('should transfer the file', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile(__dirname + '/fixtures/user.html');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('<p>{{user.name}}</p>');
|
||||
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
|
||||
done();
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a relative path', function(){
|
||||
it('should transfer the file', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('<p>{{user.name}}</p>');
|
||||
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should serve relative to "root"', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('user.html', { root: 'test/fixtures/' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('<p>{{user.name}}</p>');
|
||||
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should consider ../ malicious when "root" is not set', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/foo/../user.html');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(403, done);
|
||||
})
|
||||
|
||||
it('should allow ../ when "root" is set', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('foo/../user.html', { root: 'test/fixtures' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should disallow requesting out of "root"', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('foo/../../user.html', { root: 'test/fixtures' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(403, done);
|
||||
})
|
||||
|
||||
it('should next(404) when not found', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('user.html');
|
||||
});
|
||||
|
||||
app.use(function(req, res){
|
||||
assert(0, 'this should not be called');
|
||||
});
|
||||
|
||||
app.use(function(err, req, res, next){
|
||||
++calls;
|
||||
next(err);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(404);
|
||||
calls.should.equal(1);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
describe('with non-GET', function(){
|
||||
it('should still serve', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile(__dirname + '/fixtures/name.txt');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('tobi', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function createApp(path, options, fn) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.sendFile(path, options, fn);
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
32
test/res.sendStatus.js
Normal file
32
test/res.sendStatus.js
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
var assert = require('assert')
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('res', function () {
|
||||
describe('.sendStatus(statusCode)', function () {
|
||||
it('should send the status code and message as body', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendStatus(201);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(201, 'Created', done);
|
||||
})
|
||||
|
||||
it('should work with unknown code', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendStatus(599);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(599, '599', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,321 +0,0 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest')
|
||||
, assert = require('assert');
|
||||
|
||||
describe('res', function(){
|
||||
describe('.sendfile(path, fn)', function(){
|
||||
it('should invoke the callback when complete', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html', done)
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.end(function(){});
|
||||
})
|
||||
|
||||
it('should utilize the same options as express.static()', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html', { maxAge: 60000 });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Cache-Control', 'public, max-age=60')
|
||||
.end(done);
|
||||
})
|
||||
|
||||
it('should invoke the callback on 404', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/nope.html', function(err){
|
||||
++calls;
|
||||
assert(!res.headersSent);
|
||||
res.send(err.message);
|
||||
});
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
assert(1 == calls, 'called too many times');
|
||||
res.text.should.startWith("ENOENT, stat");
|
||||
res.statusCode.should.equal(200);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should not override manual content-types', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.contentType('txt');
|
||||
res.sendfile('test/fixtures/user.html');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'text/plain; charset=utf-8')
|
||||
.end(done);
|
||||
})
|
||||
|
||||
it('should invoke the callback on 403', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/foo/../user.html', function(err){
|
||||
assert(!res.headersSent);
|
||||
++calls;
|
||||
res.send(err.message);
|
||||
});
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Forbidden')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should invoke the callback on socket error', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html', function(err){
|
||||
assert(!res.headersSent);
|
||||
req.socket.listeners('error').should.have.length(1); // node's original handler
|
||||
done();
|
||||
});
|
||||
|
||||
req.socket.emit('error', new Error('broken!'));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(){});
|
||||
})
|
||||
})
|
||||
|
||||
describe('.sendfile(path)', function(){
|
||||
it('should not serve dotfiles', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/.name');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, done);
|
||||
})
|
||||
|
||||
it('should accept dotfiles option', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/.name', { dotfiles: 'allow' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', done);
|
||||
})
|
||||
|
||||
it('should accept headers option', function(done){
|
||||
var app = express();
|
||||
var headers = {
|
||||
'x-success': 'sent',
|
||||
'x-other': 'done'
|
||||
};
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html', { headers: headers });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-success', 'sent')
|
||||
.expect('x-other', 'done')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should ignore headers option on 404', function(done){
|
||||
var app = express();
|
||||
var headers = { 'x-success': 'sent' };
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.nothing', { headers: headers });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, function (err, res) {
|
||||
if (err) return done(err);
|
||||
res.headers.should.not.have.property('x-success');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should transfer a file', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.sendfile('test/fixtures/name.txt');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'tobi', done);
|
||||
});
|
||||
|
||||
it('should transfer a directory index file', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.sendfile('test/fixtures/blog/');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, '<b>index</b>', done);
|
||||
});
|
||||
|
||||
describe('with an absolute path', function(){
|
||||
it('should transfer the file', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile(__dirname + '/fixtures/user.html');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('<p>{{user.name}}</p>');
|
||||
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
|
||||
done();
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a relative path', function(){
|
||||
it('should transfer the file', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/user.html');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('<p>{{user.name}}</p>');
|
||||
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should serve relative to "root"', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('user.html', { root: 'test/fixtures/' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('<p>{{user.name}}</p>');
|
||||
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should consider ../ malicious when "root" is not set', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('test/fixtures/foo/../user.html');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(403, done);
|
||||
})
|
||||
|
||||
it('should allow ../ when "root" is set', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('foo/../user.html', { root: 'test/fixtures' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should disallow requesting out of "root"', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('foo/../../user.html', { root: 'test/fixtures' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(403, done);
|
||||
})
|
||||
|
||||
it('should next(404) when not found', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile('user.html');
|
||||
});
|
||||
|
||||
app.use(function(req, res){
|
||||
assert(0, 'this should not be called');
|
||||
});
|
||||
|
||||
app.use(function(err, req, res, next){
|
||||
++calls;
|
||||
next(err);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(404);
|
||||
calls.should.equal(1);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
describe('with non-GET', function(){
|
||||
it('should still serve', function(done){
|
||||
var app = express()
|
||||
, calls = 0;
|
||||
|
||||
app.use(function(req, res){
|
||||
res.sendfile(__dirname + '/fixtures/name.txt');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('tobi', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
35
test/support/tmpl.js
Normal file
35
test/support/tmpl.js
Normal file
@@ -0,0 +1,35 @@
|
||||
var fs = require('fs');
|
||||
|
||||
var variableRegExp = /\$([0-9a-zA-Z\.]+)/g;
|
||||
|
||||
module.exports = function renderFile(fileName, options, callback) {
|
||||
function onReadFile(err, str) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
str = str.replace(variableRegExp, generateVariableLookup(options));
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
callback(err, str);
|
||||
}
|
||||
|
||||
fs.readFile(fileName, 'utf8', onReadFile);
|
||||
};
|
||||
|
||||
function generateVariableLookup(data) {
|
||||
return function variableLookup(str, path) {
|
||||
var parts = path.split('.');
|
||||
var value = data;
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
value = value[parts[i]];
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
}
|
||||
@@ -28,18 +28,18 @@ describe('utils.etag(body, encoding)', function(){
|
||||
describe('utils.wetag(body, encoding)', function(){
|
||||
it('should support strings', function(){
|
||||
utils.wetag('express!')
|
||||
.should.eql('W/"8-3098196679"')
|
||||
.should.eql('W/"8-b8aabac7"')
|
||||
})
|
||||
|
||||
it('should support utf8 strings', function(){
|
||||
utils.wetag('express❤', 'utf8')
|
||||
.should.eql('W/"a-1751845617"')
|
||||
.should.eql('W/"a-686b0af1"')
|
||||
})
|
||||
|
||||
it('should support buffer', function(){
|
||||
var buf = new Buffer('express!')
|
||||
utils.wetag(buf)
|
||||
.should.eql('W/"8-3098196679"');
|
||||
.should.eql('W/"8-b8aabac7"');
|
||||
})
|
||||
|
||||
it('should support empty string', function(){
|
||||
|
||||
Reference in New Issue
Block a user