Compare commits

..

274 Commits

Author SHA1 Message Date
Douglas Christopher Wilson
86328767fe 3.19.2 2015-02-01 15:15:53 -05:00
Douglas Christopher Wilson
e497d068a1 deps: marked@0.3.3 2015-02-01 14:58:19 -05:00
Douglas Christopher Wilson
926a71f5ac deps: should@~4.6.2 2015-02-01 14:56:18 -05:00
Douglas Christopher Wilson
55f5a2dc8d deps: proxy-addr@~1.0.6 2015-02-01 14:55:42 -05:00
Douglas Christopher Wilson
fe435e497e deps: connect@2.28.3 2015-02-01 14:54:46 -05:00
Douglas Christopher Wilson
0c567b3282 3.19.1 2015-01-21 03:18:16 -05:00
Douglas Christopher Wilson
855176b633 tests: remove more mocking uses 2015-01-21 03:17:30 -05:00
Douglas Christopher Wilson
b95f2ee820 deps: should@~4.6.1 2015-01-21 02:14:27 -05:00
Douglas Christopher Wilson
ae92db98f3 deps: ejs@2.1.4 2015-01-21 02:13:17 -05:00
Douglas Christopher Wilson
0b25547ca0 deps: send@0.11.1 2015-01-21 02:05:03 -05:00
Douglas Christopher Wilson
548f2865e2 deps: connect@2.28.2 2015-01-21 02:03:28 -05:00
Douglas Christopher Wilson
ee3f2b073c 3.19.0 2015-01-09 01:07:12 -05:00
Douglas Christopher Wilson
34b6385dc3 deps: debug@~2.1.1 2015-01-09 01:05:41 -05:00
Douglas Christopher Wilson
11529a2ea0 Fix OPTIONS responses to include the HEAD method property
fixes #2459
2015-01-08 23:30:42 -05:00
Douglas Christopher Wilson
b0f8809e3d deps: commander@2.6.0 2015-01-08 22:23:04 -05:00
Douglas Christopher Wilson
ec1175daa3 Use readline for prompt in express(1) 2015-01-08 22:21:50 -05:00
Douglas Christopher Wilson
3a1d9b8289 deps: ejs@2.0.8 2015-01-08 22:17:19 -05:00
Douglas Christopher Wilson
192be8fea3 deps: send@0.11.0 2015-01-08 22:15:53 -05:00
Douglas Christopher Wilson
1afcff955b deps: proxy-addr@~1.0.5 2015-01-08 22:14:46 -05:00
Douglas Christopher Wilson
224fe05697 deps: methods@~1.1.1 2015-01-08 21:31:32 -05:00
Douglas Christopher Wilson
2dd332b491 deps: mocha@~2.1.0 2015-01-08 21:25:17 -05:00
Douglas Christopher Wilson
33e4193f45 deps: should@~4.4.4 2015-01-08 21:24:28 -05:00
Douglas Christopher Wilson
c163d2f33d deps: connect@2.28.1 2015-01-08 21:18:56 -05:00
Douglas Christopher Wilson
a715ba6be4 docs: Gittip is now Gratipay 2015-01-08 17:06:08 -05:00
Douglas Christopher Wilson
4405b849a9 3.18.6 2014-12-12 21:39:47 -05:00
Troy Goode
5d74a553d6 Fix exception in req.fresh/req.stale without response headers
fixes #2468
2014-12-12 21:17:23 -05:00
Douglas Christopher Wilson
262b60537f 3.18.5 2014-12-11 23:10:34 -05:00
Douglas Christopher Wilson
e77e644224 deps: should@~4.3.1 2014-12-11 23:03:01 -05:00
Douglas Christopher Wilson
ce89c00cd9 deps: istanbul@0.3.5 2014-12-11 23:01:12 -05:00
Douglas Christopher Wilson
d5ad34b0e9 deps: connect@2.27.6 2014-12-10 22:55:36 -05:00
Douglas Christopher Wilson
ebfa00a9c0 build: remove support folder 2014-11-29 12:10:30 -05:00
Douglas Christopher Wilson
d23417e6e8 examples: switch examples used in tests to ejs engine 2014-11-29 12:09:00 -05:00
Douglas Christopher Wilson
91824514ce tests: run render tests with internal template engine 2014-11-28 09:10:44 -05:00
Douglas Christopher Wilson
6c8bcd5c4e 3.18.4 2014-11-23 15:42:03 -05:00
Douglas Christopher Wilson
95e63ec287 deps: should@~4.3.0 2014-11-23 15:36:59 -05:00
Douglas Christopher Wilson
ebca5887cc deps: proxy-addr@~1.0.4 2014-11-23 15:34:57 -05:00
Douglas Christopher Wilson
8535d3a990 deps: supertest@~0.15.0 2014-11-23 14:58:56 -05:00
Douglas Christopher Wilson
13184c4379 deps: etag@~1.5.1 2014-11-23 14:58:22 -05:00
Douglas Christopher Wilson
eaba4eeb70 deps: connect@2.27.4 2014-11-23 14:53:23 -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
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
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
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
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
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
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
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
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
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
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
Douglas Christopher Wilson
b97f6eb506 examples: fix github view example
closes #2344
2014-09-08 19:31:58 -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
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
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
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
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
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
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
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
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
15590d75b2 3.15.3 2014-08-04 17:31:52 -04:00
Douglas Christopher Wilson
e8b471ff4f fix res.sendfile regression for serving directory index files 2014-08-04 17:30:25 -04:00
Douglas Christopher Wilson
767db01b79 deps: connect@2.24.3 2014-08-04 17:26:43 -04:00
Douglas Christopher Wilson
696e150f0a 3.15.2 2014-07-27 15:53:45 -04:00
Douglas Christopher Wilson
819265c7ae deps: send@0.7.2 2014-07-27 15:41:54 -04:00
Douglas Christopher Wilson
baf8b14a71 deps: depd@0.4.4
fixes #2262
2014-07-27 15:41:19 -04:00
Douglas Christopher Wilson
06e7685d65 deps: connect@2.24.2 2014-07-27 15:40:19 -04:00
Douglas Christopher Wilson
9f4968aaa3 3.15.1 2014-07-26 17:42:18 -04:00
Douglas Christopher Wilson
afea3c0ae8 deps: mocha@~1.21.0 2014-07-26 17:36:42 -04:00
Douglas Christopher Wilson
edaabe66cf deps: send@0.7.1 2014-07-26 17:36:40 -04:00
Douglas Christopher Wilson
f724730e1a Adjust wording on deprecation messages 2014-07-26 17:36:39 -04:00
Douglas Christopher Wilson
e49d0dc9e3 deps: depd@0.4.3
fixes #2262
2014-07-26 17:36:24 -04:00
Douglas Christopher Wilson
e7a3fbaf48 deps: connect@2.24.1 2014-07-26 17:30:13 -04:00
Douglas Christopher Wilson
928952e7f0 3.15.0 2014-07-23 00:53:19 -04:00
Ashley Streb
a28b7a85cf Fix req.protocol for proxy-direct connections
fixes #2252
2014-07-23 00:19:10 -04:00
Douglas Christopher Wilson
3fc8dc54ee docs: replace Gittip badge 2014-07-23 00:13:21 -04:00
Douglas Christopher Wilson
0d77305a1a Pass options from res.sendfile to send
fixes #2017
2014-07-23 00:08:15 -04:00
Douglas Christopher Wilson
323c185079 deps: send@0.7.0 2014-07-23 00:05:45 -04:00
Douglas Christopher Wilson
1d0da9036b deps: parseurl@~1.2.0 2014-07-22 23:53:50 -04:00
Douglas Christopher Wilson
683ba1cd75 deps: depd@0.4.2 2014-07-22 23:52:44 -04:00
Douglas Christopher Wilson
e4ff5281c9 deps: connect@2.24.0 2014-07-22 23:50:36 -04:00
Douglas Christopher Wilson
7414a1f463 deps: debug@1.0.4 2014-07-18 14:18:35 -04:00
Douglas Christopher Wilson
916c53737d 3.14.0 2014-07-11 13:11:31 -04:00
Douglas Christopher Wilson
b2382a7336 Remove unnecessary escaping in res.jsonp 2014-07-11 00:20:11 -04:00
Douglas Christopher Wilson
f684a64df7 Add explicit "Rosetta Flash JSONP abuse" protection 2014-07-11 00:15:55 -04:00
Douglas Christopher Wilson
5d03d0eac8 Deprecate res.redirect(url, status) 2014-07-10 23:21:16 -04:00
Yad Smood
544c6665f5 Fix res.send(status, num) to send num as json
fixes #2226
2014-07-10 23:19:57 -04:00
Douglas Christopher Wilson
cf8005e63f deps: basic-auth@1.0.0 2014-07-10 23:07:45 -04:00
Douglas Christopher Wilson
25ef8425d2 deps: methods@1.1.0 2014-07-10 23:06:15 -04:00
Douglas Christopher Wilson
577cc1d1a0 deps: parseurl@~1.1.3 2014-07-10 23:03:26 -04:00
Douglas Christopher Wilson
3c87a6aede deps: debug@1.0.3 2014-07-10 23:02:24 -04:00
Douglas Christopher Wilson
7c1f90bf16 deps: istanbul@0.3.0 2014-07-10 22:57:08 -04:00
Douglas Christopher Wilson
7bcf5f5085 deps: connect@2.23.0 2014-07-10 22:56:31 -04:00
Douglas Christopher Wilson
abe0ffa311 3.13.0 2014-07-04 00:40:01 -04:00
Douglas Christopher Wilson
b601d64203 tests: fix inconsistent test 2014-07-04 00:38:59 -04:00
Douglas Christopher Wilson
f381f2d9b6 add deprecation message to req.auth 2014-07-04 00:24:01 -04:00
Douglas Christopher Wilson
12507cfcd0 deps: connect@2.22.0 2014-07-03 23:09:44 -04:00
Douglas Christopher Wilson
185e327e29 add deprecation message to app.configure 2014-07-03 14:13:39 -04:00
Douglas Christopher Wilson
c468f5ff20 deps: send@0.5.0 2014-07-03 13:10:33 -04:00
Douglas Christopher Wilson
9bb47fba30 3.12.1 2014-06-26 18:35:54 -04:00
Douglas Christopher Wilson
78d489d730 deps: cookie-signature@1.0.4 2014-06-26 18:26:34 -04:00
Douglas Christopher Wilson
8ffb9f9477 deps: connect@2.21.1 2014-06-26 18:25:48 -04:00
Douglas Christopher Wilson
9cb147370e deps: istanbul@0.2.12 2014-06-26 18:21:26 -04:00
Douglas Christopher Wilson
75422c16bf 3.12.0 2014-06-21 21:57:02 -04:00
Douglas Christopher Wilson
e66667e465 Use media-typer to alter content-type charset 2014-06-21 21:09:12 -04:00
Douglas Christopher Wilson
7d6208e0af deps: connect@2.21.0 2014-06-21 21:09:10 -04:00
Douglas Christopher Wilson
2a105df9f2 3.11.0 2014-06-19 23:36:00 -04:00
Douglas Christopher Wilson
9c731f1883 deprecate things with depd module 2014-06-19 23:34:58 -04:00
Douglas Christopher Wilson
5a4e9125de deps: connect@2.20.2 2014-06-19 23:24:07 -04:00
Douglas Christopher Wilson
9db1367c2d deps: buffer-crc32@0.2.3 2014-06-19 22:55:21 -04:00
Douglas Christopher Wilson
73c5533e66 3.10.5 2014-06-12 00:28:08 -04:00
Douglas Christopher Wilson
3b1f747f96 deps: send@0.4.3 2014-06-12 00:24:21 -04:00
Douglas Christopher Wilson
9e9827d236 deps: debug@1.0.2 2014-06-12 00:23:15 -04:00
Douglas Christopher Wilson
a76d508424 deps: connect@2.19.6 2014-06-12 00:20:30 -04:00
Douglas Christopher Wilson
f881784e9b 3.10.4 2014-06-09 18:53:52 -04:00
Douglas Christopher Wilson
5af625903f deps: send@0.4.2 2014-06-09 18:51:55 -04:00
Douglas Christopher Wilson
dc94f305cc deps: debug@1.0.1 2014-06-09 18:50:36 -04:00
Douglas Christopher Wilson
8060a49c6c deps: connect@2.19.5 2014-06-09 18:50:00 -04:00
Douglas Christopher Wilson
2fd31f6ea6 3.10.3 2014-06-05 23:38:58 -04:00
Douglas Christopher Wilson
9cf7bba8f0 deps: connect@2.19.4 2014-06-05 23:37:22 -04:00
Douglas Christopher Wilson
2e257d1cf7 build: use compressed formats in package 2014-06-05 19:45:00 -04:00
Douglas Christopher Wilson
56831d7799 deps: debug@1.0.0 2014-06-05 19:34:05 -04:00
Jonathan Ong
6d65ae5ba6 use vary@0.1.0
with backwards compatibility

closes #2161
2014-06-05 19:32:32 -04:00
Douglas Christopher Wilson
c919b4a573 3.10.2 2014-06-03 21:35:34 -04:00
Douglas Christopher Wilson
fe6f392c2d deps: connect@2.19.3 2014-06-03 21:33:55 -04:00
Douglas Christopher Wilson
3b34a537ee 3.10.1 2014-06-03 16:45:09 -04:00
Douglas Christopher Wilson
ad79ce9c4b deps: connect@2.19.2 2014-06-03 16:44:29 -04:00
Douglas Christopher Wilson
721f6388c3 deps: proxy-addr@1.0.1 2014-06-03 16:42:49 -04:00
Douglas Christopher Wilson
298ac11018 3.10.0 2014-06-03 00:40:27 -04:00
Douglas Christopher Wilson
bb6e207336 deps: connect@2.19.1 2014-06-03 00:37:57 -04:00
Douglas Christopher Wilson
f433b7c7cf replace utils.escape with html-escape 2014-06-03 00:37:32 -04:00
Douglas Christopher Wilson
a94278abd1 deps: send@0.4.1 2014-06-02 21:31:23 -04:00
Douglas Christopher Wilson
a7cd5a2553 deps: methods@1.0.1 2014-06-02 19:19:56 -04:00
Douglas Christopher Wilson
0dc5836d5e 3.9.0 2014-05-30 21:35:12 -04:00
Douglas Christopher Wilson
8751d7ecf8 tests: add more tests 2014-05-30 21:28:48 -04:00
Douglas Christopher Wilson
c21226aa7c improve etag control for res.send
closes #1435
closes #2129
2014-05-30 21:02:21 -04:00
Douglas Christopher Wilson
3e358458f4 tests: add more etag tests 2014-05-30 19:51:32 -04:00
Douglas Christopher Wilson
766b3aecf7 deps: update example dependencies 2014-05-29 23:39:52 -04:00
Douglas Christopher Wilson
8ab96ab80d mark res.send ETag as weak and reduce collisions 2014-05-29 23:14:21 -04:00
Douglas Christopher Wilson
1f2e00ef8d deps: should@~4.0.0 2014-05-29 22:53:59 -04:00
Douglas Christopher Wilson
b49453cf0d update send to 0.4.0
closes #2150
2014-05-29 22:32:03 -04:00
Douglas Christopher Wilson
faffcb889c update connect to 2.18.0 2014-05-29 22:21:53 -04:00
Tiago Relvao
3c0ec59432 Include ETag in HEAD requests
backport of commit 3c7310ebcb
2014-05-28 22:31:00 -04:00
Douglas Christopher Wilson
d4a2843500 tests: add param test with encoded value
closes #2143
2014-05-28 22:30:24 -04:00
Douglas Christopher Wilson
e7ad49bbbe tests: add accepts test with params 2014-05-28 00:30:29 -04:00
Douglas Christopher Wilson
ad9a414fae tests: add more acceptance tests 2014-05-28 00:24:24 -04:00
Douglas Christopher Wilson
c18c2a8e68 tests: exclude untestable lines in examples from coverage 2014-05-28 00:07:27 -04:00
Douglas Christopher Wilson
1d54868c12 update supertest to 0.13.0 2014-05-27 23:54:34 -04:00
Douglas Christopher Wilson
f7e73e2da0 3.8.1 2014-05-27 23:43:13 -04:00
Douglas Christopher Wilson
867728b5ab update connect to 2.17.3 2014-05-27 23:02:27 -04:00
Douglas Christopher Wilson
f6bbeafd26 3.8.0 2014-05-21 01:52:14 -04:00
Douglas Christopher Wilson
f14e39d451 set proper charset in content-type for res.send
This will write strings to sockets with an explicit "utf8" encoding
(which is the default) and will override the charset in the
Content-Type so it properly relfects the encoding of the response.

closes #1631
closes #2092
2014-05-21 01:31:08 -04:00
Alberto Leal
084f5d891b Keep previous Content-Type for res.jsonp
backport of commit be997fd654
2014-05-21 01:04:29 -04:00
Douglas Christopher Wilson
b0f72e13d9 update connect to 2.17.1 2014-05-21 00:55:10 -04:00
Douglas Christopher Wilson
8d7d80ef9d tests: add more tests of web-service example 2014-05-21 00:08:17 -04:00
Douglas Christopher Wilson
cf5de082b5 tests: add more tests of cookies example 2014-05-21 00:08:06 -04:00
Douglas Christopher Wilson
1944451082 tests: add more tests of negotiation example 2014-05-20 23:50:58 -04:00
Douglas Christopher Wilson
602e5a8200 tests: add more tests of mvc example 2014-05-20 23:41:09 -04:00
Douglas Christopher Wilson
83b8b7acb7 tests: add more various tests 2014-05-20 23:25:51 -04:00
Douglas Christopher Wilson
e660f19507 tests: add req.acceptsLanguage tests 2014-05-20 23:13:29 -04:00
Douglas Christopher Wilson
ff412b927d tests: add req.acceptsEncoding tests 2014-05-20 23:08:01 -04:00
Douglas Christopher Wilson
392ef1eb06 tests: add more app.render tests 2014-05-20 22:57:00 -04:00
Douglas Christopher Wilson
dcecdc9be6 update mocha to 1.19.0 2014-05-20 21:22:02 -04:00
Douglas Christopher Wilson
ed69b68892 update connect to 2.17.0 2014-05-20 21:20:56 -04:00
Douglas Christopher Wilson
42b982e13c build: remove coveralls from devDependencies 2014-05-20 20:22:55 -04:00
Douglas Christopher Wilson
359f12791a build: prevent failure from coveralls 2014-05-18 23:05:34 -04:00
Douglas Christopher Wilson
b91cd66fc5 3.7.0 2014-05-18 10:40:13 -04:00
Douglas Christopher Wilson
787d630157 update should to 3.3.1 2014-05-18 10:38:42 -04:00
Douglas Christopher Wilson
1f938c560a tests: improve examples/auth tests 2014-05-18 01:54:05 -04:00
Douglas Christopher Wilson
a96924a555 build: remove lib-cov fork 2014-05-18 01:35:12 -04:00
Douglas Christopher Wilson
33dc6629ff update connect to 2.16.2 2014-05-18 01:30:44 -04:00
Douglas Christopher Wilson
1b3fb0af8c build: add coverage reporting 2014-05-18 01:25:15 -04:00
Douglas Christopher Wilson
12da523ff7 build: test coverage with istanbul 2014-05-18 01:23:15 -04:00
Douglas Christopher Wilson
0f49d80623 build: clean up package file 2014-05-18 01:16:38 -04:00
Douglas Christopher Wilson
1717516a78 build: improve platform portability 2014-05-18 01:14:45 -04:00
Jonathan Ong
328c6d3060 remove unnecessary test/support/http
backport of 643397ed21
2014-05-18 00:57:54 -04:00
Douglas Christopher Wilson
566720be15 improve proxy trust with ip address list
closes #2099
2014-05-17 20:02:20 -04:00
Douglas Christopher Wilson
65f13c3cc6 update connect to 2.16.1 2014-05-17 14:30:52 -04:00
Douglas Christopher Wilson
d98e2e7498 deprecation messages are bright red on TTYs 2014-05-13 17:06:45 -04:00
Douglas Christopher Wilson
f7be983a77 3.6.0 2014-05-09 17:03:23 -04:00
Douglas Christopher Wilson
a2553126dd support PURGE method 2014-05-09 10:19:20 -04:00
Douglas Christopher Wilson
0639c45acd build: add soft testing on node.js 0.11 2014-05-09 01:03:35 -04:00
Douglas Christopher Wilson
920f46ad65 tests: add more res.jsonp? tests 2014-05-08 21:27:01 -04:00
Douglas Christopher Wilson
35a66d8a14 tests: add test for res.format in route
closes #2016
2014-05-08 21:15:48 -04:00
Douglas Christopher Wilson
4e1e252e17 deprecate res.json(obj, status)
closes #2106
closes #2107
2014-05-08 21:11:05 -04:00
Douglas Christopher Wilson
0461e55380 deprecate app.del
closes #2095
2014-05-08 19:16:25 -04:00
Douglas Christopher Wilson
8dc4ff26f9 Add standard deprecation utility
* deprecations ignore when NODE_ENV=test
 * uses node.js util.deprecate
2014-05-08 19:01:16 -04:00
Roman Shtylman
6f9e927633 remove deprecation message about passing path as array
backport commit 6b19e3dc0a
2014-05-08 18:09:25 -04:00
Oliver Salzburg
40a43eb753 Value parameter of app.set() is now typed optional mixed 2014-05-08 17:04:14 -04:00
Douglas Christopher Wilson
a8bb4bab2b tests: add test for app.delete 2014-05-08 16:40:28 -04:00
Douglas Christopher Wilson
417999884b update supertest to 0.12.0 2014-05-08 16:29:41 -04:00
Douglas Christopher Wilson
17a739e35f update mkdirp to 0.5.0 2014-05-08 16:25:06 -04:00
Douglas Christopher Wilson
66b38b58bc Merge branch '3.5.x' into 3.x 2014-05-08 13:55:00 -04:00
Douglas Christopher Wilson
4b646c2f8d docs: update core contributors 2014-05-07 23:08:55 -04:00
Douglas Christopher Wilson
35757ed1f5 docs: add NPM badge 2014-05-07 22:59:12 -04:00
Douglas Christopher Wilson
fed0d5df5c docs: use SVG badges 2014-05-07 22:57:05 -04:00
Douglas Christopher Wilson
0d468cbe6c update history 2014-05-05 00:42:50 -04:00
Douglas Christopher Wilson
f92ba6d0cf update connect to 2.15.0 2014-05-05 00:35:43 -04:00
Douglas Christopher Wilson
0408e3727e deps: debug@^0.8.0 2014-05-02 17:02:28 -04:00
168 changed files with 5703 additions and 2681 deletions

41
.gitignore vendored
View File

@@ -1,19 +1,26 @@
coverage.html
.DS_Store
lib-cov
*.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
.coverage_data
cover_html
test.js
.idea

View File

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

View File

@@ -1,8 +1,11 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.11"
matrix:
allow_failures:
- node_js: "0.11"
fast_finish: true
script: "npm run-script test-travis"
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"

View File

@@ -1,56 +1,682 @@
4.1.2 / 2014-05-08
3.19.2 / 2015-02-01
===================
* deps: connect@2.28.3
- deps: compression@~1.3.1
- deps: csurf@~1.6.6
- deps: errorhandler@~1.3.3
- deps: express-session@~1.10.2
- deps: serve-index@~1.6.1
- deps: type-is@~1.5.6
* deps: proxy-addr@~1.0.6
- deps: ipaddr.js@0.1.8
3.19.1 / 2015-01-20
===================
* deps: connect@2.28.2
- deps: body-parser@~1.10.2
- deps: serve-static@~1.8.1
* deps: send@0.11.1
- Fix root path disclosure
3.19.0 / 2015-01-09
===================
* Fix `OPTIONS` responses to include the `HEAD` method property
* Use `readline` for prompt in `express(1)`
* deps: commander@2.6.0
* deps: connect@2.28.1
- deps: body-parser@~1.10.1
- deps: compression@~1.3.0
- deps: connect-timeout@~1.5.0
- deps: csurf@~1.6.4
- deps: debug@~2.1.1
- deps: errorhandler@~1.3.2
- deps: express-session@~1.10.1
- deps: finalhandler@0.3.3
- deps: method-override@~2.3.1
- deps: morgan@~1.5.1
- deps: serve-favicon@~2.2.0
- deps: serve-index@~1.6.0
- deps: serve-static@~1.8.0
- deps: type-is@~1.5.5
* deps: debug@~2.1.1
* deps: methods@~1.1.1
* deps: proxy-addr@~1.0.5
- deps: ipaddr.js@0.1.6
* deps: send@0.11.0
- deps: debug@~2.1.1
- deps: etag@~1.5.1
- deps: ms@0.7.0
- deps: on-finished@~2.2.0
3.18.6 / 2014-12-12
===================
* Fix exception in `req.fresh`/`req.stale` without response headers
3.18.5 / 2014-12-11
===================
* deps: connect@2.27.6
- deps: compression@~1.2.2
- deps: express-session@~1.9.3
- deps: http-errors@~1.2.8
- deps: serve-index@~1.5.3
- deps: type-is@~1.5.4
3.18.4 / 2014-11-23
===================
* deps: connect@2.27.4
- deps: body-parser@~1.9.3
- deps: compression@~1.2.1
- deps: errorhandler@~1.2.3
- deps: express-session@~1.9.2
- deps: qs@2.3.3
- deps: serve-favicon@~2.1.7
- deps: serve-static@~1.5.1
- deps: type-is@~1.5.3
* deps: etag@~1.5.1
* deps: proxy-addr@~1.0.4
- deps: ipaddr.js@0.1.5
3.18.3 / 2014-11-09
===================
* deps: connect@2.27.3
- Correctly invoke async callback asynchronously
- deps: csurf@~1.6.3
3.18.2 / 2014-10-28
===================
* deps: connect@2.27.2
- Fix handling of URLs containing `://` in the path
- deps: body-parser@~1.9.2
- deps: qs@2.3.2
3.18.1 / 2014-10-22
===================
* Fix internal `utils.merge` deprecation warnings
* deps: connect@2.27.1
- deps: body-parser@~1.9.1
- deps: express-session@~1.9.1
- deps: finalhandler@0.3.2
- deps: morgan@~1.4.1
- deps: qs@2.3.0
- deps: serve-static@~1.7.1
* deps: send@0.10.1
- deps: on-finished@~2.1.1
3.18.0 / 2014-10-17
===================
* Use `content-disposition` module for `res.attachment`/`res.download`
- Sends standards-compliant `Content-Disposition` header
- Full Unicode support
* Use `etag` module to generate `ETag` headers
* deps: connect@2.27.0
- Use `http-errors` module for creating errors
- Use `utils-merge` module for merging objects
- deps: body-parser@~1.9.0
- deps: compression@~1.2.0
- deps: connect-timeout@~1.4.0
- deps: debug@~2.1.0
- deps: depd@~1.0.0
- deps: express-session@~1.9.0
- deps: finalhandler@0.3.1
- deps: method-override@~2.3.0
- deps: morgan@~1.4.0
- deps: response-time@~2.2.0
- deps: serve-favicon@~2.1.6
- deps: serve-index@~1.5.0
- deps: serve-static@~1.7.0
* deps: debug@~2.1.0
- Implement `DEBUG_FD` env variable support
* deps: depd@~1.0.0
* deps: send@0.10.0
- deps: debug@~2.1.0
- deps: depd@~1.0.0
- deps: etag@~1.5.0
3.17.8 / 2014-10-15
===================
* deps: connect@2.26.6
- deps: compression@~1.1.2
- deps: csurf@~1.6.2
- deps: errorhandler@~1.2.2
3.17.7 / 2014-10-08
===================
* deps: connect@2.26.5
- Fix accepting non-object arguments to `logger`
- deps: serve-static@~1.6.4
3.17.6 / 2014-10-02
===================
* deps: connect@2.26.4
- deps: morgan@~1.3.2
- deps: type-is@~1.5.2
3.17.5 / 2014-09-24
===================
* deps: connect@2.26.3
- deps: body-parser@~1.8.4
- deps: serve-favicon@~2.1.5
- deps: serve-static@~1.6.3
* deps: proxy-addr@~1.0.3
- Use `forwarded` npm module
* deps: send@0.9.3
- deps: etag@~1.4.0
3.17.4 / 2014-09-19
===================
* deps: connect@2.26.2
- deps: body-parser@~1.8.3
- deps: qs@2.2.4
3.17.3 / 2014-09-18
===================
* deps: proxy-addr@~1.0.2
- Fix a global leak when multiple subnets are trusted
- deps: ipaddr.js@0.1.3
3.17.2 / 2014-09-15
===================
* Use `crc` instead of `buffer-crc32` for speed
* deps: connect@2.26.1
- deps: body-parser@~1.8.2
- deps: depd@0.4.5
- deps: express-session@~1.8.2
- deps: morgan@~1.3.1
- deps: serve-favicon@~2.1.3
- deps: serve-static@~1.6.2
* deps: depd@0.4.5
* deps: send@0.9.2
- deps: depd@0.4.5
- deps: etag@~1.3.1
- deps: range-parser@~1.0.2
3.17.1 / 2014-09-08
===================
* Fix error in `req.subdomains` on empty host
3.17.0 / 2014-09-08
===================
* Support 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
===================
* fix `res.sendfile` regression for serving directory index files
* deps: connect@2.24.3
- deps: serve-index@~1.1.5
- deps: serve-static@~1.4.4
* deps: send@0.7.4
- Fix incorrect 403 on Windows and Node.js 0.11
- Fix serving index files without root dir
3.15.2 / 2014-07-27
===================
* deps: connect@2.24.2
- deps: body-parser@~1.5.2
- deps: depd@0.4.4
- deps: express-session@~1.7.2
- deps: morgan@~1.2.2
- deps: serve-static@~1.4.2
* deps: depd@0.4.4
- Work-around v8 generating empty stack traces
* deps: send@0.7.2
- deps: depd@0.4.4
3.15.1 / 2014-07-26
===================
* deps: connect@2.24.1
- deps: body-parser@~1.5.1
- deps: depd@0.4.3
- deps: express-session@~1.7.1
- deps: morgan@~1.2.1
- deps: serve-index@~1.1.4
- deps: serve-static@~1.4.1
* deps: depd@0.4.3
- Fix exception when global `Error.stackTraceLimit` is too low
* deps: send@0.7.1
- deps: depd@0.4.3
3.15.0 / 2014-07-22
===================
* Fix `req.protocol` for proxy-direct connections
* Pass options from `res.sendfile` to `send`
* deps: connect@2.24.0
- deps: body-parser@~1.5.0
- deps: compression@~1.0.9
- deps: connect-timeout@~1.2.1
- deps: debug@1.0.4
- deps: depd@0.4.2
- deps: express-session@~1.7.0
- deps: finalhandler@0.1.0
- deps: method-override@~2.1.2
- deps: morgan@~1.2.0
- deps: multiparty@3.3.1
- deps: parseurl@~1.2.0
- deps: serve-static@~1.4.0
* deps: debug@1.0.4
* deps: depd@0.4.2
- Add `TRACE_DEPRECATION` environment variable
- Remove non-standard grey color from color output
- Support `--no-deprecation` argument
- Support `--trace-deprecation` argument
* deps: parseurl@~1.2.0
- Cache URLs based on original value
- Remove no-longer-needed URL mis-parse work-around
- Simplify the "fast-path" `RegExp`
* deps: send@0.7.0
- Add `dotfiles` option
- Cap `maxAge` value to 1 year
- deps: debug@1.0.4
- deps: depd@0.4.2
3.14.0 / 2014-07-11
===================
* add explicit "Rosetta Flash JSONP abuse" protection
- previous versions are not vulnerable; this is just explicit protection
* deprecate `res.redirect(url, status)` -- use `res.redirect(status, url)` instead
* fix `res.send(status, num)` to send `num` as json (not error)
* remove unnecessary escaping when `res.jsonp` returns JSON response
* deps: basic-auth@1.0.0
- support empty password
- support empty username
* deps: connect@2.23.0
- deps: debug@1.0.3
- deps: express-session@~1.6.4
- deps: method-override@~2.1.0
- deps: parseurl@~1.1.3
- deps: serve-static@~1.3.1
* deps: debug@1.0.3
- Add support for multiple wildcards in namespaces
* deps: methods@1.1.0
- add `CONNECT`
* deps: parseurl@~1.1.3
- faster parsing of href-only URLs
3.13.0 / 2014-07-03
===================
* add deprecation message to `app.configure`
* add deprecation message to `req.auth`
* use `basic-auth` to parse `Authorization` header
* deps: connect@2.22.0
- deps: csurf@~1.3.0
- deps: express-session@~1.6.1
- deps: multiparty@3.3.0
- deps: serve-static@~1.3.0
* deps: send@0.5.0
- Accept string for `maxage` (converted by `ms`)
- Include link in default redirect response
3.12.1 / 2014-06-26
===================
* deps: connect@2.21.1
- deps: cookie-parser@1.3.2
- deps: cookie-signature@1.0.4
- deps: express-session@~1.5.2
- deps: type-is@~1.3.2
* deps: cookie-signature@1.0.4
- fix for timing attacks
3.12.0 / 2014-06-21
===================
* use `media-typer` to alter content-type charset
* deps: connect@2.21.0
- deprecate `connect(middleware)` -- use `app.use(middleware)` instead
- deprecate `connect.createServer()` -- use `connect()` instead
- fix `res.setHeader()` patch to work with with get -> append -> set pattern
- deps: compression@~1.0.8
- deps: errorhandler@~1.1.1
- deps: express-session@~1.5.0
- deps: serve-index@~1.1.3
3.11.0 / 2014-06-19
===================
* deprecate things with `depd` module
* deps: buffer-crc32@0.2.3
* deps: connect@2.20.2
- deprecate `verify` option to `json` -- use `body-parser` npm module instead
- deprecate `verify` option to `urlencoded` -- use `body-parser` npm module instead
- deprecate things with `depd` module
- use `finalhandler` for final response handling
- use `media-typer` to parse `content-type` for charset
- deps: body-parser@1.4.3
- deps: connect-timeout@1.1.1
- deps: cookie-parser@1.3.1
- deps: csurf@1.2.2
- deps: errorhandler@1.1.0
- deps: express-session@1.4.0
- deps: multiparty@3.2.9
- deps: serve-index@1.1.2
- deps: type-is@1.3.1
- deps: vhost@2.0.0
3.10.5 / 2014-06-11
===================
* deps: connect@2.19.6
- deps: body-parser@1.3.1
- deps: compression@1.0.7
- deps: debug@1.0.2
- deps: serve-index@1.1.1
- deps: serve-static@1.2.3
* deps: debug@1.0.2
* deps: send@0.4.3
- Do not throw un-catchable error on file open race condition
- Use `escape-html` for HTML escaping
- deps: debug@1.0.2
- deps: finished@1.2.2
- deps: fresh@0.2.2
3.10.4 / 2014-06-09
===================
* deps: connect@2.19.5
- fix "event emitter leak" warnings
- deps: csurf@1.2.1
- deps: debug@1.0.1
- deps: serve-static@1.2.2
- deps: type-is@1.2.1
* deps: debug@1.0.1
* deps: send@0.4.2
- fix "event emitter leak" warnings
- deps: finished@1.2.1
- deps: debug@1.0.1
3.10.3 / 2014-06-05
===================
* use `vary` module for `res.vary`
* deps: connect@2.19.4
- deps: errorhandler@1.0.2
- deps: method-override@2.0.2
- deps: serve-favicon@2.0.1
* deps: debug@1.0.0
3.10.2 / 2014-06-03
===================
* deps: connect@2.19.3
- deps: compression@1.0.6
3.10.1 / 2014-06-03
===================
* deps: connect@2.19.2
- deps: compression@1.0.4
* deps: proxy-addr@1.0.1
3.10.0 / 2014-06-02
===================
* deps: connect@2.19.1
- deprecate `methodOverride()` -- use `method-override` npm module instead
- deps: body-parser@1.3.0
- deps: method-override@2.0.1
- deps: multiparty@3.2.8
- deps: response-time@2.0.0
- deps: serve-static@1.2.1
* deps: methods@1.0.1
* deps: send@0.4.1
- Send `max-age` in `Cache-Control` in correct format
3.9.0 / 2014-05-30
==================
* fix `req.host` for IPv6 literals
* fix `res.jsonp` error if callback param is object
* custom etag control with `app.set('etag', val)`
- `app.set('etag', function(body, encoding){ return '"etag"' })` custom etag generation
- `app.set('etag', 'weak')` weak tag
- `app.set('etag', 'strong')` strong etag
- `app.set('etag', false)` turn off
- `app.set('etag', true)` standard etag
* Include ETag in HEAD requests
* mark `res.send` ETag as weak and reduce collisions
* update connect to 2.18.0
- deps: compression@1.0.3
- deps: serve-index@1.1.0
- deps: serve-static@1.2.0
* update send to 0.4.0
- Calculate ETag with md5 for reduced collisions
- Ignore stream errors after request ends
- deps: debug@0.8.1
4.1.1 / 2014-04-27
3.8.1 / 2014-05-27
==================
* fix package.json to reflect supported node version
* update connect to 2.17.3
- deps: body-parser@1.2.2
- deps: express-session@1.2.1
- deps: method-override@1.0.2
4.1.0 / 2014-04-24
3.8.0 / 2014-05-21
==================
* pass options from `res.sendfile` to `send`
* preserve casing of headers in `res.header` and `res.set`
* support unicode file names in `res.attachment` and `res.download`
* update accepts to 1.0.1
* update cookie to 0.1.2
* update send to 0.3.0
* update serve-static to 1.1.0
* update type-is to 1.1.0
* keep previous `Content-Type` for `res.jsonp`
* set proper `charset` in `Content-Type` for `res.send`
* update connect to 2.17.1
- fix `res.charset` appending charset when `content-type` has one
- deps: express-session@1.2.0
- deps: morgan@1.1.1
- deps: serve-index@1.0.3
4.0.0 / 2014-04-09
3.7.0 / 2014-05-18
==================
* remove:
- node 0.8 support
- connect and connect's patches except for charset handling
- express(1) - moved to [express-generator](https://github.com/expressjs/generator)
- `express.createServer()` - it has been deprecated for a long time. Use `express()`
- `app.configure` - use logic in your own app code
- `app.router` - is removed
- `req.auth` - use `basic-auth` instead
- `req.accepted*` - use `req.accepts*()` instead
- `res.location` - relative URL resolution is removed
- `res.charset` - include the charset in the content type when using `res.set()`
- all bundled middleware except `static`
* change:
- `app.route` -> `app.mountpath` when mounting an express app in another express app
- `json spaces` no longer enabled by default in development
- `req.accepts*` -> `req.accepts*s` - i.e. `req.acceptsEncoding` -> `req.acceptsEncodings`
- `req.params` is now an object instead of an array
- `res.locals` is no longer a function. It is a plain js object. Treat it as such.
- `res.headerSent` -> `res.headersSent` to match node.js ServerResponse object
* refactor:
- `req.accepts*` with [accepts](https://github.com/expressjs/accepts)
- `req.is` with [type-is](https://github.com/expressjs/type-is)
- [path-to-regexp](https://github.com/component/path-to-regexp)
* add:
- `app.router()` - returns the app Router instance
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
- Router & Route - public API
* proper proxy trust with `app.set('trust proxy', trust)`
- `app.set('trust proxy', 1)` trust first hop
- `app.set('trust proxy', 'loopback')` trust loopback addresses
- `app.set('trust proxy', '10.0.0.1')` trust single IP
- `app.set('trust proxy', '10.0.0.1/16')` trust subnet
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
- `app.set('trust proxy', false)` turn off
- `app.set('trust proxy', true)` trust everything
* update connect to 2.16.2
- deprecate `res.headerSent` -- use `res.headersSent`
- deprecate `res.on("header")` -- use on-headers module instead
- fix edge-case in `res.appendHeader` that would append in wrong order
- json: use body-parser
- urlencoded: use body-parser
- dep: bytes@1.0.0
- dep: cookie-parser@1.1.0
- dep: csurf@1.2.0
- dep: express-session@1.1.0
- dep: method-override@1.0.1
3.6.0 / 2014-05-09
==================
* deprecate `app.del()` -- use `app.delete()` instead
* deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead
- the edge-case `res.json(status, num)` requires `res.status(status).json(num)`
* deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead
- the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)`
* support PURGE method
- add `app.purge`
- add `router.purge`
- include PURGE in `app.all`
* update connect to 2.15.0
* Add `res.appendHeader`
* Call error stack even when response has been sent
* Patch `res.headerSent` to return Boolean
* Patch `res.headersSent` for node.js 0.8
* Prevent default 404 handler after response sent
* dep: compression@1.0.2
* dep: connect-timeout@1.1.0
* dep: debug@^0.8.0
* dep: errorhandler@1.0.1
* dep: express-session@1.0.4
* dep: morgan@1.0.1
* dep: serve-favicon@2.0.0
* dep: serve-index@1.0.2
* update debug to 0.8.0
* add `enable()` method
* change from stderr to stdout
* update methods to 1.0.0
- add PURGE
* update mkdirp to 0.5.0
3.5.3 / 2014-05-08
==================

View File

@@ -1,6 +1,6 @@
(The MIT License)
Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2009-2013 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,34 +0,0 @@
MOCHA_OPTS= --check-leaks
REPORTER = dot
check: test
test: test-unit test-acceptance
test-unit:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--globals setImmediate,clearImmediate \
$(MOCHA_OPTS)
test-acceptance:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--bail \
test/acceptance/*.js
test-cov: lib-cov
@EXPRESS_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
lib-cov:
@jscoverage lib lib-cov
bench:
@$(MAKE) -C benchmarks
clean:
rm -f coverage.html
rm -fr lib-cov
.PHONY: test test-unit test-acceptance bench clean

View File

@@ -1,8 +1,11 @@
[![express logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/)
[![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)](http://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
[![Build Status](https://travis-ci.org/visionmedia/express.svg?branch=master)](https://travis-ci.org/visionmedia/express) [![Gittip](https://img.shields.io/gittip/visionmedia.svg)](https://www.gittip.com/visionmedia/)
[![NPM version](https://img.shields.io/npm/v/express.svg?style=flat)](https://www.npmjs.org/package/express)
[![Build Status](https://img.shields.io/travis/strongloop/express.svg?style=flat)](https://travis-ci.org/strongloop/express)
[![Coverage Status](https://img.shields.io/coveralls/strongloop/express.svg?style=flat)](https://coveralls.io/r/strongloop/express)
[![Gratipay](https://img.shields.io/gratipay/dougwilson.svg?style=flat)](https://gratipay.com/dougwilson/)
```js
var express = require('express');
@@ -15,22 +18,17 @@ 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
$ npm install express
$ npm install -g express
## Quick Start
The quickest way to get started with express is to utilize the executable [`express(1)`](http://github.com/expressjs/generator) to generate an application as shown below:
Install the executable. The executable's major version will match Express's:
$ npm install -g express-generator@3
The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:
Create the app:
$ npm install -g express
$ express /tmp/foo && cd /tmp/foo
Install dependencies:
@@ -39,15 +37,17 @@ app.listen(3000);
Start the server:
$ npm start
$ node app
## Features
* Built on [Connect](http://github.com/senchalabs/connect)
* Robust routing
* HTTP helpers (redirection, caching, etc)
* View system supporting 14+ template engines
* Content negotiation
* Focus on high performance
* Environment based configuration
* Executable for generating applications quickly
* High test coverage
@@ -57,17 +57,19 @@ app.listen(3000);
it a great solution for single page applications, web sites, hybrids, or public
HTTP APIs.
Express does not force you to use any specific ORM or template engine. With support for over
Built on Connect, you can use _only_ what you need, and nothing more. Applications
can be as big or as small as you like, even a single file. Express does
not force you to use any specific ORM or template engine. With support for over
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js),
you can quickly craft your perfect framework.
## More Information
* [Website and Documentation](http://expressjs.com/) stored at [visionmedia/expressjs.com](https://github.com/visionmedia/expressjs.com)
* [Website and Documentation](http://expressjs.com/) stored at [strongloop/expressjs.com](https://github.com/strongloop/expressjs.com)
* Join #express on freenode
* [Google Group](http://groups.google.com/group/express-js) for discussion
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) and [defunctzombie](https://twitter.com/defunctzombie) on twitter for updates
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
* Visit the [Wiki](http://github.com/strongloop/express/wiki)
* [Русскоязычная документация](http://jsman.ru/express/)
* Run express examples [online](https://runnable.com/express)
@@ -75,7 +77,7 @@ app.listen(3000);
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
$ git clone git://github.com/visionmedia/express.git --depth 1
$ git clone git://github.com/strongloop/express.git --depth 1
$ cd express
$ npm install
@@ -95,14 +97,35 @@ To run the test suite, first invoke the following command within the repo, insta
Then run the tests:
$ make test
```sh
$ npm test
```
## Contributors
Author: [TJ Holowaychuk](http://github.com/visionmedia)
Lead Maintainer: [Roman Shtylman](https://github.com/defunctzombie)
Contributors: https://github.com/visionmedia/express/graphs/contributors
https://github.com/strongloop/express/graphs/contributors
## License
MIT
(The MIT License)
Copyright (c) 2009-2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

440
bin/express Executable file
View File

@@ -0,0 +1,440 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander')
, mkdirp = require('mkdirp')
, pkg = require('../package.json')
, readline = require('readline')
, version = pkg.version
, os = require('os')
, fs = require('fs');
// CLI
program
.version(version)
.usage('[options] [dir]')
.option('-s, --sessions', 'add session support')
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
.option('-H, --hogan', 'add hogan.js engine support')
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
.option('-f, --force', 'force on non-empty directory')
.parse(process.argv);
// Path
var path = program.args.shift() || '.';
// end-of-line code
var eol = os.EOL
// Template engine
program.template = 'jade';
if (program.ejs) program.template = 'ejs';
if (program.jshtml) program.template = 'jshtml';
if (program.hogan) program.template = 'hjs';
/**
* Routes index template.
*/
var index = [
''
, '/*'
, ' * GET home page.'
, ' */'
, ''
, 'exports.index = function(req, res){'
, ' res.render(\'index\', { title: \'Express\' });'
, '};'
].join(eol);
/**
* Routes users template.
*/
var users = [
''
, '/*'
, ' * GET users listing.'
, ' */'
, ''
, 'exports.list = function(req, res){'
, ' res.send("respond with a resource");'
, '};'
].join(eol);
/**
* Jade layout template.
*/
var jadeLayout = [
'doctype html'
, 'html'
, ' head'
, ' title= title'
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
, ' body'
, ' block content'
].join(eol);
/**
* Jade index template.
*/
var jadeIndex = [
'extends layout'
, ''
, 'block content'
, ' h1= title'
, ' p Welcome to #{title}'
].join(eol);
/**
* EJS index template.
*/
var ejsIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title><%= title %></title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1><%= title %></h1>'
, ' <p>Welcome to <%= title %></p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML layout template.
*/
var jshtmlLayout = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title> @write(title) </title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' @write(body)'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML index template.
*/
var jshtmlIndex = [
'<h1>@write(title)</h1>'
, '<p>Welcome to @write(title)</p>'
].join(eol);
/**
* Hogan.js index template.
*/
var hoganIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title>{{ title }}</title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1>{{ title }}</h1>'
, ' <p>Welcome to {{ title }}</p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* Default css template.
*/
var css = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
/**
* Default less template.
*/
var less = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
/**
* Default stylus template.
*/
var stylus = [
'body'
, ' padding: 50px'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, 'a'
, ' color: #00B7FF'
].join(eol);
/**
* App template.
*/
var app = [
''
, '/**'
, ' * Module dependencies.'
, ' */'
, ''
, 'var express = require(\'express\');'
, 'var routes = require(\'./routes\');'
, 'var user = require(\'./routes/user\');'
, 'var http = require(\'http\');'
, 'var path = require(\'path\');'
, ''
, 'var app = express();'
, ''
, '// all environments'
, 'app.set(\'port\', process.env.PORT || 3000);'
, 'app.set(\'views\', path.join(__dirname, \'views\'));'
, 'app.set(\'view engine\', \':TEMPLATE\');'
, 'app.use(express.favicon());'
, 'app.use(express.logger(\'dev\'));'
, 'app.use(express.json());'
, 'app.use(express.urlencoded());'
, 'app.use(express.methodOverride());{sess}'
, 'app.use(app.router);{css}'
, 'app.use(express.static(path.join(__dirname, \'public\')));'
, ''
, '// development only'
, 'if (\'development\' == app.get(\'env\')) {'
, ' app.use(express.errorHandler());'
, '}'
, ''
, 'app.get(\'/\', routes.index);'
, 'app.get(\'/users\', user.list);'
, ''
, 'http.createServer(app).listen(app.get(\'port\'), function(){'
, ' console.log(\'Express server listening on port \' + app.get(\'port\'));'
, '});'
, ''
].join(eol);
// Generate application
(function createApplication(path) {
emptyDirectory(path, function(empty){
if (empty || program.force) {
createApplicationAt(path);
} else {
confirm('destination is not empty, continue? [y/N] ', function (ok) {
if (ok) {
process.stdin.destroy();
createApplicationAt(path);
} else {
abort('aborting');
}
});
}
});
})(path);
/**
* Prompt for confirmation on STDOUT/STDIN
*/
function confirm(msg, callback) {
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question(msg, function (input) {
rl.close();
callback(/^y|yes|ok|true$/i.test(input));
});
}
/**
* Create application at the given directory `path`.
*
* @param {String} path
*/
function createApplicationAt(path) {
console.log();
process.on('exit', function(){
console.log();
console.log(' install dependencies:');
console.log(' $ cd %s && npm install', path);
console.log();
console.log(' run the app:');
console.log(' $ node app');
console.log();
});
mkdir(path, function(){
mkdir(path + '/public');
mkdir(path + '/public/javascripts');
mkdir(path + '/public/images');
mkdir(path + '/public/stylesheets', function(){
switch (program.css) {
case 'less':
write(path + '/public/stylesheets/style.less', less);
break;
case 'stylus':
write(path + '/public/stylesheets/style.styl', stylus);
break;
default:
write(path + '/public/stylesheets/style.css', css);
}
});
mkdir(path + '/routes', function(){
write(path + '/routes/index.js', index);
write(path + '/routes/user.js', users);
});
mkdir(path + '/views', function(){
switch (program.template) {
case 'ejs':
write(path + '/views/index.ejs', ejsIndex);
break;
case 'jade':
write(path + '/views/layout.jade', jadeLayout);
write(path + '/views/index.jade', jadeIndex);
break;
case 'jshtml':
write(path + '/views/layout.jshtml', jshtmlLayout);
write(path + '/views/index.jshtml', jshtmlIndex);
break;
case 'hjs':
write(path + '/views/index.hjs', hoganIndex);
break;
}
});
// CSS Engine support
switch (program.css) {
case 'less':
app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));');
break;
case 'stylus':
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));');
break;
default:
app = app.replace('{css}', '');
}
// Session support
app = app.replace('{sess}', program.sessions
? eol + 'app.use(express.session({ secret: \'your secret here\' }));'
: '');
// Template support
app = app.replace(':TEMPLATE', program.template);
// package.json
var pkg = {
name: 'application-name'
, version: '0.0.1'
, private: true
, scripts: { start: 'node app.js' }
, dependencies: {
express: version
}
}
if (program.template) pkg.dependencies[program.template] = '*';
// CSS Engine support
switch (program.css) {
case 'less':
pkg.dependencies['less-middleware'] = '~0.1.15';
break;
default:
if (program.css) {
pkg.dependencies[program.css] = '*';
}
}
write(path + '/package.json', JSON.stringify(pkg, null, 2));
write(path + '/app.js', app);
});
}
/**
* Check if the given directory `path` is empty.
*
* @param {String} path
* @param {Function} fn
*/
function emptyDirectory(path, fn) {
fs.readdir(path, function(err, files){
if (err && 'ENOENT' != err.code) throw err;
fn(!files || !files.length);
});
}
/**
* echo str > path.
*
* @param {String} path
* @param {String} str
*/
function write(path, str) {
fs.writeFile(path, str);
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
}
/**
* Mkdir -p.
*
* @param {String} path
* @param {Function} fn
*/
function mkdir(path, fn) {
mkdirp(path, 0755, function(err){
if (err) throw err;
console.log(' \033[36mcreate\033[0m : ' + path);
fn && fn();
});
}
/**
* Exit with the given `str`.
*
* @param {String} str
*/
function abort(str) {
console.error(str);
process.exit(1);
}

View File

@@ -2,11 +2,8 @@
* Module dependencies.
*/
var express = require('../..');
var hash = require('./pass').hash;
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var express = require('../..')
, hash = require('./pass').hash;
var app = module.exports = express();
@@ -17,15 +14,14 @@ app.set('views', __dirname + '/views');
// middleware
app.use(bodyParser());
app.use(cookieParser('shhhh, very secret'));
app.use(session());
app.use(express.urlencoded({ extended: false }));
app.use(express.session({ secret: 'shhhh, very secret' }));
// Session-persisted message middleware
app.use(function(req, res, next){
var err = req.session.error;
var msg = req.session.success;
var err = req.session.error
, msg = req.session.success;
delete req.session.error;
delete req.session.success;
res.locals.message = '';
@@ -65,7 +61,7 @@ function authenticate(name, pass, fn) {
if (err) return fn(err);
if (hash == user.hash) return fn(null, user);
fn(new Error('invalid password'));
});
})
}
function restrict(req, res, next) {
@@ -101,9 +97,9 @@ app.post('/login', function(req, res){
authenticate(req.body.username, req.body.password, function(err, user){
if (user) {
// Regenerate session when signing in
// to prevent fixation
// to prevent fixation
req.session.regenerate(function(){
// Store the user's primary key
// Store the user's primary key
// in the session store to be retrieved,
// or in this case the entire user object
req.session.user = user;
@@ -121,6 +117,7 @@ app.post('/login', function(req, res){
});
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1,10 +1,6 @@
/**
* Module dependencies.
*/
var express = require('../..');
var logger = require('morgan');
var app = express();
var express = require('../..')
, app = express();
app.set('views', __dirname);
app.set('view engine', 'jade');
@@ -18,11 +14,14 @@ while (n--) {
pets.push({ name: 'Jane', age: 6, species: 'ferret' });
}
app.use(logger('dev'));
app.use(express.logger('dev'));
app.get('/', function(req, res){
res.render('pets', { pets: pets });
});
app.listen(3000);
console.log('Express listening on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,3 +1,4 @@
var users = [];
users.push({ name: 'Tobi' });

View File

@@ -1,8 +1,8 @@
var express = require('../../');
var app = module.exports = express();
var users = require('./db');
var express = require('../../')
, app = module.exports = express()
, users = require('./db');
// so either you can deal with different types of formatting
// so either you can deal with different types of formatting
// for expected response in index.js
app.get('/', function(req, res){
res.format({
@@ -21,7 +21,7 @@ app.get('/', function(req, res){
json: function(){
res.json(users);
}
});
})
});
// or you could write a tiny middleware like
@@ -32,12 +32,13 @@ function format(path) {
var obj = require(path);
return function(req, res){
res.format(obj);
};
}
}
app.get('/users', format('./users'));
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('listening on port 3000');
console.log('Express started on port 3000');
}

View File

@@ -1,21 +1,20 @@
/**
* Module dependencies.
*/
var express = require('../../');
var favicon = require('static-favicon');
var cookie-parser = require('cookie-parser');
var app = module.exports = express();
// ignore GET /favicon.ico
app.use(favicon());
app.use(express.favicon());
// pass a secret to cookieParser() for signed cookies
app.use(cookieParser('manny is cool'));
app.use(express.cookieParser('manny is cool'));
// add req.session cookie support
app.use(cookieSession());
app.use(express.cookieSession());
// do something with the session
app.use(count);
@@ -27,7 +26,8 @@ function count(req, res) {
res.send('viewed ' + n + ' times\n');
}
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express server listening on port 3000');
console.log('Express started on port 3000');
}

View File

@@ -1,33 +1,30 @@
/**
* Module dependencies.
*/
var express = require('../../');
var app = module.exports = express();
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var express = require('../../')
, app = module.exports = express();
// add favicon() before logger() so
// GET /favicon.ico requests are not
// logged, because this middleware
// reponds to /favicon.ico and does not
// call next()
app.use(favicon());
app.use(express.favicon());
// custom log format
if ('test' != process.env.NODE_ENV)
app.use(logger(':method :url'));
if ('test' != process.env.NODE_ENV) app.use(express.logger(':method :url'));
// parses request cookies, populating
// req.cookies and req.signedCookies
// when the secret is passed, used
// when the secret is passed, used
// for signing the cookies.
app.use(cookieParser('my secret here'));
app.use(express.cookieParser('my secret here'));
// parses json, x-www-form-urlencoded, and multipart/form-data
app.use(bodyParser());
app.use(express.bodyParser());
app.get('/', function(req, res){
if (req.cookies.remember) {
@@ -50,7 +47,8 @@ app.post('/', function(req, res){
res.redirect('back');
});
if (!module.parent){
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -2,11 +2,9 @@
* Module dependencies.
*/
var express = require('../..');
var logger = require('morgan');
var app = express();
var bodyParser = require('body-parser');
var api = express();
var express = require('../..')
, app = express()
, api = express();
// app middleware
@@ -14,8 +12,8 @@ app.use(express.static(__dirname + '/public'));
// api middleware
api.use(logger('dev'));
api.use(bodyParser());
api.use(express.logger('dev'));
api.use(express.bodyParser());
/**
* CORS support.

View File

@@ -1,14 +1,14 @@
/**
* Module dependencies.
*/
var express = require('../../');
var app = module.exports = express();
var express = require('../../')
, app = module.exports = express();
app.get('/', function(req, res){
res.send('<ul>'
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
+ '<li>Download <a href="/files/utf-8 한中日.txt">utf-8 한中日.txt</a>.</li>'
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
+ '</ul>');
});
@@ -16,8 +16,8 @@ app.get('/', function(req, res){
// /files/* is accessed via req.params[0]
// but here we name it :file
app.get('/files/:file(*)', function(req, res, next){
var file = req.params.file;
var path = __dirname + '/files/' + file;
var file = req.params.file
, path = __dirname + '/files/' + file;
res.download(path);
});
@@ -38,6 +38,7 @@ app.use(function(err, req, res, next){
}
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1 +0,0 @@
한中日

View File

@@ -1,3 +1,4 @@
/**
* Module dependencies.
*/
@@ -43,7 +44,8 @@ app.get('/', function(req, res){
});
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express app started on port 3000');
console.log('Express started on port 3000');
}

View File

@@ -2,15 +2,13 @@
* Module dependencies.
*/
var express = require('../../');
var app = module.exports = express();
var logger = require('morgan');
var favicon = require('static-favicon');
var silent = 'test' == process.env.NODE_ENV;
var express = require('../../')
, app = module.exports = express()
, silent = 'test' == process.env.NODE_ENV;
// general config
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set('view engine', 'ejs');
// our custom "verbose errors" setting
// which we can use in the templates
@@ -19,40 +17,20 @@ app.enable('verbose errors');
// disable them in production
// use $ NODE_ENV=production node examples/error-pages
if ('production' == app.settings.env) {
app.disable('verbose errors');
}
if ('production' == app.settings.env) app.disable('verbose errors');
app.use(favicon());
app.use(express.favicon());
silent || app.use(logger('dev'));
silent || app.use(express.logger('dev'));
// Routes
// "app.router" positions our routes
// above the middleware defined below,
// this means that Express will attempt
// to match & call routes _before_ continuing
// on, at which point we assume it's a 404 because
// no route has handled the request.
app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/404', function(req, res, next){
// trigger a 404 since no other middleware
// will match /404 after this one, and we're not
// responding here
next();
});
app.get('/403', function(req, res, next){
// trigger a 403 error
var err = new Error('not allowed!');
err.status = 403;
next(err);
});
app.get('/500', function(req, res, next){
// trigger a generic (500) error
next(new Error('keyboard cat!'));
});
// Error handlers
app.use(app.router);
// Since this is the last non-error-handling
// middleware use()d, we assume 404, as nothing else
@@ -64,7 +42,7 @@ app.get('/500', function(req, res, next){
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.render('404', { url: req.url });
@@ -101,8 +79,33 @@ app.use(function(err, req, res, next){
res.render('500', { error: err });
});
// Routes
app.get('/', function(req, res){
res.render('index.ejs');
});
app.get('/404', function(req, res, next){
// trigger a 404 since no other middleware
// will match /404 after this one, and we're not
// responding here
next();
});
app.get('/403', function(req, res, next){
// trigger a 403 error
var err = new Error('not allowed!');
err.status = 403;
next(err);
});
app.get('/500', function(req, res, next){
// trigger a generic (500) error
next(new Error('keyboard cat!'));
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
silent || console.log('Express started on port 3000');
console.log('Express started on port 3000');
}

View File

@@ -0,0 +1,3 @@
<% include error_header %>
<h2>Cannot find <%= url %></h2>
<% include footer %>

View File

@@ -1,5 +0,0 @@
extends error
block content
h2 Cannot find #{url}

View File

@@ -0,0 +1,8 @@
<% include error_header %>
<h2>Error: <%= error.message %></h2>
<% if (settings['verbose errors']) { %>
<pre><%= error.stack %></pre>
<% } else { %>
<p>An error occurred!</p>
<% } %>
<% include footer %>

View File

@@ -1,13 +0,0 @@
// note that we extend a different
// layout with jade for 4xx & 5xx
// responses
extends error
block content
h1 Error: #{error.message}
if settings['verbose errors']
pre= error.stack
else
p An error ocurred!

View File

@@ -1,6 +0,0 @@
html
head
title Error
body
h1 An error occurred!
block content

View File

@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>An error occurred!</h1>

View File

@@ -0,0 +1,2 @@
</body>
</html>

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom Pages Example</title>
</head>
<body>
<h1>My Site</h1>
<h2>Pages Example</h2>
<ul>
<li>visit <a href="/500">500</a></li>
<li>visit <a href="/404">404</a></li>
<li>visit <a href="/403">403</a></li>
</ul>
</body>
</html>

View File

@@ -1,15 +0,0 @@
extends layout
block content
h2 Pages Example
ul
li
| visit
a(href="/500") 500
li
| visit
a(href="/404") 404
li
| visit
a(href='/403') 403

View File

@@ -1,6 +0,0 @@
html
head
title Custom Pages Example
body
h1 My Site
block content

View File

@@ -1,13 +1,20 @@
/**
* Module dependencies.
*/
var express = require('../../');
var logger = require('morgan');
var app = module.exports = express();
var test = app.get('env') == 'test';
var express = require('../../')
, app = module.exports = express()
, test = app.get('env') == 'test';
if (!test) app.use(logger('dev'));
if (!test) app.use(express.logger('dev'));
app.use(app.router);
// the error handler is strategically
// placed *below* the app.router; if it
// were above it would not receive errors
// from app.get() etc
app.use(error);
// error handling middleware have an arity of 4
// instead of the typical (req, res, next),
@@ -35,11 +42,7 @@ app.get('/next', function(req, res, next){
});
});
// the error handler is placed after routes
// if it were above it would not receive errors
// from app.get() etc
app.use(error);
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1,7 +1,6 @@
var express = require('../..');
var logger = require('morgan');
var app = express();
var express = require('../..')
, app = express();
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
@@ -21,10 +20,10 @@ User.prototype.toJSON = function(){
return {
id: this.id,
name: this.name
};
}
};
app.use(logger('dev'));
app.use(express.logger('dev'));
// earlier on expose an object
// that we can tack properties on.
@@ -57,5 +56,8 @@ app.get('/user', function(req, res){
res.render('page');
});
app.listen(3000);
console.log('app listening on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,3 +1,4 @@
var express = require('../../');
var app = express();
@@ -6,5 +7,8 @@ app.get('/', function(req, res){
res.send('Hello World');
});
app.listen(3000);
console.log('Express started on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,3 +1,4 @@
/**
* Module dependencies.
*/
@@ -11,7 +12,9 @@ var pub = __dirname + '/public';
// setup middleware
var app = express();
app.use(app.router);
app.use(express.static(pub));
app.use(express.errorHandler());
// Optional since express defaults to CWD/views
@@ -38,11 +41,8 @@ app.get('/', function(req, res){
res.render('users', { users: users });
});
// change this to a better error handler in your code
// sending stacktrace to users in production is not good
app.use(function(err, req, res, next) {
res.send(err.stack);
});
app.listen(3000);
console.log('Express app started on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,10 +1,11 @@
/**
* Module dependencies.
*/
var express = require('../..');
var fs = require('fs');
var md = require('marked').parse;
var express = require('../..')
, fs = require('fs')
, md = require('marked').parse;
var app = module.exports = express();
@@ -17,13 +18,13 @@ app.engine('md', function(path, options, fn){
var html = md(str);
html = html.replace(/\{([^}]+)\}/g, function(_, name){
return options[name] || '';
});
})
fn(null, html);
} catch(err) {
fn(err);
}
});
});
})
app.set('views', __dirname + '/views');
@@ -32,12 +33,13 @@ app.set('view engine', 'md');
app.get('/', function(req, res){
res.render('index', { title: 'Markdown Example' });
});
})
app.get('/fail', function(req, res){
res.render('missing', { title: 'Markdown Example' });
});
})
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1,12 +1,16 @@
/**
* Module dependencies.
*/
var express = require('../..');
var multiparty = require('multiparty');
var format = require('util').format;
var express = require('../..')
, format = require('util').format;
var app = module.exports = express();
var app = module.exports = express()
// bodyParser in connect 2.x uses node-formidable to parse
// the multipart form data.
app.use(express.bodyParser())
app.get('/', function(req, res){
res.send('<form method="post" enctype="multipart/form-data">'
@@ -17,43 +21,17 @@ app.get('/', function(req, res){
});
app.post('/', function(req, res, next){
// create a form to begin parsing
var form = new multiparty.Form();
var image;
var title;
form.on('error', next);
form.on('close', function(){
res.send(format('\nuploaded %s (%d Kb) as %s'
, image.filename
, image.size / 1024 | 0
, title));
});
// listen on field event for title
form.on('field', function(name, val){
if (name !== 'title') return;
title = val;
});
// listen on part event for image file
form.on('part', function(part){
if (!part.filename) return;
if (part.name !== 'image') return part.resume();
image = {};
image.filename = part.filename;
image.size = 0;
part.on('data', function(buf){
image.size += buf.length;
});
});
// parse the form
form.parse(req);
// the uploaded file can be found as `req.files.image` and the
// title field as `req.body.title`
res.send(format('\nuploaded %s (%d Kb) to %s as %s'
, req.files.image.name
, req.files.image.size / 1024 | 0
, req.files.image.path
, req.body.title));
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(4000);
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,3 +1,4 @@
exports.index = function(req, res){
res.redirect('/users');
};

View File

@@ -1,9 +1,8 @@
/**
* Module dependencies.
*/
var db = require('../../db');
exports.engine = 'ejs';
exports.before = function(req, res, next){
var pet = db.pets[req.params.pet_id];
if (!pet) return next(new Error('Pet not found'));

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style.css">
<title>Edit <%= pet.name %></title>
</head>
<body>
<h1><%= pet.name %></h1>
<form action="/pet/<%= pet.id %>" method="post">
<input type="hidden" name="_method" value="put">
<label>Name: <input type="text" name="user[name]" value="<%= pet.name %>"></label>
<input type="submit" value="Update">
</form>
</body>
</html>

View File

@@ -1,7 +0,0 @@
link(rel='stylesheet', href='/style.css')
h1= pet.name
form(action='/pet/#{pet.id}', method='post')
input(type='hidden', name='_method', value='put')
label= 'Name: '
input(type='text', name='pet[name]', value=pet.name)
input(type='submit', value='Update')

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style.css">
<title><%= pet.name %></title>
</head>
<body>
<h1><%= pet.name %> <a href="/pet/<%= pet.id %>/edit">edit</a></h1>
<p>You are viewing <%= pet.name %></p>
</body>
</html>

View File

@@ -1,6 +0,0 @@
link(rel='stylesheet', href='/style.css')
h1= pet.name
a(href='/pet/#{pet.id}/edit') edit
p You are viewing #{pet.name}

View File

@@ -1,6 +1,3 @@
/**
* Module dependencies.
*/
var db = require('../../db');

View File

@@ -1,6 +1,3 @@
/**
* Module dependencies.
*/
var db = require('../../db');
@@ -15,7 +12,7 @@ exports.before = function(req, res, next){
// found it, move on to the routes
next();
});
};
}
exports.list = function(req, res, next){
res.render('list', { users: db.users });

View File

@@ -0,0 +1,12 @@
<link rel="stylesheet" href="/style.css" />
<h1><%= user.name %></h1>
<form action='/user/<%= user.id %>' method='post'>
<input type="hidden" name="_method" value="put" />
<label>Name: <input type="text" name="user[name]" value="<%= user.name %>" /></label>
<input type="submit" value="Update" />
</form>
<form action='/user/<%= user.id %>/pet' method='post'>
<label>Pet: <input type="text" name="pet[name]" placeholder="name" /></label>
<input type="submit" value="Add" />
</form>

View File

@@ -1,12 +0,0 @@
link(rel='stylesheet', href='/style.css')
h1= user.name
form(action='/user/#{user.id}', method='post')
input(type='hidden', name='_method', value='put')
label= 'Name: '
input(type='text', name='user[name]', value='#{user.name}')
input(type='submit', value='Update')
form(action='/user/#{user.id}/pet', method='post')
label= 'Pet: '
input(type='text', name='pet[name]', placeholder='Name')
input(type='submit', value='Add')

View File

@@ -0,0 +1,8 @@
<link rel="stylesheet" href="/style.css" />
<h1>Users</h1>
<p>Click a user below to view their pets.</p>
<ul>
<% users.forEach(function(user){ %>
<li><a href="/user/<%= user.id %>"><%= user.name %></a></li>
<% }) %>
</ul>

View File

@@ -1,7 +0,0 @@
link(rel='stylesheet', href='/style.css')
h1 Users
p Click a user below to view their pets.
ul
each user in users
li
a(href='/user/#{user.id}')= user.name

View File

@@ -0,0 +1,21 @@
<link rel="stylesheet" href="/style.css" />
<h1><%= user.name %> <a href="/user/<%= user.id %>/edit">edit</a></h1>
<% if (hasMessages) { %>
<ul id="messages">
<% messages.forEach(function(msg){ %>
<li><%= msg %></li>
<% }) %>
</ul>
<% } %>
<% if (user.pets.length) { %>
<p>View <%= user.name %>s pets:</p>
<ul>
<% user.pets.forEach(function(pet){ %>
<li><a href="/pet/<%= pet.id %>"><%= pet.name %></a></li>
<% }) %>
</ul>
<% } else { %>
<p>No pets!</p>
<% } %>

View File

@@ -1,17 +0,0 @@
link(rel='stylesheet', href='/style.css')
h1= user.name + ' '
a(href='/user/#{user.id}/edit') edit
if (hasMessages)
ul#messages
each msg in messages
li= msg
if (user.pets.length)
p View #{user.name}'s pets:
ul
each pet in user.pets
li
a(href='/pet/#{pet.id}')= pet.name
else
p No pets!

View File

@@ -1,3 +1,4 @@
// faux database
var pets = exports.pets = [];

View File

@@ -1,21 +1,14 @@
/**
* Module dependencies.
*/
var express = require('../..');
var logger = require('morgan');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
var app = module.exports = express();
// settings
// set our default template engine to "jade"
// which prevents the need for extensions
app.set('view engine', 'jade');
// map .renderFile to ".html" files
app.engine('html', require('ejs').renderFile);
// make ".html" the default
app.set('view engine', 'html');
// set views for error and 404 pages
app.set('views', __dirname + '/views');
@@ -32,20 +25,19 @@ app.response.message = function(msg){
};
// log
if (!module.parent) app.use(logger('dev'));
if (!module.parent) app.use(express.logger('dev'));
// serve static files
app.use(express.static(__dirname + '/public'));
// session support
app.use(cookieParser('some secret here'));
app.use(session());
app.use(express.session({ secret: 'some secret here' }));
// parse request bodies (req.body)
app.use(bodyParser());
app.use(express.urlencoded({ extended: true }));
// override methods (put, delete)
app.use(methodOverride());
// support _method (PUT in forms etc)
app.use(express.methodOverride());
// expose the "messages" local variable when views are rendered
app.use(function(req, res, next){
@@ -93,7 +85,8 @@ app.use(function(req, res, next){
res.status(404).render('404', { url: req.originalUrl });
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('\n listening on port 3000\n');
console.log('Express started on port 3000');
}

View File

@@ -1,20 +1,17 @@
/**
* Module dependencies.
*/
var express = require('../../..');
var fs = require('fs');
var express = require('../../..')
, fs = require('fs');
module.exports = function(parent, options){
var verbose = options.verbose;
fs.readdirSync(__dirname + '/../controllers').forEach(function(name){
verbose && console.log('\n %s:', name);
var obj = require('./../controllers/' + name);
var name = obj.name || name;
var prefix = obj.prefix || '';
var app = express();
var method;
var path;
var obj = require('./../controllers/' + name)
, name = obj.name || name
, prefix = obj.prefix || ''
, app = express()
, method
, path;
// allow specifying the view engine
if (obj.engine) app.set('view engine', obj.engine);

View File

@@ -0,0 +1,3 @@
<link rel="stylesheet" href="/style.css" />
<h1>404: Not Found</h1>
<p>Sorry we can't find <%= url %></p>

View File

@@ -1,3 +0,0 @@
link(rel='stylesheet', href='/style.css')
h1 404: Not Found
p Sorry we can't find #{url}

View File

@@ -0,0 +1,3 @@
<link rel="stylesheet" href="/style.css" />
<h1>500: Internal Server Error</h1>
<p>Looks like something blew up!</p>

View File

@@ -1,3 +0,0 @@
link(rel='stylesheet', href='/style.css')
h1 500: Internal Server Error
p Looks like something blew up!

View File

@@ -1,3 +1,4 @@
// first:
// $ npm install redis online
// $ redis-server
@@ -6,10 +7,10 @@
* Module dependencies.
*/
var express = require('../..');
var online = require('online');
var redis = require('redis');
var db = redis.createClient();
var express = require('../..')
, online = require('online')
, redis = require('redis')
, db = redis.createClient();
// online
@@ -49,5 +50,8 @@ app.get('/', function(req, res, next){
});
});
app.listen(3000);
console.log('listening on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,9 +1,10 @@
/**
* Module dependencies.
*/
var express = require('../../');
var app = module.exports = express();
var express = require('../../')
, app = module.exports = express();
// Faux database
@@ -17,9 +18,9 @@ var users = [
// Convert :to and :from to integers
app.param(['to', 'from'], function(req, res, next, num, name){
req.params[name] = num = parseInt(num, 10);
if( isNaN(num) ){
app.param(['to', 'from'], function(req, res, next, num, name){
req.params[name] = parseInt(num, 10);
if( isNaN(req.params[name]) ){
next(new Error('failed to parseInt '+num));
} else {
next();
@@ -57,13 +58,14 @@ app.get('/user/:user', function(req, res, next){
*/
app.get('/users/:from-:to', function(req, res, next){
var from = req.params.from;
var to = req.params.to;
var names = users.map(function(user){ return user.name; });
var from = req.params.from
, to = req.params.to
, names = users.map(function(user){ return user.name; });
res.send('users ' + names.slice(from, to).join(', '));
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
}

View File

@@ -1,3 +1,4 @@
/**
* Module dependencies.
*/
@@ -11,13 +12,16 @@ var app = module.exports = express();
app.resource = function(path, obj) {
this.get(path, obj.index);
this.get(path + '/:a..:b.:format?', function(req, res){
var a = parseInt(req.params.a, 10);
var b = parseInt(req.params.b, 10);
var format = req.params.format;
var a = parseInt(req.params.a, 10)
, b = parseInt(req.params.b, 10)
, format = req.params.format;
obj.range(req, res, a, b, format);
});
this.get(path + '/:id', obj.show);
this.del(path + '/:id', obj.destroy);
this.delete(path + '/:id', function(req, res){
var id = parseInt(req.params.id, 10);
obj.destroy(req, res, id);
});
};
// Fake records
@@ -40,8 +44,7 @@ var User = {
show: function(req, res){
res.send(users[req.params.id] || { error: 'Cannot find user' });
},
destroy: function(req, res){
var id = req.params.id;
destroy: function(req, res, id){
var destroyed = id in users;
delete users[id];
res.send(destroyed ? 'destroyed' : 'Cannot find user');
@@ -81,10 +84,11 @@ app.get('/', function(req, res){
, '<li>GET /users/1..3.json</li>'
, '<li>DELETE /users/4</li>'
, '</ul>'
].join('\n'));
].join('\n'));
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
}

View File

@@ -1,12 +1,7 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var verbose = process.env.NODE_ENV != 'test';
var app = module.exports = express();
var express = require('../../lib/express')
, verbose = process.env.NODE_ENV != 'test'
, app = module.exports = express();
app.map = function(a, route){
route = route || '';
@@ -34,7 +29,7 @@ var users = {
res.send('user ' + req.params.uid);
},
del: function(req, res){
delete: function(req, res){
res.send('delete users');
}
};
@@ -44,7 +39,7 @@ var pets = {
res.send('user ' + req.params.uid + '\'s pets');
},
del: function(req, res){
delete: function(req, res){
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
}
};
@@ -52,17 +47,21 @@ var pets = {
app.map({
'/users': {
get: users.list,
del: users.del,
delete: users.delete,
'/:uid': {
get: users.get,
'/pets': {
get: pets.list,
'/:pid': {
del: pets.del
delete: pets.delete
}
}
}
}
});
app.listen(3000);
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -77,9 +77,12 @@ app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
res.send('Editing user ' + req.user.name);
});
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
res.send('Deleted user ' + req.user.name);
});
app.listen(3000);
console.log('Express app started on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,23 +1,22 @@
/**
* Module dependencies.
*/
var express = require('../..');
var app = express();
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var site = require('./site');
var post = require('./post');
var user = require('./user');
var express = require('../..')
, app = express()
, site = require('./site')
, post = require('./post')
, user = require('./user');
// Config
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
app.use(logger('dev'));
app.use(cookieParser());
app.use(bodyParser());
app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(__dirname + '/public'));
// General
@@ -37,5 +36,8 @@ app.put('/user/:id/edit', user.update);
app.get('/posts', post.list);
app.listen(3000);
console.log('Express app started on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,3 +1,4 @@
// Fake posts database
var posts = [

View File

@@ -1,3 +1,4 @@
exports.index = function(req, res){
res.render('index', { title: 'Route Separation Example' });
};

View File

@@ -1,3 +1,4 @@
// Fake user database
var users = [

View File

@@ -1,3 +1,4 @@
var search = document.querySelector('[type=search]');
var code = document.querySelector('pre');

View File

@@ -1,3 +1,4 @@
// first:
// $ npm install redis
// $ redis-server
@@ -6,10 +7,9 @@
* Module dependencies.
*/
var express = require('../..');
var redis = require('redis');
var db = redis.createClient();
var express = require('../..')
, redis = require('redis')
, db = redis.createClient();
// npm install redis
@@ -57,5 +57,8 @@ app.get('/client.js', function(req, res){
res.sendfile(__dirname + '/client.js');
});
app.listen(3000);
console.log('app listening on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,4 +1,4 @@
doctype
!!! 5
html
head
title Search example

View File

@@ -1,20 +1,16 @@
// first:
// $ npm install redis
// $ redis-server
var express = require('../..');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var app = express();
// Required by session() middleware
// pass the secret for signed cookies
// (required by session())
app.use(cookieParser('keyboard cat'));
app.use(express.logger('dev'));
// Populates req.session
app.use(session());
app.use(express.session({ secret: 'keyboard cat' }));
app.get('/', function(req, res){
var body = '';
@@ -27,5 +23,8 @@ app.get('/', function(req, res){
res.send(body + '<p>viewed <strong>' + req.session.views + '</strong> times.</p>');
});
app.listen(3000);
console.log('Express app started on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,27 +1,17 @@
/**
* Module dependencies.
*/
var express = require('../..');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var session = require('express-session');
// pass the express to the connect redis module
// allowing it to inherit from session.Store
var RedisStore = require('connect-redis')(session);
// allowing it to inherit from express.session.Store
var RedisStore = require('connect-redis')(express);
var app = express();
app.use(logger('dev'));
// Required by session() middleware
// pass the secret for signed cookies
// (required by session())
app.use(cookieParser('keyboard cat'));
app.use(express.logger('dev'));
// Populates req.session
app.use(session({ store: new RedisStore }));
app.use(express.session({ store: new RedisStore, secret: 'keyboard cat' }));
app.get('/', function(req, res){
var body = '';

View File

@@ -1,13 +1,9 @@
/**
* Module dependencies.
*/
var express = require('../..');
var logger = require('morgan');
var app = express();
// log requests
app.use(logger('dev'));
app.use(express.logger('dev'));
// express on its own has no notion
// of a "file". The express.static()
@@ -32,9 +28,17 @@ app.use('/static', express.static(__dirname + '/public'));
// this will allow "GET /style.css" instead of "GET /css/style.css":
app.use(express.static(__dirname + '/public/css'));
// this examples does not have any routes, however
// you may `app.use(app.router)` before or after these
// static() middleware. If placed before them your routes
// will be matched BEFORE file serving takes place. If placed
// after as shown here then file serving is performed BEFORE
// any routes are hit:
app.use(app.router);
app.listen(3000);
console.log('listening on port 3000');
console.log('try:');
console.log(' GET /hello.txt');
console.log(' GET /js/app.js');
console.log(' GET /css/style.css');
console.log(' GET /css/style.css');

View File

@@ -3,8 +3,6 @@
*/
var express = require('../..');
var logger = require('morgan');
var vhost = require('vhost');
/*
edit /etc/hosts:
@@ -18,10 +16,10 @@ edit /etc/hosts:
var main = express();
main.use(logger('dev'));
main.use(express.logger('dev'));
main.get('/', function(req, res){
res.send('Hello from main app!');
res.send('Hello from main app!')
});
main.get('/:sub', function(req, res){
@@ -41,8 +39,11 @@ redirect.all('*', function(req, res){
var app = express();
app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app
app.use(vhost('example.com', main)); // Serves top level domain via Main server app
app.use(express.vhost('*.example.com', redirect)) // Serves all subdomains via Redirect app
app.use(express.vhost('example.com', main)); // Serves top level domain via Main server app
app.listen(3000);
console.log('Express app started on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,10 +1,11 @@
/**
* Module dependencies.
*/
var http = require('http');
var path = require('path');
var extname = path.extname;
var http = require('http')
, path = require('path')
, extname = path.extname
/**
* Expose `GithubView`.
@@ -34,13 +35,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

@@ -1,11 +1,12 @@
/**
* Module dependencies.
*/
var express = require('../../');
var http = require('http');
var GithubView = require('./github-view');
var md = require('marked').parse;
var express = require('../../')
, http = require('http')
, GithubView = require('./github-view')
, md = require('marked').parse;
var app = module.exports = express();
@@ -15,15 +16,15 @@ app.engine('md', function(str, options, fn){
var html = md(str);
html = html.replace(/\{([^}]+)\}/g, function(_, name){
return options[name] || '';
});
})
fn(null, html);
} catch(err) {
fn(err);
}
});
})
// 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);
@@ -33,13 +34,14 @@ app.get('/', function(req, res){
// app.locals, res.locals, and locals passed
// work like they normally would
res.render('examples/markdown/views/index.md', { title: 'Example' });
});
})
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');
});
})
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1,10 +1,7 @@
/**
* Module dependencies.
*/
var express = require('../..');
var User = require('./user');
var app = express();
var express = require('../..')
, User = require('./user')
, app = express();
app.set('views', __dirname);
app.set('view engine', 'jade');
@@ -72,7 +69,7 @@ app.get('/middleware', count, users, function(req, res, next){
// this approach is much like the last
// however we're explicitly exposing
// the locals within each middleware
//
//
// note that this may not always work
// well, for example here we filter
// the users in the middleware, which
@@ -145,5 +142,8 @@ app.all('/api/*', function(req, res, next){
*/
app.listen(3000);
console.log('Application listening on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,3 +1,4 @@
module.exports = User;
// faux model

View File

@@ -1,3 +1,4 @@
/**
* Module dependencies.
*/
@@ -39,6 +40,29 @@ app.use('/api', function(req, res, next){
next();
});
// position our routes above the error handling middleware,
// and below our API middleware, since we want the API validation
// to take place BEFORE our routes
app.use(app.router);
// middleware with an arity of 4 are considered
// error handling middleware. When you next(err)
// it will be passed through the defined middleware
// in order, but ONLY those with an arity of 4, ignoring
// regular middleware.
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 });
});
// 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" });
});
// map of valid api keys, typically mapped to
// account info with some sort of database like redis.
// api keys do _not_ serve as authentication, merely to
@@ -49,7 +73,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' }
];
@@ -78,32 +102,15 @@ app.get('/api/repos', function(req, res, next){
});
app.get('/api/user/:name/repos', function(req, res, next){
var name = req.params.name;
var user = userRepos[name];
var name = req.params.name
, user = userRepos[name];
if (user) res.send(user);
else next();
});
// middleware with an arity of 4 are considered
// error handling middleware. When you next(err)
// it will be passed through the defined middleware
// in order, but ONLY those with an arity of 4, ignoring
// regular middleware.
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 });
});
// 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" });
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express server listening on port 3000');
console.log('Express started on port 3000');
}

View File

@@ -1,4 +1,2 @@
module.exports = process.env.EXPRESS_COV
? require('./lib-cov/express')
: require('./lib/express');
module.exports = require('./lib/express');

View File

@@ -2,15 +2,18 @@
* Module dependencies.
*/
var mixin = require('utils-merge');
var escapeHtml = require('escape-html');
var Router = require('./router');
var methods = require('methods');
var middleware = require('./middleware/init');
var query = require('./middleware/query');
var debug = require('debug')('express:application');
var View = require('./view');
var http = require('http');
var connect = require('connect')
, Router = require('./router')
, methods = require('methods')
, middleware = require('./middleware')
, debug = require('debug')('express:application')
, locals = require('./utils').locals
, compileETag = require('./utils').compileETag
, compileTrust = require('./utils').compileTrust
, View = require('./view')
, http = require('http');
var deprecate = require('depd')('express');
var merge = require('utils-merge');
/**
* Application prototype.
@@ -44,13 +47,18 @@ app.init = function(){
app.defaultConfiguration = function(){
// default settings
this.enable('x-powered-by');
this.enable('etag');
this.set('etag', 'weak');
var env = process.env.NODE_ENV || 'development';
this.set('env', env);
this.set('subdomain offset', 2);
this.set('trust proxy', false);
debug('booting in %s mode', env);
// implicit middleware
this.use(connect.query());
this.use(middleware.init(this));
// inherit protos
this.on('mount', function(parent){
this.request.__proto__ = parent.request;
@@ -59,11 +67,18 @@ app.defaultConfiguration = function(){
this.settings.__proto__ = parent.settings;
});
// setup locals
this.locals = Object.create(null);
// router
this._router = new Router(this);
this.routes = this._router.map;
this.__defineGetter__('router', function(){
this._usedRouter = true;
this._router.caseSensitive = this.enabled('case sensitive routing');
this._router.strict = this.enabled('strict routing');
return this._router.middleware;
});
// top-most app is mounted at /
this.mountpath = '/';
// setup locals
this.locals = locals(this);
// default locals
this.locals.settings = this.settings;
@@ -73,94 +88,18 @@ app.defaultConfiguration = function(){
this.set('views', process.cwd() + '/views');
this.set('jsonp callback name', 'callback');
if (env === 'development') {
this.set('json spaces', 2);
}
if (env === 'production') {
this.enable('view cache');
}
Object.defineProperty(this, 'router', {
get: function() {
throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
}
});
};
/**
* lazily adds the base router if it has not yet been added.
*
* We cannot add the base router in the defaultConfiguration because
* it reads app settings which might be set after that has run.
*
* @api private
*/
app.lazyrouter = function() {
if (!this._router) {
this._router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
this._router.use(query());
this._router.use(middleware.init(this));
}
};
/**
* Dispatch a req, res pair into the application. Starts pipeline processing.
*
* If no _done_ callback is provided, then default error handlers will respond
* in the event of an error bubbling through the stack.
*
* @api private
*/
app.handle = function(req, res, done) {
var env = this.get('env');
this._router.handle(req, res, function(err) {
if (done) {
return done(err);
}
// unhandled error
if (err) {
// default to 500
if (res.statusCode < 400) res.statusCode = 500;
debug('default %s', res.statusCode);
// respect err.status
if (err.status) res.statusCode = err.status;
// production gets a basic error message
var msg = 'production' == env
? http.STATUS_CODES[res.statusCode]
: err.stack || err.toString();
msg = escapeHtml(msg);
// log to stderr in a non-test env
if ('test' != env) console.error(err.stack || err.toString());
if (res.headersSent) return req.socket.destroy();
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', Buffer.byteLength(msg));
if ('HEAD' == req.method) return res.end();
res.end(msg);
return;
}
// 404
debug('default 404');
res.statusCode = 404;
res.setHeader('Content-Type', 'text/html');
if ('HEAD' == req.method) return res.end();
res.end('Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl) + '\n');
});
};
/**
* Proxy `Router#use()` to add middleware to the app router.
* See Router#use() documentation for details.
*
* If the _fn_ parameter is an express app, then it will be
* mounted at the _route_ specified.
* Proxy `connect#use()` to apply settings to
* mounted applications.
*
* @param {String|Function|Server} route
* @param {Function|Server} fn
@@ -169,21 +108,20 @@ app.handle = function(req, res, done) {
*/
app.use = function(route, fn){
var mount_app;
var app;
// default route to '/'
if ('string' != typeof route) fn = route, route = '/';
// express app
if (fn.handle && fn.set) mount_app = fn;
if (fn.handle && fn.set) app = fn;
// restore .app property on req and res
if (mount_app) {
debug('.use app under %s', route);
mount_app.mountpath = route;
if (app) {
app.route = route;
fn = function(req, res, next) {
var orig = req.app;
mount_app.handle(req, res, function(err) {
app.handle(req, res, function(err){
req.__proto__ = orig.request;
res.__proto__ = orig.response;
next(err);
@@ -191,33 +129,17 @@ app.use = function(route, fn){
};
}
this.lazyrouter();
this._router.use(route, fn);
connect.proto.use.call(this, route, fn);
// mounted an app
if (mount_app) {
mount_app.parent = this;
mount_app.emit('mount', this);
if (app) {
app.parent = this;
app.emit('mount', this);
}
return this;
};
/**
* Proxy to the app `Router#route()`
* Returns a new `Route` instance for the _path_.
*
* Routes are isolated middleware stacks for specific paths.
* See the Route api docs for details.
*
* @api public
*/
app.route = function(path){
this.lazyrouter();
return this._router.route(path);
};
/**
* Register the given template engine callback `fn`
* as `ext`.
@@ -260,10 +182,30 @@ app.engine = function(ext, fn){
};
/**
* Proxy to `Router#param()` with one added api feature. The _name_ parameter
* can be an array of names.
* Map the given param placeholder `name`(s) to the given callback(s).
*
* See the Router#param() docs for more details.
* Parameter mapping is used to provide pre-conditions to routes
* which use normalized placeholders. For example a _:user_id_ parameter
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
*
* app.param('user_id', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* next(err);
* } else if (user) {
* req.user = user;
* next();
* } else {
* next(new Error('failed to load user'));
* }
* });
* });
*
* @param {String|Array} name
* @param {Function} fn
@@ -272,17 +214,27 @@ app.engine = function(ext, fn){
*/
app.param = function(name, fn){
var self = this;
self.lazyrouter();
var self = this
, fns = [].slice.call(arguments, 1);
// array
if (Array.isArray(name)) {
name.forEach(function(key) {
self.param(key, fn);
name.forEach(function(name){
fns.forEach(function(fn){
self.param(name, fn);
});
});
// param logic
} else if ('function' == typeof name) {
this._router.param(name);
// single
} else {
if (':' == name[0]) name = name.substr(1);
fns.forEach(function(fn){
self._router.param(name, fn);
});
return this;
}
self._router.param(name, fn);
return this;
};
@@ -302,12 +254,27 @@ app.param = function(name, fn){
*/
app.set = function(setting, val){
if (1 == arguments.length) {
if (arguments.length === 1) {
// app.get(setting)
return this.settings[setting];
} else {
this.settings[setting] = val;
return this;
}
// set value
this.settings[setting] = val;
// trigger matched settings
switch (setting) {
case 'etag':
debug('compile etag %s', val);
this.set('etag fn', compileETag(val));
break;
case 'trust proxy':
debug('compile trust proxy %s', val);
this.set('trust proxy fn', compileTrust(val));
break;
}
return this;
};
/**
@@ -326,7 +293,7 @@ app.set = function(setting, val){
app.path = function(){
return this.parent
? this.parent.path() + this.mountpath
? this.parent.path() + this.route
: '';
};
@@ -392,6 +359,63 @@ app.disable = function(setting){
return this.set(setting, false);
};
/**
* Configure callback for zero or more envs,
* when no `env` is specified that callback will
* be invoked for all environments. Any combination
* can be used multiple times, in any order desired.
*
* Examples:
*
* app.configure(function(){
* // executed for all envs
* });
*
* app.configure('stage', function(){
* // executed staging env
* });
*
* app.configure('stage', 'production', function(){
* // executed for stage and production
* });
*
* Note:
*
* These callbacks are invoked immediately, and
* are effectively sugar for the following:
*
* var env = process.env.NODE_ENV || 'development';
*
* switch (env) {
* case 'development':
* ...
* break;
* case 'stage':
* ...
* break;
* case 'production':
* ...
* break;
* }
*
* @param {String} env...
* @param {Function} fn
* @return {app} for chaining
* @api public
*/
app.configure = function(env, fn){
var envs = 'all'
, args = [].slice.call(arguments);
fn = args.pop();
if (args.length) envs = args;
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
return this;
};
app.configure = deprecate.function(app.configure,
'app.configure: Check app.get(\'env\') in an if statement');
/**
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
*/
@@ -400,10 +424,11 @@ methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
this.lazyrouter();
// if no router attached yet, attach the router
if (!this._usedRouter) this.use(this.router);
var route = this._router.route(path);
route[method].apply(route, [].slice.call(arguments, 1));
// setup route
this._router[method].apply(this._router, arguments);
return this;
};
});
@@ -419,20 +444,16 @@ methods.forEach(function(method){
*/
app.all = function(path){
this.lazyrouter();
var route = this._router.route(path);
var args = [].slice.call(arguments, 1);
var args = arguments;
methods.forEach(function(method){
route[method].apply(route, args);
});
app[method].apply(this, args);
}, this);
return this;
};
// del -> delete alias
app.del = app.delete;
app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');
/**
* Render the given view `name` name with `options`
@@ -452,10 +473,10 @@ app.del = app.delete;
*/
app.render = function(name, options, fn){
var opts = {};
var cache = this.cache;
var engines = this.engines;
var view;
var opts = {}
, cache = this.cache
, engines = this.engines
, view;
// support callback function as second arg
if ('function' == typeof options) {
@@ -463,13 +484,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

View File

@@ -2,13 +2,15 @@
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter;
var mixin = require('utils-merge');
var proto = require('./application');
var Route = require('./router/route');
var Router = require('./router');
var req = require('./request');
var res = require('./response');
var deprecate = require('depd')('express');
var mixin = require('merge-descriptors');
var merge = require('utils-merge');
var connect = require('connect')
, proto = require('./application')
, Route = require('./router/route')
, Router = require('./router')
, req = require('./request')
, res = require('./response');
/**
* Expose `createApplication()`.
@@ -16,6 +18,12 @@ var res = require('./response');
exports = module.exports = createApplication;
/**
* Expose mime.
*/
exports.mime = connect.mime;
/**
* Create an express application.
*
@@ -24,19 +32,35 @@ exports = module.exports = createApplication;
*/
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, proto);
mixin(app, EventEmitter.prototype);
var app = connect();
merge(app, proto);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
/**
* Expose connect.middleware as express.*
* for example `express.logger` etc.
*/
mixin(exports, connect.middleware);
/**
* Deprecated createServer().
*/
exports.createServer = deprecate.function(createApplication,
'createServer() is deprecated\n' +
'express applications no longer inherit from http.Server\n' +
'please use:\n' +
'\n' +
' var express = require("express");\n' +
' var app = express();\n' +
'\n'
);
/**
* Expose the prototypes.
*/
@@ -52,42 +76,7 @@ exports.response = res;
exports.Route = Route;
exports.Router = Router;
/**
* Expose middleware
*/
// Error handler title
exports.query = require('./middleware/query');
exports.static = require('serve-static');
exports.errorHandler.title = 'Express';
/**
* Replace removed middleware with an appropriate error message.
*/
[
'json',
'urlencoded',
'bodyParser',
'compress',
'cookieSession',
'session',
'logger',
'cookieParser',
'favicon',
'responseTime',
'errorHandler',
'timeout',
'methodOverride',
'vhost',
'csrf',
'directory',
'limit',
'multipart',
'staticCache',
].forEach(function (name) {
Object.defineProperty(exports, name, {
get: function () {
throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
},
configurable: true
});
});

View File

@@ -1,3 +1,10 @@
/**
* Module dependencies.
*/
var utils = require('./utils');
/**
* Initialization middleware, exposing the
* request and response to eachother, as well
@@ -18,9 +25,8 @@ exports.init = function(app){
req.__proto__ = app.request;
res.__proto__ = app.response;
res.locals = res.locals || Object.create(null);
res.locals = res.locals || utils.locals(res);
next();
};
}
};

View File

@@ -1,39 +0,0 @@
/**
* Module dependencies.
*/
var qs = require('qs');
var parseUrl = require('parseurl');
/**
* Query:
*
* Automatically parse the query-string when available,
* populating the `req.query` object using
* [qs](https://github.com/visionmedia/node-querystring).
*
* Examples:
*
* .use(connect.query())
* .use(function(req, res){
* res.end(JSON.stringify(req.query));
* });
*
* The `options` passed are provided to qs.parse function.
*
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function query(options){
return function query(req, res, next){
if (!req.query) {
req.query = ~req.url.indexOf('?')
? qs.parse(parseUrl(req).query, options)
: {};
}
next();
};
};

View File

@@ -1,13 +1,19 @@
/**
* Module dependencies.
*/
var accepts = require('accepts');
var typeis = require('type-is');
var http = require('http');
var fresh = require('fresh');
var parseRange = require('range-parser');
var parse = require('parseurl');
var auth = require('basic-auth');
var deprecate = require('depd')('express');
var http = require('http')
, utils = require('./utils')
, connect = require('connect')
, fresh = require('fresh')
, parseRange = require('range-parser')
, parse = require('parseurl')
, proxyaddr = require('proxy-addr')
, mime = connect.mime;
var isIP = require('net').isIP;
/**
* Request prototype.
@@ -54,8 +60,6 @@ req.header = function(name){
};
/**
* To do: update docs.
*
* Check if the given `type(s)` is acceptable, returning
* the best match when true, otherwise `undefined`, in which
* case you should respond with 406 "Not Acceptable".
@@ -99,9 +103,9 @@ req.header = function(name){
* @api public
*/
req.accepts = function(){
var accept = accepts(this);
return accept.types.apply(accept, arguments);
req.accepts = function(type){
var args = arguments.length > 1 ? [].slice.apply(arguments) : type;
return utils.accepts(args, this.get('Accept'));
};
/**
@@ -112,15 +116,11 @@ req.accepts = function(){
* @api public
*/
req.acceptsEncoding = // backwards compatibility
req.acceptsEncodings = function(){
var accept = accepts(this);
return accept.encodings.apply(accept, arguments);
req.acceptsEncoding = function(encoding){
return !! ~this.acceptedEncodings.indexOf(encoding);
};
/**
* To do: update docs.
*
* Check if the given `charset` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
@@ -129,15 +129,14 @@ req.acceptsEncodings = function(){
* @api public
*/
req.acceptsCharset = // backwards compatibility
req.acceptsCharsets = function(){
var accept = accepts(this);
return accept.charsets.apply(accept, arguments);
req.acceptsCharset = function(charset){
var accepted = this.acceptedCharsets;
return accepted.length
? !! ~accepted.indexOf(charset)
: true;
};
/**
* To do: update docs.
*
* Check if the given `lang` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
@@ -146,10 +145,11 @@ req.acceptsCharsets = function(){
* @api public
*/
req.acceptsLanguage = // backwards compatibility
req.acceptsLanguages = function(){
var accept = accepts(this);
return accept.languages.apply(accept, arguments);
req.acceptsLanguage = function(lang){
var accepted = this.acceptedLanguages;
return accepted.length
? !! ~accepted.indexOf(lang)
: true;
};
/**
@@ -178,6 +178,98 @@ req.range = function(size){
return parseRange(size, range);
};
/**
* Return an array of encodings.
*
* Examples:
*
* ['gzip', 'deflate']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedEncodings', function(){
var accept = this.get('Accept-Encoding');
return accept
? accept.trim().split(/ *, */)
: [];
});
/**
* Return an array of Accepted media types
* ordered from highest quality to lowest.
*
* Examples:
*
* [ { value: 'application/json',
* quality: 1,
* type: 'application',
* subtype: 'json' },
* { value: 'text/html',
* quality: 0.5,
* type: 'text',
* subtype: 'html' } ]
*
* @return {Array}
* @api public
*/
req.__defineGetter__('accepted', function(){
var accept = this.get('Accept');
return accept
? utils.parseAccept(accept)
: [];
});
/**
* Return an array of Accepted languages
* ordered from highest quality to lowest.
*
* Examples:
*
* Accept-Language: en;q=.5, en-us
* ['en-us', 'en']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedLanguages', function(){
var accept = this.get('Accept-Language');
return accept
? utils
.parseParams(accept)
.map(function(obj){
return obj.value;
})
: [];
});
/**
* Return an array of Accepted charsets
* ordered from highest quality to lowest.
*
* Examples:
*
* Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8
* ['unicode-1-1', 'iso-8859-5']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedCharsets', function(){
var accept = this.get('Accept-Charset');
return accept
? utils
.parseParams(accept)
.map(function(obj){
return obj.value;
})
: [];
});
/**
* Return the value of param `name` when present or `defaultValue`.
*
@@ -187,7 +279,7 @@ req.range = function(size){
*
* To utilize request bodies, `req.body`
* should be an object. This can be done by using
* the `bodyParser()` middleware.
* the `connect.bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} [defaultValue]
@@ -231,28 +323,48 @@ req.param = function(name, defaultValue){
* @api public
*/
req.is = function(types){
if (!Array.isArray(types)) types = [].slice.call(arguments);
return typeis(this, types);
req.is = function(type){
var ct = this.get('Content-Type');
if (!ct) return false;
ct = ct.split(';')[0];
if (!~type.indexOf('/')) type = mime.lookup(type);
if (~type.indexOf('*')) {
type = type.split('/');
ct = ct.split('/');
if ('*' == type[0] && type[1] == ct[1]) return true;
if ('*' == type[1] && type[0] == ct[0]) return true;
return false;
}
return !! ~ct.indexOf(type);
};
/**
* Return the protocol string "http" or "https"
* when requested with TLS. When the "trust proxy"
* setting is enabled the "X-Forwarded-Proto" header
* field will be trusted. If you're running behind
* a reverse proxy that supplies https for you this
* may be enabled.
* setting trusts the socket address, the
* "X-Forwarded-Proto" header field will be trusted
* and used if present.
*
* If you're running behind a reverse proxy that
* supplies https for you this may be enabled.
*
* @return {String}
* @api public
*/
req.__defineGetter__('protocol', function(){
var trustProxy = this.app.get('trust proxy');
if (this.connection.encrypted) return 'https';
if (!trustProxy) return 'http';
var proto = this.get('X-Forwarded-Proto') || 'http';
var proto = this.connection.encrypted
? 'https'
: 'http';
var trust = this.app.get('trust proxy fn');
if (!trust(this.connection.remoteAddress)) {
return proto;
}
// Note: X-Forwarded-Proto is normally only ever a
// single value, but this is to be safe.
proto = this.get('X-Forwarded-Proto') || proto;
return proto.split(/\s*,\s*/)[0];
});
@@ -270,36 +382,59 @@ req.__defineGetter__('secure', function(){
});
/**
* Return the remote address, or when
* "trust proxy" is `true` return
* the upstream addr.
* Return the remote address from the trusted proxy.
*
* The is the remote address on the socket unless
* "trust proxy" is set.
*
* @return {String}
* @api public
*/
req.__defineGetter__('ip', function(){
return this.ips[0] || this.connection.remoteAddress;
var trust = this.app.get('trust proxy fn');
return proxyaddr(this, trust);
});
/**
* When "trust proxy" is `true`, parse
* the "X-Forwarded-For" ip address list.
* When "trust proxy" is set, trusted proxy addresses + client.
*
* For example if the value were "client, proxy1, proxy2"
* you would receive the array `["client", "proxy1", "proxy2"]`
* where "proxy2" is the furthest down-stream.
* where "proxy2" is the furthest down-stream and "proxy1" and
* "proxy2" were trusted.
*
* @return {Array}
* @api public
*/
req.__defineGetter__('ips', function(){
var trustProxy = this.app.get('trust proxy');
var val = this.get('X-Forwarded-For');
return trustProxy && val
? val.split(/ *, */)
: [];
var trust = this.app.get('trust proxy fn');
var addrs = proxyaddr.all(this, trust);
return addrs.slice(1).reverse();
});
/**
* Return basic auth credentials.
*
* Examples:
*
* // http://tobi:hello@example.com
* req.auth
* // => { username: 'tobi', password: 'hello' }
*
* @return {Object} or undefined
* @api public
*/
req.__defineGetter__('auth', function(){
deprecate('req.auth: Use basic-auth npm module instead');
// credentials
var creds = auth(this);
if (!creds) return;
return { username: creds.name, password: creds.pass };
});
/**
@@ -318,11 +453,16 @@ req.__defineGetter__('ips', function(){
*/
req.__defineGetter__('subdomains', function(){
var host = this.host;
if (!host) return [];
var offset = this.app.get('subdomain offset');
return (this.host || '')
.split('.')
.reverse()
.slice(offset);
var subdomains = !isIP(host)
? host.split('.').reverse()
: [host];
return subdomains.slice(offset);
});
/**
@@ -339,19 +479,30 @@ req.__defineGetter__('path', function(){
/**
* Parse the "Host" header field hostname.
*
* When the "trust proxy" setting trusts the socket
* address, the "X-Forwarded-Host" header field will
* be trusted.
*
* @return {String}
* @api public
*/
req.__defineGetter__('host', function(){
var trustProxy = this.app.get('trust proxy');
var host = trustProxy && this.get('X-Forwarded-Host');
host = host || this.get('Host');
var trust = this.app.get('trust proxy fn');
var host = this.get('X-Forwarded-Host');
if (!host || !trust(this.connection.remoteAddress)) {
host = this.get('Host');
}
if (!host) return;
// IPv6 literal support
var offset = host[0] === '['
? host.indexOf(']') + 1
: 0;
var index = host.indexOf(':', offset);
return ~index
? host.substring(0, index)
: host;
@@ -375,7 +526,7 @@ req.__defineGetter__('fresh', function(){
// 2xx or 304 as per rfc2616 14.26
if ((s >= 200 && s < 300) || 304 == s) {
return fresh(this.headers, this.res._headers);
return fresh(this.headers, (this.res._headers || {}));
}
return false;

View File

@@ -2,21 +2,26 @@
* Module dependencies.
*/
var http = require('http');
var path = require('path');
var mixin = require('utils-merge');
var contentDisposition = require('content-disposition');
var deprecate = require('depd')('express');
var escapeHtml = require('escape-html');
var sign = require('cookie-signature').sign;
var normalizeType = require('./utils').normalizeType;
var normalizeTypes = require('./utils').normalizeTypes;
var contentDisposition = require('./utils').contentDisposition;
var etag = require('./utils').etag;
var statusCodes = http.STATUS_CODES;
var cookie = require('cookie');
var send = require('send');
var basename = path.basename;
var extname = path.extname;
var mime = send.mime;
var merge = require('utils-merge');
var parseUrl = require('parseurl');
var vary = require('vary');
var http = require('http')
, path = require('path')
, connect = require('connect')
, sign = require('cookie-signature').sign
, normalizeType = require('./utils').normalizeType
, normalizeTypes = require('./utils').normalizeTypes
, setCharset = require('./utils').setCharset
, statusCodes = http.STATUS_CODES
, cookie = require('cookie')
, send = require('send')
, mime = connect.mime
, resolve = require('url').resolve
, basename = path.basename
, extname = path.extname;
/**
* Response prototype.
@@ -82,6 +87,8 @@ res.links = function(links){
res.send = function(body){
var req = this.req;
var head = 'HEAD' == req.method;
var type;
var encoding;
var len;
// settings
@@ -98,18 +105,24 @@ res.send = function(body){
}
}
// disambiguate res.send(status) and res.send(status, num)
if (typeof body === 'number' && arguments.length === 1) {
// res.send(status) will set status message as text string
this.get('Content-Type') || this.type('txt');
this.statusCode = body;
body = http.STATUS_CODES[body];
}
switch (typeof body) {
// response status
case 'number':
this.get('Content-Type') || this.type('txt');
this.statusCode = body;
body = http.STATUS_CODES[body];
break;
// string defaulting to html
case 'string':
if (!this.get('Content-Type')) this.type('html');
if (!this.get('Content-Type')) {
this.charset = this.charset || 'utf-8';
this.type('html');
}
break;
case 'boolean':
case 'number':
case 'object':
if (null == body) {
body = '';
@@ -121,18 +134,31 @@ res.send = function(body){
break;
}
// write strings in utf-8
if ('string' === typeof body) {
encoding = 'utf8';
type = this.get('Content-Type');
// reflect this in content-type
if ('string' === typeof type) {
this.set('Content-Type', setCharset(type, 'utf-8'));
}
}
// populate Content-Length
if (undefined !== body && !this.get('Content-Length')) {
this.set('Content-Length', len = Buffer.isBuffer(body)
len = Buffer.isBuffer(body)
? body.length
: Buffer.byteLength(body));
: Buffer.byteLength(body, encoding);
this.set('Content-Length', len);
}
// ETag support
// TODO: W/ support
if (app.settings.etag && len && 'GET' == req.method) {
var etag = len !== undefined && app.get('etag fn');
if (etag && ('GET' === req.method || 'HEAD' === req.method)) {
if (!this.get('ETag')) {
this.set('ETag', etag(body));
etag = etag(body, encoding);
etag && this.set('ETag', etag);
}
}
@@ -148,7 +174,8 @@ res.send = function(body){
}
// respond
this.end(head ? null : body);
this.end((head ? null : body), encoding);
return this;
};
@@ -174,6 +201,11 @@ res.json = function(obj){
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
if (typeof obj === 'number') {
deprecate('res.json(obj, status): Use res.json(status, obj) instead');
} else {
deprecate('res.json(num, status): Use res.status(status).json(num) instead');
}
} else {
this.statusCode = obj;
obj = arguments[1];
@@ -187,6 +219,7 @@ res.json = function(obj){
var body = JSON.stringify(obj, replacer, spaces);
// content-type
this.charset = this.charset || 'utf-8';
this.get('Content-Type') || this.set('Content-Type', 'application/json');
return this.send(body);
@@ -214,6 +247,11 @@ res.jsonp = function(obj){
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
if (typeof obj === 'number') {
deprecate('res.jsonp(obj, status): Use res.jsonp(status, obj) instead');
} else {
deprecate('res.jsonp(num, status): Use res.status(status).jsonp(num) instead');
}
} else {
this.statusCode = obj;
obj = arguments[1];
@@ -224,13 +262,15 @@ res.jsonp = function(obj){
var app = this.app;
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = JSON.stringify(obj, replacer, spaces)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
var body = JSON.stringify(obj, replacer, spaces);
var callback = this.req.query[app.get('jsonp callback name')];
// content-type
this.set('Content-Type', 'application/json');
if (!this.get('Content-Type')) {
this.charset = 'utf-8';
this.set('X-Content-Type-Options', 'nosniff');
this.set('Content-Type', 'application/json');
}
// fixup callback
if (Array.isArray(callback)) {
@@ -238,10 +278,22 @@ res.jsonp = function(obj){
}
// jsonp
if (callback && 'string' === typeof callback) {
if (typeof callback === 'string' && callback.length !== 0) {
this.charset = 'utf-8';
this.set('X-Content-Type-Options', 'nosniff');
this.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');';
// restrict callback charset
callback = callback.replace(/[^\[\]\w$.]/g, '');
// replace chars not allowed in JavaScript that are in JSON
body = body
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
// the typeof check is just to reduce client error noise
body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
}
return this.send(body);
@@ -258,9 +310,9 @@ res.jsonp = function(obj){
*
* Options:
*
* - `maxAge` defaulting to 0
* - `root` root directory for relative filenames
* - `hidden` serve hidden files, defaulting to false
* - `maxAge` defaulting to 0
* - `root` root directory for relative filenames
* - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
*
* Other options are passed along to `send`.
*
@@ -291,12 +343,11 @@ res.jsonp = function(obj){
*/
res.sendfile = function(path, options, fn){
options = options || {};
var self = this;
var req = self.req;
var next = this.req.next;
var done;
var self = this
, req = self.req
, next = this.req.next
, options = options || {}
, done;
// support function as second arg
if ('function' == typeof options) {
@@ -338,9 +389,6 @@ res.sendfile = function(path, options, fn){
req.socket.removeListener('error', error);
}
// Back-compat
options.maxage = options.maxage || options.maxAge || 0;
// transfer
var file = send(req, path, options);
file.on('error', error);
@@ -366,15 +414,17 @@ 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;
}
filename = filename || path;
this.set('Content-Disposition', contentDisposition(filename));
return this.sendfile(path, fn);
};
@@ -460,8 +510,8 @@ res.type = function(type){
*/
res.format = function(obj){
var req = this.req;
var next = req.next;
var req = this.req
, next = req.next;
var fn = obj.default;
if (fn) delete obj.default;
@@ -472,7 +522,10 @@ res.format = function(obj){
this.vary("Accept");
if (key) {
this.set('Content-Type', normalizeType(key).value);
var type = normalizeType(key).value;
var charset = mime.charsets.lookup(type);
if (charset) type += '; charset=' + charset;
this.set('Content-Type', type);
obj[key](req, this, next);
} else if (fn) {
fn();
@@ -494,9 +547,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;
};
@@ -523,10 +580,6 @@ res.header = function(field, val){
if (2 == arguments.length) {
if (Array.isArray(val)) val = val.map(String);
else val = String(val);
if ('content-type' == field.toLowerCase() && !/;\s*charset\s*=/.test(val)) {
var charset = mime.charsets.lookup(val.split(';')[0]);
if (charset) val += '; charset=' + charset.toLowerCase();
}
this.setHeader(field, val);
} else {
for (var key in field) {
@@ -560,7 +613,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);
};
@@ -588,10 +641,10 @@ 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');
if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
if ('number' == typeof val) val = val.toString();
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
if (signed) val = 's:' + sign(val, secret);
@@ -600,18 +653,7 @@ res.cookie = function(name, val, options){
options.maxAge /= 1000;
}
if (null == options.path) options.path = '/';
var headerVal = cookie.serialize(name, String(val), options);
// supports multiple 'res.cookie' calls by getting previous value
var prev = this.get('Set-Cookie');
if (prev) {
if (Array.isArray(prev)) {
headerVal = prev.concat(headerVal);
} else {
headerVal = [prev, headerVal];
}
}
this.set('Set-Cookie', headerVal);
this.set('Set-Cookie', cookie.serialize(name, String(val), options));
return this;
};
@@ -626,18 +668,47 @@ res.cookie = function(name, val, options){
*
* res.location('/foo/bar').;
* res.location('http://example.com');
* res.location('../login');
* res.location('../login'); // /blog/post/1 -> /blog/login
*
* Mounting:
*
* When an application is mounted and `res.location()`
* is given a path that does _not_ lead with "/" it becomes
* relative to the mount-point. For example if the application
* is mounted at "/blog", the following would become "/blog/login".
*
* res.location('login');
*
* While the leading slash would result in a location of "/login":
*
* res.location('/login');
*
* @param {String} url
* @api public
*/
res.location = function(url){
var req = this.req;
var app = this.app
, req = this.req
, path;
// "back" is an alias for the referrer
if ('back' == url) url = req.get('Referrer') || '/';
// relative
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
// relative to path
if ('.' == url[0]) {
path = parseUrl.original(req).pathname;
path = path + ('/' == path[path.length - 1] ? '' : '/');
url = resolve(path, url);
// relative to mount-point
} else if ('/' != url[0]) {
path = app.path();
url = path + '/' + url;
}
}
// Respond
this.set('Location', url);
return this;
@@ -656,18 +727,15 @@ res.location = function(url){
* res.redirect('/foo/bar');
* res.redirect('http://example.com');
* res.redirect(301, 'http://example.com');
* res.redirect('http://example.com', 301);
* res.redirect('../login'); // /blog/post/1 -> /blog/login
*
* @param {String} url
* @param {Number} code
* @api public
*/
res.redirect = function(url){
var head = 'HEAD' == this.req.method;
var status = 302;
var body;
var head = 'HEAD' == this.req.method
, status = 302
, body;
// allow status / url
if (2 == arguments.length) {
@@ -675,6 +743,7 @@ res.redirect = function(url){
status = url;
url = arguments[1];
} else {
deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
status = arguments[1];
}
}
@@ -715,31 +784,12 @@ res.redirect = function(url){
*/
res.vary = function(field){
var self = this;
// nothing
// checks for back-compat
if (!field) return this;
if (Array.isArray(field) && !field.length) return this;
// array
if (Array.isArray(field)) {
field.forEach(function(field){
self.vary(field);
});
return;
}
vary(this, field);
var vary = this.get('Vary');
// append
if (vary) {
vary = vary.split(/ *, */);
if (!~vary.indexOf(field)) vary.push(field);
this.set('Vary', vary.join(', '));
return this;
}
// set
this.set('Vary', field);
return this;
};
@@ -760,10 +810,10 @@ res.vary = function(field){
*/
res.render = function(view, options, fn){
options = options || {};
var self = this;
var req = this.req;
var app = req.app;
var self = this
, options = options || {}
, req = this.req
, app = req.app;
// support callback function as second arg
if ('function' == typeof options) {

View File

@@ -3,73 +3,47 @@
*/
var Route = require('./route');
var Layer = require('./layer');
var utils = require('../utils');
var methods = require('methods');
var debug = require('debug')('express:router');
var parseUrl = require('parseurl');
/**
* Expose `Router` constructor.
*/
exports = module.exports = Router;
/**
* Initialize a new `Router` with the given `options`.
*
* @param {Object} options
* @return {Router} which is an callable function
* @api public
* @api private
*/
var proto = module.exports = function(options) {
function Router(options) {
options = options || {};
function router(req, res, next) {
router.handle(req, res, next);
}
// mixin Router class functions
router.__proto__ = proto;
router.params = {};
router._params = [];
router.caseSensitive = options.caseSensitive;
router.strict = options.strict;
router.stack = [];
return router;
};
var self = this;
this.map = {};
this.params = {};
this._params = [];
this.caseSensitive = options.caseSensitive;
this.strict = options.strict;
this.middleware = function router(req, res, next){
self._dispatch(req, res, next);
};
}
/**
* Map the given param placeholder `name`(s) to the given callback.
* Register a param callback `fn` for the given `name`.
*
* Parameter mapping is used to provide pre-conditions to routes
* which use normalized placeholders. For example a _:user_id_ parameter
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
*
* Just like in middleware, you must either respond to the request or call next
* to avoid stalling the request.
*
* app.param('user_id', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* return next(err);
* } else if (!user) {
* return next(new Error('failed to load user'));
* }
* req.user = user;
* next();
* });
* });
*
* @param {String} name
* @param {String|Function} name
* @param {Function} fn
* @return {app} for chaining
* @return {Router} for chaining
* @api public
*/
proto.param = function(name, fn){
Router.prototype.param = function(name, fn){
// param logic
if ('function' == typeof name) {
this._params.push(name);
@@ -77,13 +51,9 @@ proto.param = function(name, fn){
}
// apply param functions
var params = this._params;
var len = params.length;
var ret;
if (name[0] === ':') {
name = name.substr(1);
}
var params = this._params
, len = params.length
, ret;
for (var i = 0; i < len; ++i) {
if (ret = params[i](name, fn)) {
@@ -102,284 +72,265 @@ proto.param = function(name, fn){
};
/**
* Dispatch a req, res into the router.
* Route dispatcher aka the route "middleware".
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @param {Function} next
* @api private
*/
proto.handle = function(req, res, done) {
var self = this;
Router.prototype._dispatch = function(req, res, next){
var params = this.params
, self = this;
debug('dispatching %s %s', req.method, req.url);
debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
var method = req.method.toLowerCase();
// route dispatch
(function pass(i, err){
var paramCallbacks
, paramIndex = 0
, paramVal
, route
, keys
, key;
var search = 1 + req.url.indexOf('?');
var pathlength = search ? search - 1 : req.url.length;
var fqdn = 1 + req.url.substr(0, pathlength).indexOf('://');
var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
var idx = 0;
var removed = '';
var slashAdded = false;
// match next route
function nextRoute(err) {
pass(req._route_index + 1, err);
}
// store options for OPTIONS request
// only used if OPTIONS request
// match route
req.route = route = self.matchRequest(req, i);
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res, next);
// no route
if (!route) return next(err);
debug('matched %s %s', route.method, route.path);
// we have a route
// start at param 0
req.params = route.params;
keys = route.keys;
i = 0;
// param callbacks
function param(err) {
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
try {
if ('route' == err) {
nextRoute();
} else if (err) {
i = 0;
callbacks(err);
} else if (paramCallbacks && undefined !== paramVal) {
paramCallback();
} else if (key) {
param();
} else {
i = 0;
callbacks();
}
} catch (err) {
param(err);
}
};
param(err);
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
if (err || !fn) return param(err);
fn(req, res, paramCallback, paramVal, key.name);
}
// invoke route callbacks
function callbacks(err) {
var fn = route.callbacks[i++];
try {
if ('route' == err) {
nextRoute();
} else if (err && fn) {
if (fn.length < 4) return callbacks(err);
fn(err, req, res, callbacks);
} else if (fn) {
if (fn.length < 4) return fn(req, res, callbacks);
callbacks();
} else {
nextRoute(err);
}
} catch (err) {
callbacks(err);
}
}
})(0);
};
/**
* Respond to __OPTIONS__ method.
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @api private
*/
Router.prototype._options = function(req, res, next){
var path = parseUrl(req).pathname
, body = this._optionsFor(path).join(',');
if (!body) return next();
res.set('Allow', body).send(body);
};
/**
* Return an array of HTTP verbs or "options" for `path`.
*
* @param {String} path
* @return {Array}
* @api private
*/
Router.prototype._optionsFor = function _optionsFor(path) {
var options = [];
// middleware and routes
var stack = self.stack;
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
// for options requests, respond with a default if nothing else responds
if (method === 'options') {
var old = done;
done = function(err) {
if (err || options.length === 0) return old(err);
if (method === 'options') continue;
var body = options.join(',');
return res.set('Allow', body).send(body);
};
var routes = this.map[method];
// HEAD methods include GET routes
if (!routes && method === 'head') {
routes = this.map.get;
}
if (!routes) continue;
for (var j = 0; j < routes.length; j++) {
if (routes[j].match(path)) {
options.push(method.toUpperCase());
break;
}
}
}
(function next(err) {
if (err === 'route') {
err = undefined;
}
var layer = stack[idx++];
if (!layer) {
return done(err);
}
if (slashAdded) {
req.url = req.url.substr(1);
slashAdded = false;
}
req.url = protohost + removed + req.url.substr(protohost.length);
req.originalUrl = req.originalUrl || req.url;
removed = '';
try {
var path = parseUrl(req).pathname;
if (undefined == path) path = '/';
if (!layer.match(path)) return next(err);
// route object and not middleware
var route = layer.route;
// if final route, then we support options
if (route) {
// we don't run any routes with error first
if (err) {
return next(err);
}
req.route = route;
// we can now dispatch to the route
if (method === 'options' && !route.methods['options']) {
options.push.apply(options, route._options());
}
}
req.params = layer.params;
// this should be done for the layer
return self.process_params(layer, req, res, function(err) {
if (err) {
return next(err);
}
if (route) {
return layer.handle(req, res, next);
}
trim_prefix();
});
} catch (err) {
next(err);
}
function trim_prefix() {
var c = path[layer.path.length];
if (c && '/' != c && '.' != c) return next(err);
// Trim off the part of the url that matches the route
// middleware (.use stuff) needs to have the path stripped
debug('trim prefix (%s) from url %s', removed, req.url);
removed = layer.path;
req.url = protohost + req.url.substr(protohost.length + removed.length);
// Ensure leading slash
if (!fqdn && '/' != req.url[0]) {
req.url = '/' + req.url;
slashAdded = true;
}
debug('%s %s : %s', layer.handle.name || 'anonymous', layer.path, req.originalUrl);
var arity = layer.handle.length;
if (err) {
if (arity === 4) {
layer.handle(err, req, res, next);
} else {
next(err);
}
} else if (arity < 4) {
layer.handle(req, res, next);
} else {
next(err);
}
}
})();
return options.sort();
};
/**
* Process any parameters for the route.
* Attempt to match a route for `req`
* with optional starting index of `i`
* defaulting to 0.
*
* @param {IncomingMessage} req
* @param {Number} i
* @return {Route}
* @api private
*/
proto.process_params = function(route, req, res, done) {
var params = this.params;
Router.prototype.matchRequest = function(req, i, head){
var method = req.method.toLowerCase()
, url = parseUrl(req)
, path = url.pathname
, routes = this.map
, i = i || 0
, route;
// captured parameters from the route, keys and values
var keys = route.keys;
// fast track
if (!keys || keys.length === 0) {
return done();
// HEAD support
if (!head && 'head' == method) {
route = this.matchRequest(req, i, true);
if (route) return route;
method = 'get';
}
var i = 0;
var paramIndex = 0;
var key;
var paramVal;
var paramCallbacks;
// routes for this method
if (routes = routes[method]) {
// process params in order
// param callbacks can be async
function param(err) {
if (err) {
return done(err);
}
if (i >= keys.length ) {
return done();
}
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
try {
if (paramCallbacks && undefined !== paramVal) {
return paramCallback();
} else if (key) {
return param();
// matching routes
for (var len = routes.length; i < len; ++i) {
route = routes[i];
if (route.match(path)) {
req._route_index = i;
return route;
}
} catch (err) {
return done(err);
}
done();
}
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
if (err || !fn) return param(err);
fn(req, res, paramCallback, paramVal, key.name);
}
param();
};
/**
* Use the given middleware function, with optional path, defaulting to "/".
* Attempt to match a route for `method`
* and `url` with optional starting
* index of `i` defaulting to 0.
*
* Use (like `.all`) will run for any http METHOD, but it will not add
* handlers for those methods so OPTIONS requests will not consider `.use`
* functions even if they could respond.
*
* The other difference is that _route_ path is stripped and not visible
* to the handler function. The main effect of this feature is that mounted
* handlers can operate without any code changes regardless of the "prefix"
* pathname.
*
* @param {String|Function} route
* @param {Function} fn
* @return {app} for chaining
* @api public
* @param {String} method
* @param {String} url
* @param {Number} i
* @return {Route}
* @api private
*/
proto.use = function(route, fn){
// default route to '/'
if ('string' != typeof route) {
fn = route;
route = '/';
}
Router.prototype.match = function(method, url, i, head){
var req = { method: method, url: url };
return this.matchRequest(req, i, head);
};
if (typeof fn !== 'function') {
/**
* Route `method`, `path`, and one or more callbacks.
*
* @param {String} method
* @param {String} path
* @param {Function} callback...
* @return {Router} for chaining
* @api private
*/
Router.prototype.route = function(method, path, callbacks){
var method = method.toLowerCase()
, callbacks = utils.flatten([].slice.call(arguments, 2));
// ensure path was given
if (!path) throw new Error('Router#' + method + '() requires a path');
// ensure all callbacks are functions
callbacks.forEach(function(fn){
if ('function' == typeof fn) return;
var type = {}.toString.call(fn);
var msg = 'Router.use() requires callback functions but got a ' + type;
var msg = '.' + method + '() requires callback functions but got a ' + type;
throw new Error(msg);
}
});
// strip trailing slash
if ('/' == route[route.length - 1]) {
route = route.slice(0, -1);
}
var layer = new Layer(route, {
// create the route
debug('defined %s %s', method, path);
var route = new Route(method, path, callbacks, {
sensitive: this.caseSensitive,
strict: this.strict,
end: false
}, fn);
strict: this.strict
});
// add the middleware
debug('use %s %s', route || '/', fn.name || 'anonymous');
this.stack.push(layer);
// add it
(this.map[method] = this.map[method] || []).push(route);
return this;
};
/**
* Create a new Route for the given path.
*
* Each route contains a separate middleware stack and VERB handlers.
*
* See the Route api documentation for details on adding handlers
* and middleware to routes.
*
* @param {String} path
* @return {Route}
* @api public
*/
proto.route = function(path){
var route = new Route(path);
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
Router.prototype.all = function(path) {
var self = this;
var args = [].slice.call(arguments);
methods.forEach(function(method){
self.route.apply(self, [method].concat(args));
});
return this;
};
// create Router#VERB functions
methods.concat('all').forEach(function(method){
proto[method] = function(path){
var route = this.route(path)
route[method].apply(route, [].slice.call(arguments, 1));
methods.forEach(function(method){
Router.prototype[method] = function(path){
var args = [method].concat([].slice.call(arguments));
this.route.apply(this, args);
return this;
};
});

View File

@@ -1,67 +0,0 @@
/**
* Module dependencies.
*/
var pathRegexp = require('path-to-regexp');
var debug = require('debug')('express:router:layer');
/**
* Expose `Layer`.
*/
module.exports = Layer;
function Layer(path, options, fn) {
if (!(this instanceof Layer)) {
return new Layer(path, options, fn);
}
debug('new %s', path);
options = options || {};
this.regexp = pathRegexp(path, this.keys = [], options);
this.handle = fn;
}
/**
* Check if this route matches `path`, if so
* populate `.params`.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
Layer.prototype.match = function(path){
var keys = this.keys;
var params = this.params = {};
var m = this.regexp.exec(path);
var n = 0;
var key;
var val;
if (!m) return false;
this.path = m[0];
for (var i = 1, len = m.length; i < len; ++i) {
key = keys[i - 1];
try {
val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
: m[i];
} catch(e) {
var err = new Error("Failed to decode param '" + m[i] + "'");
err.status = 400;
throw err;
}
if (key) {
params[key.name] = val;
} else {
params[n++] = val;
}
}
return true;
};

View File

@@ -1,9 +1,8 @@
/**
* Module dependencies.
*/
var debug = require('debug')('express:router:route');
var methods = require('methods');
var utils = require('../utils');
/**
@@ -13,179 +12,67 @@ var utils = require('../utils');
module.exports = Route;
/**
* Initialize `Route` with the given `path`,
* Initialize `Route` with the given HTTP `method`, `path`,
* and an array of `callbacks` and `options`.
*
* Options:
*
* - `sensitive` enable case-sensitive routes
* - `strict` enable strict matching for trailing slashes
*
* @param {String} method
* @param {String} path
* @param {Array} callbacks
* @param {Object} options.
* @api private
*/
function Route(path) {
debug('new %s', path);
function Route(method, path, callbacks, options) {
options = options || {};
this.path = path;
this.stack = undefined;
// route handlers for various http methods
this.methods = {};
this.method = method;
this.callbacks = callbacks;
this.regexp = utils.pathRegexp(path
, this.keys = []
, options.sensitive
, options.strict);
}
/**
* @return {Array} supported HTTP methods
* @api private
*/
Route.prototype._options = function(){
return Object.keys(this.methods).map(function(method) {
return method.toUpperCase();
});
};
/**
* dispatch req, res into this route
* Check if this route matches `path`, if so
* populate `.params`.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
Route.prototype.dispatch = function(req, res, done){
var self = this;
var method = req.method.toLowerCase();
Route.prototype.match = function(path){
var keys = this.keys
, params = this.params = []
, m = this.regexp.exec(path);
if (method === 'head' && !this.methods['head']) {
method = 'get';
}
if (!m) return false;
req.route = self;
// single middleware route case
if (typeof this.stack === 'function') {
this.stack(req, res, done);
return;
}
var stack = self.stack;
if (!stack) {
return done();
}
var idx = 0;
(function next_layer(err) {
if (err && err === 'route') {
return done();
}
var layer = stack[idx++];
if (!layer) {
return done(err);
}
if (layer.method && layer.method !== method) {
return next_layer(err);
}
var arity = layer.handle.length;
if (err) {
if (arity < 4) {
return next_layer(err);
}
try {
layer.handle(err, req, res, next_layer);
} catch (err) {
next_layer(err);
}
return;
}
if (arity > 3) {
return next_layer();
}
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
try {
layer.handle(req, res, next_layer);
} catch (err) {
next_layer(err);
var val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
: m[i];
} catch(e) {
var err = new Error("Failed to decode param '" + m[i] + "'");
err.status = 400;
throw err;
}
})();
if (key) {
params[key.name] = val;
} else {
params.push(val);
}
}
return true;
};
/**
* Add a handler for all HTTP verbs to this route.
*
* Behaves just like middleware and can respond or call `next`
* to continue processing.
*
* You can use multiple `.all` call to add multiple handlers.
*
* function check_something(req, res, next){
* next();
* };
*
* function validate_user(req, res, next){
* next();
* };
*
* route
* .all(validate_user)
* .all(check_something)
* .get(function(req, res, next){
* res.send('hello world');
* });
*
* @param {function} handler
* @return {Route} for chaining
* @api public
*/
Route.prototype.all = function(){
var self = this;
var callbacks = utils.flatten([].slice.call(arguments));
callbacks.forEach(function(fn) {
if (typeof fn !== 'function') {
var type = {}.toString.call(fn);
var msg = 'Route.all() requires callback functions but got a ' + type;
throw new Error(msg);
}
if (!self.stack) {
self.stack = fn;
}
else if (typeof self.stack === 'function') {
self.stack = [{ handle: self.stack }, { handle: fn }];
}
else {
self.stack.push({ handle: fn });
}
});
return self;
};
methods.forEach(function(method){
Route.prototype[method] = function(){
var self = this;
var callbacks = utils.flatten([].slice.call(arguments));
callbacks.forEach(function(fn) {
if (typeof fn !== 'function') {
var type = {}.toString.call(fn);
var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
throw new Error(msg);
}
debug('%s %s', method, self.path);
if (!self.methods[method]) {
self.methods[method] = true;
}
if (!self.stack) {
self.stack = [];
}
else if (typeof self.stack === 'function') {
self.stack = [{ handle: self.stack }];
}
self.stack.push({ method: method, handle: fn });
});
return self;
};
});

View File

@@ -1,21 +1,70 @@
/**
* Module dependencies.
*/
var mime = require('send').mime;
var crc32 = require('buffer-crc32');
var basename = require('path').basename;
var etag = require('etag');
var mime = require('connect').mime;
var proxyaddr = require('proxy-addr');
var typer = require('media-typer');
/**
* Return ETag for `body`.
* toString ref.
*/
var toString = {}.toString;
/**
* Return strong ETag for `body`.
*
* @param {String|Buffer} body
* @param {String} [encoding]
* @return {String}
* @api private
*/
exports.etag = function(body){
return '"' + crc32.signed(body) + '"';
exports.etag = function (body, encoding) {
var buf = !Buffer.isBuffer(body)
? new Buffer(body, encoding)
: body;
return etag(buf, {weak: false});
};
/**
* Return weak ETag for `body`.
*
* @param {String|Buffer} body
* @param {String} [encoding]
* @return {String}
* @api private
*/
exports.wetag = function wetag(body, encoding){
var buf = !Buffer.isBuffer(body)
? new Buffer(body, encoding)
: body;
return etag(buf, {weak: true});
};
/**
* Make `locals()` bound to the given `obj`.
*
* This is used for `app.locals` and `res.locals`.
*
* @param {Object} obj
* @return {Function}
* @api private
*/
exports.locals = function(){
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
};
return locals;
};
/**
@@ -41,8 +90,8 @@ exports.isAbsolute = function(path){
*/
exports.flatten = function(arr, ret){
ret = ret || [];
var len = arr.length;
var ret = ret || []
, len = arr.length;
for (var i = 0; i < len; ++i) {
if (Array.isArray(arr[i])) {
exports.flatten(arr[i], ret);
@@ -86,25 +135,125 @@ exports.normalizeTypes = function(types){
};
/**
* Generate Content-Disposition header appropriate for the filename.
* non-ascii filenames are urlencoded and a filename* parameter is added
* Return the acceptable type in `types`, if any.
*
* @param {String} filename
* @param {Array} types
* @param {String} str
* @return {String}
* @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 + '"';
}
exports.acceptsArray = function(types, str){
// accept anything when Accept is not present
if (!str) return types[0];
return ret;
// parse
var accepted = exports.parseAccept(str)
, normalized = exports.normalizeTypes(types)
, len = accepted.length;
for (var i = 0; i < len; ++i) {
for (var j = 0, jlen = types.length; j < jlen; ++j) {
if (exports.accept(normalized[j], accepted[i])) {
return types[j];
}
}
}
};
/**
* Check if `type(s)` are acceptable based on
* the given `str`.
*
* @param {String|Array} type(s)
* @param {String} str
* @return {Boolean|String}
* @api private
*/
exports.accepts = function(type, str){
if ('string' == typeof type) type = type.split(/ *, */);
return exports.acceptsArray(type, str);
};
/**
* Check if `type` array is acceptable for `other`.
*
* @param {Object} type
* @param {Object} other
* @return {Boolean}
* @api private
*/
exports.accept = function(type, other){
var t = type.value.split('/');
return (t[0] == other.type || '*' == other.type)
&& (t[1] == other.subtype || '*' == other.subtype)
&& paramsEqual(type.params, other.params);
};
/**
* Check if accept params are equal.
*
* @param {Object} a
* @param {Object} b
* @return {Boolean}
* @api private
*/
function paramsEqual(a, b){
return !Object.keys(a).some(function(k) {
return a[k] != b[k];
});
}
/**
* Parse accept `str`, returning
* an array objects containing
* `.type` and `.subtype` along
* with the values provided by
* `parseQuality()`.
*
* @param {Type} name
* @return {Type}
* @api private
*/
exports.parseAccept = function(str){
return exports
.parseParams(str)
.map(function(obj){
var parts = obj.value.split('/');
obj.type = parts[0];
obj.subtype = parts[1];
return obj;
});
};
/**
* Parse quality `str`, returning an
* array of objects with `.value`,
* `.quality` and optional `.params`
*
* @param {String} str
* @return {Array}
* @api private
*/
exports.parseParams = function(str){
return str
.split(/ *, */)
.map(acceptParams)
.filter(function(obj){
return obj.quality;
})
.sort(function(a, b){
if (a.quality === b.quality) {
return a.originalIndex - b.originalIndex;
} else {
return b.quality - a.quality;
}
});
};
/**
@@ -132,3 +281,127 @@ function acceptParams(str, index) {
return ret;
}
/**
* Normalize the given path string,
* returning a regular expression.
*
* An empty array should be passed,
* which will contain the placeholder
* key names. For example "/user/:id" will
* then contain ["id"].
*
* @param {String|RegExp|Array} path
* @param {Array} keys
* @param {Boolean} sensitive
* @param {Boolean} strict
* @return {RegExp}
* @api private
*/
exports.pathRegexp = function(path, keys, sensitive, strict) {
if (toString.call(path) == '[object RegExp]') return path;
if (Array.isArray(path)) path = '(' + path.join('|') + ')';
path = path
.concat(strict ? '' : '/?')
.replace(/\/\(/g, '(?:/')
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){
keys.push({ name: key, optional: !! optional });
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ (optional || '')
+ (star ? '(/*)?' : '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
/**
* Compile "etag" value to function.
*
* @param {Boolean|String|Function} val
* @return {Function}
* @api private
*/
exports.compileETag = function(val) {
var fn;
if (typeof val === 'function') {
return val;
}
switch (val) {
case true:
fn = exports.wetag;
break;
case false:
break;
case 'strong':
fn = exports.etag;
break;
case 'weak':
fn = exports.wetag;
break;
default:
throw new TypeError('unknown value for etag function: ' + val);
}
return fn;
}
/**
* Compile "proxy trust" value to function.
*
* @param {Boolean|String|Number|Array|Function} val
* @return {Function}
* @api private
*/
exports.compileTrust = function(val) {
if (typeof val === 'function') return val;
if (val === true) {
// Support plain true/false
return function(){ return true };
}
if (typeof val === 'number') {
// Support trusting hop count
return function(a, i){ return i < val };
}
if (typeof val === 'string') {
// Support comma-separated values
val = val.split(/ *, */);
}
return proxyaddr.compile(val || []);
}
/**
* Set the charset in a given Content-Type string.
*
* @param {String} type
* @param {String} charset
* @return {String}
* @api private
*/
exports.setCharset = function(type, charset){
if (!type || !charset) return type;
// parse type
var parsed = typer.parse(type);
// set charset
parsed.parameters.charset = charset;
// format type
return typer.format(parsed);
};

View File

@@ -2,14 +2,14 @@
* Module dependencies.
*/
var path = require('path');
var fs = require('fs');
var utils = require('./utils');
var dirname = path.dirname;
var basename = path.basename;
var extname = path.extname;
var exists = fs.existsSync || path.existsSync;
var join = path.join;
var path = require('path')
, fs = require('fs')
, utils = require('./utils')
, dirname = path.dirname
, basename = path.basename
, extname = path.extname
, exists = fs.existsSync || path.existsSync
, join = path.join;
/**
* Expose `View`.

View File

@@ -1,71 +1,19 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "4.1.2",
"version": "3.19.2",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
},
{
"name": "Aaron Heckmann",
"email": "aaron.heckmann+github@gmail.com"
},
{
"name": "Ciaran Jessup",
"email": "ciaranj@gmail.com"
},
{
"name": "Guillermo Rauch",
"email": "rauchg@gmail.com"
},
{
"name": "Jonathan Ong",
"email": "me@jongleberry.com"
},
{
"name": "Roman Shtylman",
"email": "shtylman+expressjs@gmail.com"
}
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
"Ciaran Jessup <ciaranj@gmail.com>",
"Douglas Christopher Wilson <doug@somethingdoug.com>",
"Guillermo Rauch <rauchg@gmail.com>",
"Jonathan Ong <me@jongleberry.com>",
"Roman Shtylman <shtylman+expressjs@gmail.com"
],
"dependencies": {
"parseurl": "1.0.1",
"accepts": "1.0.1",
"type-is": "1.1.0",
"range-parser": "1.0.0",
"cookie": "0.1.2",
"buffer-crc32": "0.2.1",
"fresh": "0.2.2",
"methods": "0.1.0",
"send": "0.3.0",
"cookie-signature": "1.0.3",
"merge-descriptors": "0.0.2",
"utils-merge": "1.0.0",
"escape-html": "1.0.1",
"qs": "0.6.6",
"serve-static": "1.1.0",
"path-to-regexp": "0.1.2",
"debug": ">= 0.7.3 < 1"
},
"devDependencies": {
"mocha": "~1.18.2",
"body-parser": "1.0.2",
"connect-redis": "~2.0.0",
"ejs": "~1.0.0",
"express-session": "1.0.3",
"jade": "~0.35.0",
"marked": "0.3.2",
"multiparty": "~3.2.4",
"static-favicon": "1.0.2",
"hjs": "~0.0.6",
"should": "~3.3.1",
"supertest": "~0.11.0",
"method-override": "1.0.0",
"cookie-parser": "1.0.1",
"morgan": "1.0.0",
"vhost": "1.0.0"
},
"license": "MIT",
"repository": "strongloop/express",
"homepage": "http://expressjs.com/",
"keywords": [
"express",
"framework",
@@ -77,13 +25,57 @@
"app",
"api"
],
"repository": "git://github.com/visionmedia/express",
"scripts": {
"prepublish": "npm prune",
"test": "make test"
"dependencies": {
"basic-auth": "1.0.0",
"connect": "2.28.3",
"content-disposition": "0.5.0",
"commander": "2.6.0",
"cookie-signature": "1.0.5",
"debug": "~2.1.1",
"depd": "~1.0.0",
"escape-html": "1.0.1",
"etag": "~1.5.1",
"fresh": "0.2.4",
"media-typer": "0.3.0",
"methods": "~1.1.1",
"mkdirp": "0.5.0",
"parseurl": "~1.3.0",
"proxy-addr": "~1.0.6",
"range-parser": "~1.0.2",
"send": "0.11.1",
"utils-merge": "1.0.0",
"vary": "~1.0.0",
"cookie": "0.1.2",
"merge-descriptors": "0.0.2"
},
"devDependencies": {
"connect-redis": "~1.5.0",
"ejs": "2.1.4",
"istanbul": "0.3.5",
"marked": "0.3.3",
"mocha": "~2.1.0",
"should": "~4.6.2",
"supertest": "~0.15.0",
"hjs": "~0.0.6"
},
"engines": {
"node": ">= 0.10.0"
"node": ">= 0.8.0"
},
"license": "MIT"
"bin": {
"express": "./bin/express"
},
"files": [
"LICENSE",
"History.md",
"Readme.md",
"index.js",
"bin/",
"lib/"
],
"scripts": {
"test": "mocha --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-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

@@ -1,65 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../');
var app = express()
, blog = express()
, admin = express();
blog.use('/admin', admin);
app.use('/blog', blog);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.locals.self = true;
app.get('/render', function(req, res){
res.render('hello');
});
admin.get('/', function(req, res){
res.send('Hello World\n');
});
blog.get('/', function(req, res){
res.send('Hello World\n');
});
app.get('/', function(req, res){
res.send('Hello World\n');
});
app.get('/json', function(req, res){
res.send({ name: 'Tobi', role: 'admin' });
});
app.get('/json/:n', function(req, res){
var n = ~~req.params.n;
var arr = [];
var obj = { name: 'Tobi', role: 'admin' };
while (n--) arr.push(obj);
res.send(arr);
});
function foo(req, res, next) {
next();
}
app.get('/middleware', foo, foo, foo, foo, function(req, res){
res.send('Hello World\n');
});
var n = 100;
while (n--) {
app.get('/foo', foo, foo, function(req, res){
});
}
app.get('/match', function(req, res){
res.send('Hello World\n');
});
app.listen(8000);

View File

@@ -1 +0,0 @@
p Hello

View File

@@ -1,171 +0,0 @@
var express = require('../')
, Route = express.Route
, methods = require('methods')
, assert = require('assert');
describe('Route', function(){
describe('.all', function(){
it('should add handler', function(done){
var route = new Route('/foo');
route.all(function(req, res, next) {
assert.equal(req.a, 1);
assert.equal(res.b, 2);
next();
});
route.dispatch({ a:1, method: 'GET' }, { b:2 }, done);
})
it('should handle VERBS', function(done) {
var route = new Route('/foo');
var count = 0;
route.all(function(req, res, next) {
count++;
});
methods.forEach(function testMethod(method) {
route.dispatch({ method: method }, {});
});
assert.equal(count, methods.length);
done();
})
it('should stack', function(done) {
var route = new Route('/foo');
var count = 0;
route.all(function(req, res, next) {
count++;
next();
});
route.all(function(req, res, next) {
count++;
next();
});
route.dispatch({ method: 'GET' }, {}, function(err) {
assert.ifError(err);
count++;
});
assert.equal(count, 3);
done();
})
})
describe('.VERB', function(){
it('should support .get', function(done){
var route = new Route('');
var count = 0;
route.get(function(req, res, next) {
count++;
})
route.dispatch({ method: 'GET' }, {});
assert(count);
done();
})
it('should limit to just .VERB', function(done){
var route = new Route('');
route.get(function(req, res, next) {
assert(false);
done();
})
route.post(function(req, res, next) {
assert(true);
})
route.dispatch({ method: 'post' }, {});
done();
})
it('should allow fallthrough', function(done){
var route = new Route('');
var order = '';
route.get(function(req, res, next) {
order += 'a';
next();
})
route.all(function(req, res, next) {
order += 'b';
next();
});
route.get(function(req, res, next) {
order += 'c';
})
route.dispatch({ method: 'get' }, {});
assert.equal(order, 'abc');
done();
})
})
describe('errors', function(){
it('should handle errors via arity 4 functions', function(done){
var route = new Route('');
var order = '';
route.all(function(req, res, next){
next(new Error('foobar'));
});
route.all(function(req, res, next){
order += '0';
next();
});
route.all(function(err, req, res, next){
order += 'a';
next(err);
});
route.all(function(err, req, res, next){
assert.equal(err.message, 'foobar');
assert.equal(order, 'a');
done();
});
route.dispatch({ method: 'get' }, {});
})
it('should handle throw', function(done) {
var route = new Route('');
var order = '';
route.all(function(req, res, next){
throw new Error('foobar');
});
route.all(function(req, res, next){
order += '0';
next();
});
route.all(function(err, req, res, next){
order += 'a';
next(err);
});
route.all(function(err, req, res, next){
assert.equal(err.message, 'foobar');
assert.equal(order, 'a');
done();
});
route.dispatch({ method: 'get' }, {});
});
})
})

View File

@@ -1,187 +1,121 @@
var express = require('../')
, Router = express.Router
, request = require('supertest')
, methods = require('methods')
, assert = require('assert');
describe('Router', function(){
it('should return a function with router methods', function() {
var router = Router();
assert(typeof router == 'function');
var router, app;
var router = new Router();
assert(typeof router == 'function');
beforeEach(function(){
router = new Router;
app = express();
})
assert(typeof router.get == 'function');
assert(typeof router.handle == 'function');
assert(typeof router.use == 'function');
});
describe('.match(method, url, i)', function(){
it('should match based on index', function(){
router.route('get', '/foo', function(){});
router.route('get', '/foob?', function(){});
router.route('get', '/bar', function(){});
it('should support .use of other routers', function(done){
var router = new Router();
var another = new Router();
var method = 'GET';
var url = '/foo?bar=baz';
another.get('/bar', function(req, res){
res.end();
});
router.use('/foo', another);
var route = router.match(method, url, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
router.handle({ url: '/foo/bar', method: 'GET' }, { end: done });
});
var route = router.match(method, url, 1);
route.path.should.equal('/foob?');
it('should support dynamic routes', function(done){
var router = new Router();
var another = new Router();
var route = router.match(method, url, 2);
assert(!route);
another.get('/:bar', function(req, res){
req.params.bar.should.equal('route');
res.end();
});
router.use('/:foo', another);
url = '/bar';
var route = router.match(method, url);
route.path.should.equal('/bar');
})
})
describe('.matchRequest(req, i)', function(){
it('should match based on index', function(){
router.route('get', '/foo', function(){});
router.route('get', '/foob?', function(){});
router.route('get', '/bar', function(){});
var req = { method: 'GET', url: '/foo?bar=baz' };
router.handle({ url: '/test/route', method: 'GET' }, { end: done });
});
var route = router.matchRequest(req, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
describe('.handle', function(){
var route = router.matchRequest(req, 1);
req._route_index.should.equal(1);
route.path.should.equal('/foob?');
var route = router.matchRequest(req, 2);
assert(!route);
req.url = '/bar';
var route = router.matchRequest(req);
route.path.should.equal('/bar');
})
})
describe('.middleware', function(){
it('should dispatch', function(done){
var router = new Router();
router.route('/foo').get(function(req, res){
router.route('get', '/foo', function(req, res){
res.send('foo');
});
var res = {
send: function(val) {
val.should.equal('foo');
done();
}
}
router.handle({ url: '/foo', method: 'GET' }, res);
app.use(router.middleware);
request(app)
.get('/foo')
.expect('foo', done);
})
})
describe('.multiple callbacks', function(){
it('should throw if a callback is null', function(){
assert.throws(function () {
var router = new Router();
router.route('/foo').all(null);
router.route('get', '/foo', null, function(){});
})
})
it('should throw if a callback is undefined', function(){
assert.throws(function () {
var router = new Router();
router.route('/foo').all(undefined);
router.route('get', '/foo', undefined, function(){});
})
})
it('should throw if a callback is not a function', function(){
assert.throws(function () {
var router = new Router();
router.route('/foo').all('not a function');
router.route('get', '/foo', 'not a function', function(){});
})
})
it('should not throw if all callbacks are functions', function(){
var router = new Router();
router.route('/foo').all(function(){}).all(function(){});
router.route('get', '/foo', function(){}, function(){});
})
})
describe('error', function(){
it('should skip non error middleware', function(done){
var router = new Router();
router.get('/foo', function(req, res, next){
next(new Error('foo'));
});
router.get('/bar', function(req, res, next){
next(new Error('bar'));
});
router.use(function(req, res, next){
assert(false);
});
router.use(function(err, req, res, next){
assert.equal(err.message, 'foo');
done();
});
router.handle({ url: '/foo', method: 'GET' }, {}, done);
});
it('should handle throwing inside routes with params', function(done) {
var router = new Router();
router.get('/foo/:id', function(req, res, next){
throw new Error('foo');
});
router.use(function(req, res, next){
assert(false);
});
router.use(function(err, req, res, next){
assert.equal(err.message, 'foo');
done();
});
router.handle({ url: '/foo/2', method: 'GET' }, {}, done);
});
})
describe('.all', function() {
it('should support using .all to capture all http verbs', function(done){
it('should support using .all to capture all http verbs', function() {
var router = new Router();
var count = 0;
router.all('/foo', function(){ count++; });
router.all('/foo', function(){});
var url = '/foo?bar=baz';
methods.forEach(function testMethod(method) {
router.handle({ url: url, method: method }, {}, function() {});
var route = router.match(method, url);
route.constructor.name.should.equal('Route');
route.method.should.equal(method);
route.path.should.equal('/foo');
});
assert.equal(count, methods.length);
done();
})
})
describe('.param', function() {
it('should call param function when routing VERBS', function(done) {
var router = new Router();
router.param('id', function(req, res, next, id) {
assert.equal(id, '123');
next();
});
router.get('/foo/:id/bar', function(req, res, next) {
assert.equal(req.params.id, '123');
next();
});
router.handle({ url: '/foo/123/bar', method: 'get' }, {}, done);
});
it('should call param function when routing middleware', function(done) {
var router = new Router();
router.param('id', function(req, res, next, id) {
assert.equal(id, '123');
next();
});
router.use('/foo/:id/bar', function(req, res, next) {
assert.equal(req.params.id, '123');
assert.equal(req.url, '/baz');
next();
});
router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done);
});
});
})

View File

@@ -1,13 +1,5 @@
var app = require('../../examples/auth/app')
, request = require('supertest');
function redirects(to, fn){
return function(err, res){
res.statusCode.should.equal(302)
res.headers.should.have.property('location').match(to);
fn()
}
}
var request = require('supertest')
function getCookie(res) {
return res.headers['set-cookie'][0].split(';')[0];
@@ -18,25 +10,93 @@ describe('auth', function(){
it('should redirect to /login', function(done){
request(app)
.get('/')
.end(redirects(/login$/, done))
.expect('Location', '/login')
.expect(302, done)
})
})
describe('GET /restricted (w/o cookie)',function(){
it('should redirect to /login', function(done){
describe('GET /login',function(){
it('should render login form', function(done){
request(app)
.get('/restricted')
.end(redirects(/login$/,done))
.get('/login')
.expect(200, /<form/, done)
})
})
describe('POST /login', function(){
it('should fail without proper credentials', function(done){
it('should display login error', function(done){
request(app)
.post('/login')
.type('urlencoded')
.send('username=not-tj&password=foobar')
.end(redirects(/login$/, done))
.expect('Location', '/login')
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/login')
.set('Cookie', getCookie(res))
.expect(200, /Authentication failed/, done)
})
})
})
})
describe('GET /logout',function(){
it('should redirect to /', function(done){
request(app)
.get('/logout')
.expect('Location', '/')
.expect(302, done)
})
})
describe('GET /restricted',function(){
it('should redirect to /login without cookie', function(done){
request(app)
.get('/restricted')
.expect('Location', '/login')
.expect(302, done)
})
it('should succeed with proper cookie', function(done){
request(app)
.post('/login')
.type('urlencoded')
.send('username=tj&password=foobar')
.expect('Location', '/')
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/restricted')
.set('Cookie', getCookie(res))
.expect(200, done)
})
})
})
describe('POST /login', function(){
it('should fail without proper username', function(done){
request(app)
.post('/login')
.type('urlencoded')
.send('username=not-tj&password=foobar')
.expect('Location', '/login')
.expect(302, done)
})
it('should fail without proper password', function(done){
request(app)
.post('/login')
.type('urlencoded')
.send('username=tj&password=baz')
.expect('Location', '/login')
.expect(302, done)
})
it('should succeed with proper credentials', function(done){
request(app)
.post('/login')
.type('urlencoded')
.send('username=tj&password=foobar')
.expect('Location', '/')
.expect(302, done)
})
})
})

View File

@@ -7,16 +7,43 @@ describe('content-negotiation', function(){
it('should default to text/html', function(done){
request(app)
.get('/')
.expect('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>')
.end(done);
.expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)
})
it('should accept to text/plain', function(done){
request(app)
.get('/')
.set('Accept', 'text/plain')
.expect(' - Tobi\n - Loki\n - Jane\n')
.end(done);
.expect(200, ' - Tobi\n - Loki\n - Jane\n', done)
})
it('should accept to application/json', function(done){
request(app)
.get('/')
.set('Accept', 'application/json')
.expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done)
})
})
})
describe('GET /users', function(){
it('should default to text/html', function(done){
request(app)
.get('/users')
.expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)
})
it('should accept to text/plain', function(done){
request(app)
.get('/users')
.set('Accept', 'text/plain')
.expect(200, ' - Tobi\n - Loki\n - Jane\n', done)
})
it('should accept to application/json', function(done){
request(app)
.get('/users')
.set('Accept', 'application/json')
.expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done)
})
})
})

View File

@@ -18,6 +18,35 @@ describe('cookies', function(){
done()
})
})
it('should respond to cookie', function(done){
request(app)
.post('/')
.send({ remember: 1 })
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/')
.set('Cookie', res.headers['set-cookie'][0])
.expect(200, /Remembered/, done)
})
})
})
describe('GET /forget', function(){
it('should clear cookie', function(done){
request(app)
.post('/')
.send({ remember: 1 })
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/forget')
.set('Cookie', res.headers['set-cookie'][0])
.expect('Set-Cookie', /remember=;/)
.expect(302, done)
})
})
})
describe('POST /', function(){
@@ -25,10 +54,20 @@ describe('cookies', function(){
request(app)
.post('/')
.send({ remember: 1 })
.end(function(err, res){
.expect(302, function(err, res){
res.headers.should.have.property('set-cookie')
done()
})
})
it('should no set cookie w/o reminder', function(done){
request(app)
.post('/')
.send({})
.expect(302, function(err, res){
res.headers.should.not.have.property('set-cookie')
done()
})
})
})
})
})

View File

@@ -7,14 +7,11 @@ describe('ejs', function(){
it('should respond with html', function(done){
request(app)
.get('/')
.end(function(err, res){
res.should.have.status(200);
res.should.have.header('Content-Type', 'text/html; charset=utf-8');
res.text.should.include('<li>tobi &lt;tobi@learnboost.com&gt;</li>');
res.text.should.include('<li>loki &lt;loki@learnboost.com&gt;</li>');
res.text.should.include('<li>jane &lt;jane@learnboost.com&gt;</li>');
done();
});
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<li>tobi &lt;tobi@learnboost\.com&gt;<\/li>/)
.expect(/<li>loki &lt;loki@learnboost\.com&gt;<\/li>/)
.expect(/<li>jane &lt;jane@learnboost\.com&gt;<\/li>/)
.expect(200, done)
})
})
})
})

View File

@@ -1,6 +1,6 @@
var app = require('../../examples/markdown')
, request = require('supertest');
var request = require('supertest')
describe('markdown', function(){
describe('GET /', function(){
@@ -18,4 +18,4 @@ describe('markdown', function(){
.expect(500,done)
})
})
})
})

View File

@@ -7,10 +7,39 @@ describe('mvc', function(){
it('should redirect to /users', function(done){
request(app)
.get('/')
.expect('Location', '/users')
.expect(302, done)
})
})
describe('GET /pet/0', function(){
it('should get pet', function(done){
request(app)
.get('/pet/0')
.expect(200, /Tobi/, done)
})
})
describe('GET /pet/0/edit', function(){
it('should get pet edit page', function(done){
request(app)
.get('/pet/0/edit')
.expect(/<form/)
.expect(200, /Tobi/, done)
})
})
describe('PUT /pet/2', function(){
it('should update the pet', function(done){
request(app)
.put('/pet/3')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send({ pet: { name: 'Boots' } })
.end(function(err, res){
res.should.have.status(302);
res.headers.location.should.include('/users');
done();
if (err) return done(err);
request(app)
.get('/pet/3/edit')
.expect(200, /Boots/, done)
})
})
})
@@ -19,13 +48,11 @@ describe('mvc', function(){
it('should display a list of users', function(done){
request(app)
.get('/users')
.end(function(err, res){
res.text.should.include('<h1>Users</h1>');
res.text.should.include('>TJ<');
res.text.should.include('>Guillermo<');
res.text.should.include('>Nathan<');
done();
})
.expect(/<h1>Users<\/h1>/)
.expect(/>TJ</)
.expect(/>Guillermo</)
.expect(/>Nathan</)
.expect(200, done)
})
})
@@ -34,21 +61,16 @@ describe('mvc', function(){
it('should display the user', function(done){
request(app)
.get('/user/0')
.end(function(err, res){
res.text.should.include('<h1>TJ <a href="/user/0/edit">edit');
done();
})
.expect(200, /<h1>TJ <a href="\/user\/0\/edit">edit/, done)
})
it('should display the users pets', function(done){
request(app)
.get('/user/0')
.end(function(err, res){
res.text.should.include('/pet/0">Tobi');
res.text.should.include('/pet/1">Loki');
res.text.should.include('/pet/2">Jane');
done();
})
.expect(/\/pet\/0">Tobi/)
.expect(/\/pet\/1">Loki/)
.expect(/\/pet\/2">Jane/)
.expect(200, done)
})
})
@@ -65,11 +87,8 @@ describe('mvc', function(){
it('should display the edit form', function(done){
request(app)
.get('/user/1/edit')
.end(function(err, res){
res.text.should.include('<h1>Guillermo</h1>');
res.text.should.include('value="put"');
done();
})
.expect(/Guillermo/)
.expect(200, /<form/, done)
})
})
@@ -77,15 +96,30 @@ describe('mvc', function(){
it('should update the user', function(done){
request(app)
.put('/user/1')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send({ user: { name: 'Tobo' }})
.end(function(err, res){
if (err) return done(err);
request(app)
.get('/user/1/edit')
.end(function(err, res){
res.text.should.include('<h1>Tobo</h1>');
done();
})
.expect(200, /Tobo/, done)
})
})
})
})
describe('POST /user/:id/pet', function(){
it('should create a pet for user', function(done){
request(app)
.post('/user/2/pet')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send({ pet: { name: 'Snickers' }})
.expect('Location', '/user/2')
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/user/2')
.expect(200, /Snickers/, done)
})
})
})
})

Some files were not shown because too many files have changed in this diff Show More