Compare commits

...

220 Commits

Author SHA1 Message Date
Douglas Christopher Wilson
ac56cf4606 4.10.2 2014-11-09 19:09:24 -05:00
Josemar Magalhaes
6dea32cd18 examples: add multi router example
closes #2434
2014-11-09 19:01:13 -05:00
Douglas Christopher Wilson
5fab60bc6c Correctly invoke async router callback asynchronously 2014-11-09 18:50:00 -05:00
Douglas Christopher Wilson
881e1ba660 deps: type-is@~1.5.3 2014-11-09 18:42:33 -05:00
Douglas Christopher Wilson
0e488c19df deps: accepts@~1.1.3 2014-11-09 18:41:01 -05:00
Douglas Christopher Wilson
2262a18900 Merge tag '3.18.3' 2014-11-09 18:39:46 -05:00
Douglas Christopher Wilson
28c6952d1c 3.18.3 2014-11-09 18:35:34 -05:00
Douglas Christopher Wilson
01e3530a31 deps: should@~4.2.1 2014-11-09 18:33:17 -05:00
Douglas Christopher Wilson
77c83d0c57 deps: connect@2.27.3 2014-11-09 18:32:43 -05:00
Douglas Christopher Wilson
b4eaf89186 examples: remove invalid cors example
Not only is the example not even standards-compliant, but it
encourages bad security settings and practices.
2014-11-06 21:14:02 -05:00
Douglas Christopher Wilson
e4debea297 Remove unused source file 2014-11-01 14:09:12 -04:00
Jaime Agudo
1c96e18d20 docs: fix Gratipay links
fixes #2424
2014-11-01 00:09:54 -04:00
Douglas Christopher Wilson
8bb013ec95 4.10.1 2014-10-29 01:15:58 -04:00
Douglas Christopher Wilson
6c0031bfd8 deps: qs@2.3.2 2014-10-29 01:14:44 -04:00
Douglas Christopher Wilson
ab6c189504 Merge tag '3.18.2' 2014-10-29 01:14:04 -04:00
Douglas Christopher Wilson
a12ae729bd 3.18.2 2014-10-29 01:10:45 -04:00
Douglas Christopher Wilson
d53a0cd91e deps: connect@2.27.2 2014-10-29 01:08:36 -04:00
Aria Stewart
eabd4564aa Fix handling of URLs containing :// in the path
fixes #2421
2014-10-29 00:33:02 -04:00
Douglas Christopher Wilson
d40dc651f3 4.10.0 2014-10-23 22:27:45 -04:00
Douglas Christopher Wilson
68290ee87a Fix handling of invalid empty URLs
fixes #2399
2014-10-23 21:33:38 -04:00
Douglas Christopher Wilson
6614352563 Add support for app.set('views', array)
closes #2320
2014-10-23 17:28:53 -04:00
Douglas Christopher Wilson
0e5f2f84ea Use path.resolve in view lookup 2014-10-23 15:55:17 -04:00
Douglas Christopher Wilson
b1d0c19ca1 examples: make main app file names consistent
fixes #2408
2014-10-23 02:39:38 -04:00
lemmy
dfa7ee4732 Pass context to .forEach instead of closure
Has a slight performance improvement

closes #2347
2014-10-23 02:30:09 -04:00
Douglas Christopher Wilson
e9539fc780 docs: visionmedia is now tj on Github 2014-10-23 02:20:51 -04:00
Fishrock123
5f7a37ee51 docs: misc. tweaks
closes #2394
2014-10-23 02:18:24 -04:00
Douglas Christopher Wilson
ff3a368b2f deps: update example dependencies 2014-10-23 02:08:34 -04:00
Douglas Christopher Wilson
ccc45a74f8 Merge tag '3.18.1' 2014-10-23 02:06:20 -04:00
Douglas Christopher Wilson
cd9d2ec6a9 deps: on-finished@~2.1.1 2014-10-23 01:45:58 -04:00
Douglas Christopher Wilson
ce7bbae007 deps: serve-static@~1.7.1 2014-10-23 01:43:19 -04:00
Douglas Christopher Wilson
6a5dd52deb deps: qs@2.3.0 2014-10-23 01:38:48 -04:00
Douglas Christopher Wilson
6c2f7fb48d deps: finalhandler@0.3.2 2014-10-23 01:37:00 -04:00
Douglas Christopher Wilson
4dd970578a Fix res.send to mention res.sendStatus 2014-10-23 01:35:16 -04:00
Douglas Christopher Wilson
88dfd36eaa 3.18.1 2014-10-23 01:26:21 -04:00
Douglas Christopher Wilson
5759b3e9f5 deps: send@0.10.1 2014-10-23 01:25:07 -04:00
Douglas Christopher Wilson
c939a771c0 deps: connect@2.27.1 2014-10-23 01:23:54 -04:00
Douglas Christopher Wilson
af1043844f Fix internal utils.merge deprecation warnings 2014-10-22 15:18:10 -04:00
Douglas Christopher Wilson
dd763ec5b8 deps: should@~4.1.0 2014-10-22 15:10:22 -04:00
Douglas Christopher Wilson
9c2c21aaaf deps: mocha@~2.0.0 2014-10-22 15:08:49 -04:00
Douglas Christopher Wilson
366000184f 3.18.0 2014-10-18 00:57:48 -04:00
Douglas Christopher Wilson
4d1ee23f84 Use etag module to generate ETag headers 2014-10-18 00:53:17 -04:00
Douglas Christopher Wilson
6f31218ecc Use content-disposition module 2014-10-17 23:45:58 -04:00
Douglas Christopher Wilson
6cd4859035 deps: finalhandler@0.3.1 2014-10-17 22:37:52 -04:00
Douglas Christopher Wilson
4f1cd4f73c deps: etag@~1.5.0 2014-10-17 22:37:49 -04:00
Douglas Christopher Wilson
f15bba7309 4.9.8 2014-10-17 22:01:51 -04:00
Alex Upadhyay
6f0302fb78 Fix res.redirect body when redirect status specified
fixes #2402
fixes #2404
2014-10-17 22:00:59 -04:00
Douglas Christopher Wilson
0e5613363f Use content-disposition module 2014-10-17 21:08:05 -04:00
Fishrock123
7a7f18c20b build: misc. updates to packaging
closes #2398
2014-10-17 20:49:56 -04:00
Douglas Christopher Wilson
b766aad112 deps: jade@~1.7.0 2014-10-17 20:45:57 -04:00
Douglas Christopher Wilson
7488e27609 deps: send@0.10.0 2014-10-17 20:45:09 -04:00
Douglas Christopher Wilson
bc9d854763 deps: depd@~1.0.0 2014-10-17 20:44:07 -04:00
Douglas Christopher Wilson
2e20a85810 deps: debug@~2.1.0 2014-10-17 20:43:23 -04:00
Douglas Christopher Wilson
a706408208 deps: connect@2.27.0 2014-10-17 20:42:12 -04:00
Douglas Christopher Wilson
6f91416020 deps: accepts@~1.1.2 2014-10-16 01:29:00 -04:00
Douglas Christopher Wilson
2c5ed88c90 Merge tag '3.17.8' 2014-10-16 01:27:44 -04:00
Douglas Christopher Wilson
6d39d0f8a8 3.17.8 2014-10-16 00:29:56 -04:00
Douglas Christopher Wilson
159ea67713 deps: connect@2.26.6 2014-10-16 00:24:28 -04:00
Douglas Christopher Wilson
33959ed350 deps: mocha@~1.21.5 2014-10-16 00:19:47 -04:00
Bessie Chan
be478d348c Fix typo in res.redirect deprecation
closes #2395
2014-10-11 18:11:22 -04:00
Douglas Christopher Wilson
b0e4e641f9 4.9.7 2014-10-10 16:40:46 -04:00
Douglas Christopher Wilson
94f10c26cb Fix using same param name in array of paths
fixes #2389
2014-10-10 16:31:09 -04:00
Douglas Christopher Wilson
efd2dfb8c8 4.9.6 2014-10-08 22:33:13 -04:00
Douglas Christopher Wilson
3f2454e3df deps: type-is@~1.5.2 2014-10-08 21:51:10 -04:00
Douglas Christopher Wilson
61da3c7d0e deps: serve-static@~1.6.4 2014-10-08 21:50:08 -04:00
Douglas Christopher Wilson
2c140961ab deps: accepts@~1.1.1 2014-10-08 21:48:27 -04:00
Douglas Christopher Wilson
6aa4a450ed Merge tag '3.17.7' 2014-10-08 21:45:42 -04:00
Douglas Christopher Wilson
9f292d873e 3.17.7 2014-10-08 17:00:45 -04:00
Douglas Christopher Wilson
ef3e95ca73 deps: supertest@~0.14.0 2014-10-08 16:44:29 -04:00
Douglas Christopher Wilson
f45bd632df deps: connect@2.26.5 2014-10-08 16:43:02 -04:00
Douglas Christopher Wilson
cc18da5cdf 3.17.6 2014-10-02 23:29:40 -04:00
Douglas Christopher Wilson
5603f86edd deps: connect@2.26.4 2014-10-02 23:27:41 -04:00
Douglas Christopher Wilson
daadf6033b 4.9.5 2014-09-24 20:18:59 -04:00
Douglas Christopher Wilson
590c919204 deps: serve-static@~1.6.3 2014-09-24 20:16:00 -04:00
Douglas Christopher Wilson
3bcba79e2d deps: etag@~1.4.0 2014-09-24 20:14:39 -04:00
Douglas Christopher Wilson
b8c8ecebb7 Merge tag '3.17.5' 2014-09-24 20:11:19 -04:00
Douglas Christopher Wilson
43e2cd79cb 3.17.5 2014-09-24 19:38:35 -04:00
Douglas Christopher Wilson
653270bb43 deps: send@0.9.3 2014-09-24 19:15:46 -04:00
Douglas Christopher Wilson
734bdf5ca1 deps: proxy-addr@~1.0.3 2014-09-24 19:14:33 -04:00
Douglas Christopher Wilson
341c1919d9 deps: connect@2.26.3 2014-09-24 19:12:43 -04:00
Douglas Christopher Wilson
8e46af1b1d 4.9.4 2014-09-19 23:07:58 -07:00
Douglas Christopher Wilson
e4fc09423e deps: qs@2.2.4 2014-09-19 23:06:31 -07:00
Douglas Christopher Wilson
0300b61fdd Merge tag '3.17.4' 2014-09-19 23:05:45 -07:00
Douglas Christopher Wilson
b09afad7b1 3.17.4 2014-09-19 22:57:07 -07:00
Douglas Christopher Wilson
0e0b259556 deps: connect@2.26.2 2014-09-19 22:54:39 -07:00
Douglas Christopher Wilson
bc38d896ea 4.9.3 2014-09-18 10:45:35 -07:00
Douglas Christopher Wilson
b2518fe135 Merge tag '3.17.3' 2014-09-18 10:43:35 -07:00
Douglas Christopher Wilson
63286e1192 3.17.3 2014-09-18 10:38:31 -07:00
Douglas Christopher Wilson
c00f2f8596 deps: proxy-addr@~1.0.2 2014-09-18 10:36:21 -07:00
Douglas Christopher Wilson
91891e3aee 4.9.2 2014-09-17 20:49:01 -07:00
Douglas Christopher Wilson
728917164c Fix router.use to accept array of middleware without path 2014-09-17 19:18:13 -07:00
Douglas Christopher Wilson
bf1980f1b4 Improve error message for bad app.use arguments 2014-09-17 19:18:10 -07:00
Douglas Christopher Wilson
3c1a964362 Fix regression for empty string path in app.use
fixes #2361
fixes #2362
2014-09-17 19:18:01 -07:00
Douglas Christopher Wilson
947fb8b274 4.9.1 2014-09-16 23:31:57 -07:00
Douglas Christopher Wilson
c5193536e5 deps: update example dependencies 2014-09-16 23:24:23 -07:00
Douglas Christopher Wilson
d08fd64190 deps: serve-static@~1.6.2 2014-09-16 23:23:14 -07:00
Douglas Christopher Wilson
2470ae6c72 deps: etag@~1.3.1 2014-09-16 23:21:45 -07:00
Douglas Christopher Wilson
916a75cf19 Merge tag '3.17.2' 2014-09-16 23:08:03 -07:00
Douglas Christopher Wilson
daacb749c1 deps: remove un-used buffer-crc32 dependency 2014-09-16 23:03:04 -07:00
Douglas Christopher Wilson
f29399c4e1 3.17.2 2014-09-16 00:14:41 -07:00
Douglas Christopher Wilson
f6ac068ab0 Use crc instead of buffer-crc32 for speed 2014-09-16 00:11:08 -07:00
Douglas Christopher Wilson
7eb65eeca2 deps: send@0.9.2 2014-09-15 23:59:02 -07:00
Douglas Christopher Wilson
178fe15091 deps: depd@0.4.5 2014-09-15 23:57:41 -07:00
Douglas Christopher Wilson
381f278d0a deps: connect@2.26.1 2014-09-15 23:56:36 -07:00
Douglas Christopher Wilson
534fa181c6 Add test-tap npm script for TAP output
closes #2359
2014-09-15 21:41:35 -07:00
Julien Gilli
80847d8c82 tests: fix broken tests with node v0.12 branch
Currently, test/req.ip.js assumes that the connection between the client
and the server is an IPv4 connection. However, depending on the
configuration of the host where this test runs, the connection can be an
IPv4 one or an IPv6 one using an IPv4 mapped address. In the future, it
could also be a "full" IPv6 connection.

This change makes this test handle any type of address.

fixes #2342
2014-09-15 21:41:34 -07:00
Fei Yao
bb8abf1f90 Remove unused require in router match
closes #2358
2014-09-14 09:14:24 -07:00
Douglas Christopher Wilson
cf41a8f254 Fix app.use to accept array of middleware without path
fixes #2356
2014-09-12 17:46:49 -04:00
Douglas Christopher Wilson
1716e3b067 4.9.0 2014-09-09 00:32:17 -04:00
Seth Samuel
12f92a50dc Add res.sendStatus
closes #2269
closes #2297
closes #2340
2014-09-09 00:24:11 -04:00
Douglas Christopher Wilson
51d33edb79 Use etag to generate ETag headers 2014-09-09 00:13:49 -04:00
Douglas Christopher Wilson
2a0c35a108 Invoke callback for sendfile when client aborts
fixes #2189
fixes #2300
closes #2303
closes #2305
2014-09-09 00:12:49 -04:00
Douglas Christopher Wilson
d4de82b853 deps: accepts@~1.1.0 2014-09-09 00:12:10 -04:00
Douglas Christopher Wilson
e2102263ce deps: serve-static@~1.6.1 2014-09-09 00:11:04 -04:00
Douglas Christopher Wilson
d2b1a89b4a deps: type-is@~1.5.1 2014-09-09 00:10:02 -04:00
Douglas Christopher Wilson
d9937c628a deps: finalhandler@0.2.0 2014-09-09 00:08:07 -04:00
Douglas Christopher Wilson
276db8c49a deps: update example dependencies 2014-09-09 00:06:27 -04:00
Douglas Christopher Wilson
1768d94a1a deps: qs@2.2.3 2014-09-09 00:04:08 -04:00
Douglas Christopher Wilson
6bc7574ab5 docs: typo fixes
closes #2316
2014-09-08 23:53:21 -04:00
Douglas Christopher Wilson
3dca534995 Merge tag '3.17.1' 2014-09-08 23:48:59 -04:00
Douglas Christopher Wilson
4b1b8e420f 3.17.1 2014-09-08 23:45:38 -04:00
Douglas Christopher Wilson
70767b19ac Fix error in req.subdomains on empty host 2014-09-08 23:45:34 -04:00
Douglas Christopher Wilson
7d277c1c15 docs: remove erroneous from history 2014-09-08 23:44:24 -04:00
Douglas Christopher Wilson
fa1fcd9fec 3.17.0 2014-09-08 23:07:02 -04:00
Douglas Christopher Wilson
2de6514b4b Support IP address host in req.subdomains
fixes #2308
2014-09-08 23:04:06 -04:00
Douglas Christopher Wilson
d07c06363f Support X-Forwarded-Host in req.subdomains 2014-09-08 23:02:42 -04:00
Douglas Christopher Wilson
4e97533fd2 deps: jade@~1.6.0 2014-09-08 22:48:47 -04:00
Douglas Christopher Wilson
d7d6219a1e deps: range-parser@~1.0.2 2014-09-08 22:47:54 -04:00
Douglas Christopher Wilson
9b18461bbc deps: vary@~1.0.0 2014-09-08 21:18:28 -04:00
Douglas Christopher Wilson
b77aa38c98 deps: cookie-signature@1.0.5 2014-09-08 21:17:03 -04:00
Douglas Christopher Wilson
cbb251377e deps: fresh@0.2.4 2014-09-08 21:14:27 -04:00
Douglas Christopher Wilson
d6ed469de3 deps: send@0.9.1 2014-09-08 21:13:20 -04:00
Douglas Christopher Wilson
49284c236b deps: debug@~2.0.0 2014-09-08 21:11:41 -04:00
Douglas Christopher Wilson
be18487f7d deps: media-typer@0.3.0 2014-09-08 21:10:57 -04:00
Douglas Christopher Wilson
094ff11949 deps: connect@2.26.0 2014-09-08 21:09:22 -04:00
lemmy
d2d0afff64 Remove unused variable
closes #2345
2014-09-08 20:59:03 -04:00
Douglas Christopher Wilson
33bb8fc4b6 examples: fix up route-separation code
closes #2341
2014-09-08 20:51:32 -04:00
Douglas Christopher Wilson
b97f6eb506 examples: fix github view example
closes #2344
2014-09-08 19:31:58 -04:00
Douglas Christopher Wilson
621d074bd8 4.8.8 2014-09-05 02:26:12 -04:00
Douglas Christopher Wilson
d7e7b2e7d7 deps: update example dependencies 2014-09-05 02:24:02 -04:00
Douglas Christopher Wilson
2d6b735b4f deps: serve-static@~1.5.4 2014-09-05 02:21:27 -04:00
Douglas Christopher Wilson
a3115882d4 Merge tag '3.16.10' 2014-09-05 02:20:02 -04:00
Douglas Christopher Wilson
3d188fe13e 3.16.10 2014-09-05 02:17:55 -04:00
Douglas Christopher Wilson
8327708ec2 deps: istanbul@0.3.2 2014-09-05 01:57:02 -04:00
Douglas Christopher Wilson
c8640b3465 deps: send@0.8.5 2014-09-05 01:56:27 -04:00
Douglas Christopher Wilson
3ce5f9b493 deps: connect@2.25.10 2014-09-05 01:55:24 -04:00
Douglas Christopher Wilson
46f0bfc65f 4.8.7 2014-08-30 01:37:52 -04:00
Douglas Christopher Wilson
0b49e7f1fd deps: update example dependencies 2014-08-30 01:33:05 -04:00
Douglas Christopher Wilson
f6f47f428c deps: qs@2.2.2 2014-08-30 01:30:56 -04:00
Douglas Christopher Wilson
20635d03fc Merge tag '3.16.9' 2014-08-30 01:28:38 -04:00
Douglas Christopher Wilson
4d032cda05 3.16.9 2014-08-30 01:22:06 -04:00
Douglas Christopher Wilson
4127ba10b0 deps: connect@2.25.9 2014-08-30 01:19:53 -04:00
Douglas Christopher Wilson
b6ae091bdf 4.8.6 2014-08-27 21:51:37 -04:00
Douglas Christopher Wilson
a206b4e273 deps: update example dependencies 2014-08-27 21:25:56 -04:00
Douglas Christopher Wilson
2b2733c235 deps: qs@2.2.0 2014-08-27 21:22:41 -04:00
Douglas Christopher Wilson
7fb7bcc0f7 Merge tag '3.16.8' 2014-08-27 21:20:53 -04:00
Douglas Christopher Wilson
0299bee8fa 3.16.8 2014-08-27 21:09:18 -04:00
Douglas Christopher Wilson
6a581c9961 deps: connect@2.25.8 2014-08-27 21:04:41 -04:00
Douglas Christopher Wilson
4986b1cb4c tests: add some res.sendfile directory tests 2014-08-24 13:28:39 -04:00
Douglas Christopher Wilson
3de4e4276d docs: fix logo link 2014-08-24 10:05:24 -04:00
Douglas Christopher Wilson
27f195374d 4.8.5 2014-08-18 23:05:16 -04:00
Douglas Christopher Wilson
ff0de5eb27 deps: update example dependencies 2014-08-18 23:00:01 -04:00
Douglas Christopher Wilson
f4ddef1570 deps: serve-static@~1.5.3 2014-08-18 22:58:47 -04:00
Douglas Christopher Wilson
9eafaa23d8 Merge tag '3.16.7' 2014-08-18 22:54:25 -04:00
Douglas Christopher Wilson
0b12cc0cac 3.16.7 2014-08-18 22:45:17 -04:00
Douglas Christopher Wilson
fdd0ccabe8 docs: flatten badges for github look 2014-08-18 22:45:13 -04:00
Douglas Christopher Wilson
8c36eab679 docs: use shields.io badges 2014-08-18 22:19:21 -04:00
Douglas Christopher Wilson
5c145b5490 build: add link to homepage 2014-08-18 22:17:25 -04:00
Raymond Feng
d7bef52591 build: move repository
closes #2268
closes #2270
2014-08-18 22:07:35 -04:00
Douglas Christopher Wilson
1576a95e87 deps: send@0.8.3 2014-08-18 21:57:29 -04:00
Douglas Christopher Wilson
7f92fe66e0 deps: connect@2.25.7 2014-08-18 21:41:14 -04:00
Douglas Christopher Wilson
0cf02d4667 4.8.4 2014-08-15 00:26:39 -04:00
Douglas Christopher Wilson
ef52b80d75 deps: update example dependencies 2014-08-15 00:24:28 -04:00
Douglas Christopher Wilson
1ca01c0c47 tests: add router.use validation tests
closes #2299
2014-08-15 00:22:54 -04:00
Douglas Christopher Wilson
fbceae2716 tests: add additional res.sendFile test
closes #2298
2014-08-15 00:18:54 -04:00
Douglas Christopher Wilson
ad3ca25c58 deps: qs@1.2.2 2014-08-15 00:16:20 -04:00
Douglas Christopher Wilson
666ffc62d8 deps: serve-static@~1.5.2 2014-08-15 00:15:28 -04:00
Douglas Christopher Wilson
6680132392 Merge tag '3.16.6' 2014-08-15 00:14:42 -04:00
Douglas Christopher Wilson
f13f4652da 3.16.6 2014-08-14 23:53:32 -04:00
Douglas Christopher Wilson
059c068c7b deps: send@0.8.2
fixes #2300
2014-08-14 23:52:07 -04:00
Douglas Christopher Wilson
49947f1476 deps: connect@2.25.6 2014-08-14 23:49:39 -04:00
Douglas Christopher Wilson
0dddd772c0 3.16.5 2014-08-11 22:30:09 -04:00
Douglas Christopher Wilson
0f87c6f392 deps: connect@2.25.5 2014-08-11 22:29:35 -04:00
Douglas Christopher Wilson
1643ae442c 4.8.3 2014-08-10 22:30:07 -04:00
Douglas Christopher Wilson
2594f3103b deps: serve-static@~1.5.1 2014-08-10 22:29:24 -04:00
Douglas Christopher Wilson
8473b3c338 deps: qs@1.2.1 2014-08-10 22:27:42 -04:00
Douglas Christopher Wilson
59cb99e9be Merge tag '3.16.4' 2014-08-10 22:26:49 -04:00
Douglas Christopher Wilson
7119f2b16d 3.16.4 2014-08-10 22:22:58 -04:00
Douglas Christopher Wilson
a57efea173 Fix original URL parsing in res.location 2014-08-10 22:20:54 -04:00
Douglas Christopher Wilson
4a4ca7347a deps: parseurl@~1.3.0 2014-08-10 22:19:10 -04:00
Douglas Christopher Wilson
570f60d36e deps: connect@2.25.4 2014-08-10 22:18:12 -04:00
Douglas Christopher Wilson
d13e613584 3.16.3 2014-08-07 22:32:16 -04:00
Douglas Christopher Wilson
9204e1f42a deps: connect@2.25.3 2014-08-07 22:31:53 -04:00
Douglas Christopher Wilson
22ca953e96 4.8.2 2014-08-07 12:05:02 -04:00
Douglas Christopher Wilson
7989c883fe deps: qs@1.2.0 2014-08-07 12:03:53 -04:00
Douglas Christopher Wilson
e05a52078a Merge tag '3.16.2' 2014-08-07 12:01:08 -04:00
Douglas Christopher Wilson
ddac571fdf 3.16.2 2014-08-07 11:59:54 -04:00
Douglas Christopher Wilson
982d24b475 deps: connect@2.25.2 2014-08-07 11:53:40 -04:00
Douglas Christopher Wilson
552b441f8a 4.8.1 2014-08-06 18:21:55 -04:00
Douglas Christopher Wilson
e8f8ea7e05 docs: update examples for deprecations 2014-08-06 18:13:54 -04:00
Douglas Christopher Wilson
4f5b27dd81 deps: update example dependencies 2014-08-06 18:10:32 -04:00
Douglas Christopher Wilson
cca88a7c47 Merge tag '3.16.1' 2014-08-06 18:09:15 -04:00
Douglas Christopher Wilson
ea427c1bb4 3.16.1 2014-08-06 18:07:31 -04:00
Douglas Christopher Wilson
0bd6c311cf deps: mocha@~1.21.4 2014-08-06 18:06:19 -04:00
Douglas Christopher Wilson
7f606ebf29 deps: connect@2.25.1 2014-08-06 18:05:50 -04:00
Douglas Christopher Wilson
a3b5adcf4a deps: qs@1.1.0 2014-08-06 17:50:08 -04:00
Douglas Christopher Wilson
1150ca7264 Fix incorrect deprecation warnings on res.download
fixes #2284
2014-08-06 17:48:36 -04:00
Douglas Christopher Wilson
4aea02310a 4.8.0 2014-08-06 02:50:59 -04:00
Fabien Franzen
17cea29013 Support mounted app as any argument to app.use()
fixes #2277
2014-08-06 02:49:06 -04:00
Douglas Christopher Wilson
8449f23f0d Deprecate res.sendfile 2014-08-06 02:27:16 -04:00
Douglas Christopher Wilson
2cb029f896 Add res.sendFile
fixes #1906
closes #2266
2014-08-06 02:26:51 -04:00
Douglas Christopher Wilson
7e32fa1be6 deps: update example dependencies 2014-08-06 01:59:09 -04:00
Douglas Christopher Wilson
1168d0bb8b deps: qs@1.0.2 2014-08-06 01:58:32 -04:00
Douglas Christopher Wilson
7d0f1c3db9 deps: serve-static@~1.5.0 2014-08-06 01:57:15 -04:00
Douglas Christopher Wilson
19abf7684b Merge tag '3.16.0' 2014-08-06 01:55:45 -04:00
Douglas Christopher Wilson
c652cf7eed 3.16.0 2014-08-06 01:40:46 -04:00
Douglas Christopher Wilson
19fd6f85b0 deps: connect-redis@~1.5.0 2014-08-06 01:39:12 -04:00
Douglas Christopher Wilson
b6c5b0511f deps: jade@~1.5.0 2014-08-06 01:37:58 -04:00
Douglas Christopher Wilson
0e42a37edd deps: send@0.8.1 2014-08-06 01:37:13 -04:00
Douglas Christopher Wilson
b24ed15878 deps: connect@2.25.0 2014-08-06 01:35:00 -04:00
Douglas Christopher Wilson
12e070e39a tests: add encoding tests for res.sendfile 2014-08-04 17:38:04 -04:00
60 changed files with 2207 additions and 485 deletions

38
.gitignore vendored
View File

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

View File

@@ -1,11 +0,0 @@
.git*
benchmarks/
coverage/
docs/
examples/
support/
test/
testing.js
.DS_Store
.travis.yml
Contributing.md

View File

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

View File

@@ -1,3 +1,241 @@
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 +584,280 @@
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
- Router & Route - public API
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 +2240,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 +2454,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
View File

@@ -1,11 +1,11 @@
[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](https://expressjs.com/)
[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
[![NPM Version](https://badge.fury.io/js/express.svg)](https://badge.fury.io/js/express)
[![Build Status](https://travis-ci.org/visionmedia/express.svg?branch=master)](https://travis-ci.org/visionmedia/express)
[![Coverage Status](https://img.shields.io/coveralls/visionmedia/express.svg)](https://coveralls.io/r/visionmedia/express)
[![Gittip](http://img.shields.io/gittip/dougwilson.svg)](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/

View File

@@ -1,5 +1,5 @@
// check out https://github.com/visionmedia/node-pwd
// check out https://github.com/tj/node-pwd
/**
* Module dependencies.

View File

@@ -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');

View File

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

View File

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

View 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;

View 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;

View 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');
}

View File

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

View File

@@ -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');
};
};

View File

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

View File

@@ -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 });

View File

@@ -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');
});

View File

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

View File

@@ -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);
}

View File

@@ -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');

View File

@@ -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);
});
/**

View File

@@ -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];
}
@@ -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 aborted errors
if (err && err.code !== 'ECONNABORT') {
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 aborted errors
if (err && err.code !== 'ECONNABORT') {
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);
}

View File

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

View File

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

View File

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

View File

@@ -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;
};
});

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Fast, unopinionated, minimalist web framework",
"version": "4.7.4",
"version": "4.10.2",
"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.3",
"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.0",
"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.3",
"qs": "2.3.2",
"range-parser": "~1.0.2",
"send": "0.10.1",
"serve-static": "~1.7.1",
"type-is": "~1.5.3",
"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.2",
"mocha": "~2.0.0",
"should": "~4.2.1",
"supertest": "~0.14.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.1",
"connect-redis": "~2.1.0",
"cookie-parser": "~1.3.3",
"express-session": "~1.9.1",
"jade": "~1.7.0",
"method-override": "~2.3.0",
"morgan": "~1.4.1",
"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/"
}
}

View File

@@ -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();

View File

@@ -1,4 +1,4 @@
var app = require('../../examples/auth/app')
var app = require('../../examples/auth')
var request = require('supertest')
function getCookie(res) {

View File

@@ -1,5 +1,5 @@
var app = require('../../examples/cookies/app')
var app = require('../../examples/cookies')
, request = require('supertest');
describe('cookies', function(){

View File

@@ -1,5 +1,5 @@
var app = require('../../examples/downloads/app')
var app = require('../../examples/downloads')
, request = require('supertest');
describe('downloads', function(){

View 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)
})
})
})

View File

@@ -1,4 +1,4 @@
var app = require('../../examples/params/app')
var app = require('../../examples/params')
var request = require('supertest')
describe('params', function(){

View File

@@ -1,4 +1,4 @@
var app = require('../../examples/resource/app')
var app = require('../../examples/resource')
var request = require('supertest')
describe('resource', function(){

View 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)
})
})
})

View File

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

View File

@@ -131,6 +131,64 @@ describe('app', function(){
})
})
describe('when "views" is given', function(){
it('should lookup the file in the path', function(done){
var app = express();
app.set('views', __dirname + '/fixtures/default_layout');
app.locals.user = { name: 'tobi' };
app.render('user.jade', 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 = express();
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
app.set('views', views);
app.locals.user = { name: 'tobi' };
app.render('user.jade', 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 = express();
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
app.set('views', views);
app.locals.name = 'tobi';
app.render('name.jade', 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 = express();
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
app.set('views', views);
app.locals.name = 'tobi';
app.render('pet.jade', function(err, str){
err.message.should.equal('Failed to lookup view "pet.jade" 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();

View File

@@ -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(){

View File

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

@@ -0,0 +1 @@
20%

View File

@@ -0,0 +1 @@
p= name

View File

@@ -0,0 +1 @@
p= user.name

1
test/fixtures/local_layout/user.jade vendored Normal file
View File

@@ -0,0 +1 @@
span= user.name

View File

@@ -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';
}

View File

@@ -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);
})
})
})

View File

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

View File

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

View File

@@ -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(){

View File

@@ -114,6 +114,54 @@ 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 = express();
app.set('views', __dirname + '/fixtures/default_layout');
app.use(function(req, res){
res.render('user.jade', { 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 = express();
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
app.set('views', views);
app.use(function(req, res){
res.render('user.jade', { user: { name: 'tobi' } });
});
request(app)
.get('/')
.expect('<span>tobi</span>', done);
})
it('should lookup in later paths until found', function(done){
var app = express();
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
app.set('views', views);
app.use(function(req, res){
res.render('name.jade', { name: 'tobi' });
});
request(app)
.get('/')
.expect('<p>tobi</p>', done);
})
})
})
})
describe('.render(name, option)', function(){

View File

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

View File

@@ -1,21 +1,225 @@
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, fn)', function(){
it('should invoke the callback when complete', function(done){
var app = express();
describe('.sendFile(path)', function () {
it('should error missing path', function (done) {
var app = createApp();
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', done)
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(200)
.end(function(){});
.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);
})
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){
@@ -31,6 +235,44 @@ describe('res', function(){
.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;
@@ -189,6 +431,30 @@ describe('res', function(){
.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);
});
describe('with an absolute path', function(){
it('should transfer the file', function(done){
var app = express();
@@ -319,3 +585,13 @@ describe('res', function(){
})
})
})
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
View 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);
})
})
})

View File

@@ -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(){