Compare commits

...

493 Commits
3.2.5 ... 4.4.5

Author SHA1 Message Date
Douglas Christopher Wilson
fd27f1f4e1 4.4.5 2014-06-26 20:26:43 -04:00
Douglas Christopher Wilson
f0ad557987 deps: cookie-signature@1.0.4 2014-06-26 20:25:20 -04:00
Douglas Christopher Wilson
f498b660da 4.4.4 2014-06-20 16:56:51 -04:00
刘星
e606d99dc8 Fix res.attachment Unicode filenames in Safari
closes #2188
2014-06-20 16:55:05 -04:00
Douglas Christopher Wilson
6aba1b4c49 deps: accepts@~1.0.5 2014-06-20 16:44:50 -04:00
Douglas Christopher Wilson
6ee9433f29 deps: update example dependencies 2014-06-20 16:43:04 -04:00
Douglas Christopher Wilson
ffe663aedf deps: buffer-crc32@0.2.3 2014-06-20 00:35:38 -04:00
Douglas Christopher Wilson
8258ce14f4 fix "trim prefix" debug message in express:router
closes #2177
2014-06-14 12:57:32 -04:00
Douglas Christopher Wilson
ac573cf830 4.4.3 2014-06-12 00:41:24 -04:00
Douglas Christopher Wilson
e799c0fb7b Merge tag '3.10.5' 2014-06-12 00:38:29 -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
c361a06bd4 deps: serve-static@1.2.3 2014-06-12 00:17:28 -04:00
Douglas Christopher Wilson
3428543bb8 deps: accepts@1.0.3 2014-06-12 00:16:05 -04:00
Douglas Christopher Wilson
9cdbc80522 deps: send@0.4.3 2014-06-12 00:14:36 -04:00
Douglas Christopher Wilson
6775658ed5 Fix persistence of req.params from app.params
fixes #2170
2014-06-11 18:15:09 -04:00
Douglas Christopher Wilson
7df7f7a575 deps: debug@1.0.2 2014-06-11 17:53:56 -04:00
Douglas Christopher Wilson
7daae1912b 4.4.2 2014-06-09 20:40:39 -04:00
Douglas Christopher Wilson
3205f68510 deps: update example dependencies 2014-06-09 20:39:34 -04:00
Douglas Christopher Wilson
898dcfac8b Merge tag '3.10.4' 2014-06-09 20:39:22 -04:00
Douglas Christopher Wilson
b1efa19f97 deps: update testing dependencies 2014-06-09 20:24:36 -04:00
Douglas Christopher Wilson
b45fd70f99 deps: send@0.4.2 2014-06-09 20:22:42 -04:00
Douglas Christopher Wilson
7f6c7a19c6 deps: serve-static@1.2.2 2014-06-09 20:21:31 -04:00
Douglas Christopher Wilson
142462d539 deps: debug@1.0.1 2014-06-09 20:20:50 -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
4d3e0d88a2 Fix catching errors from top-level handlers 2014-06-09 09:44:37 -04:00
Nick Heiner
253ce4837a Use path.resolve for views dir instead of concat
closes #2165
2014-06-08 17:41:36 -04:00
Joshua Goldberg
ad05eb8222 Fix typo in console.log in multipart example
closes #2164
2014-06-07 20:14:33 -04:00
Douglas Christopher Wilson
21393c244c tests: add more route tests 2014-06-06 11:12:52 -04:00
Douglas Christopher Wilson
4279e6ef45 improve before hook in mvc example 2014-06-06 10:42:29 -04:00
Douglas Christopher Wilson
3db6dd752f change confusing 404 handling in download example 2014-06-06 10:23:47 -04:00
Douglas Christopher Wilson
fcbe68eeb5 docs: move badges 2014-06-06 00:41:08 -04:00
Douglas Christopher Wilson
5019f38e29 tests: add more tests 2014-06-06 00:38:14 -04:00
Douglas Christopher Wilson
9bf1247716 Merge tag '3.10.3' 2014-06-05 23:45:31 -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
980a15d847 deps: type-is@1.2.1 2014-06-05 19:40:27 -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
bffb71d4c8 deps: proxy-addr@1.0.1 2014-06-03 17:25:20 -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
402ec83157 Merge tag '3.10.0' 2014-06-03 00:47:39 -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
f9ec70edd0 4.4.1 2014-06-02 21:13:50 -04:00
Douglas Christopher Wilson
9e5a758e7c deps: update example dependencies 2014-06-02 20:59:45 -04:00
Douglas Christopher Wilson
492e933796 deps: serve-static@1.2.1 2014-06-02 20:50:54 -04:00
Douglas Christopher Wilson
8ccd9d0eb5 deps: send@0.4.1 2014-06-02 20:49:11 -04:00
Douglas Christopher Wilson
16fdc11ccb deps: methods@1.0.1 2014-06-02 20:48:18 -04:00
Douglas Christopher Wilson
a7cd5a2553 deps: methods@1.0.1 2014-06-02 19:19:56 -04:00
Douglas Christopher Wilson
9e6b881f85 remove jsdoc params for polymorphic functions
until jsdoc has a way to actually document them

closes #2156
2014-06-02 10:26:15 -04:00
Douglas Christopher Wilson
95fa49147b 4.4.0 2014-05-31 00:00:39 -04:00
Douglas Christopher Wilson
f665c57c5c update serve-static to 1.2.0 2014-05-30 22:53:02 -04:00
Douglas Christopher Wilson
9024d24e81 deps: supertest@~0.13.0 2014-05-30 22:50:22 -04:00
Douglas Christopher Wilson
f92a7ad0a3 update accepts to 1.0.2 2014-05-30 22:48:32 -04:00
Douglas Christopher Wilson
db4448dda8 Merge tag '3.9.0' 2014-05-30 22:17:51 -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
Douglas Christopher Wilson
311e83e591 4.3.2 2014-05-29 00:17:21 -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
fb2d918056 fix handling of errors from param callbacks
fixes #2149
2014-05-28 22:26:05 -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
dfefea5e9d update example dependencies 2014-05-27 23:51:47 -04:00
Douglas Christopher Wilson
3fbab91231 Merge tag '3.8.1' 2014-05-27 23:49:47 -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
87e02c30e7 4.3.1 2014-05-23 19:11:28 -04:00
Douglas Christopher Wilson
c3470c9c96 tests: add route ordering test 2014-05-23 18:46:00 -04:00
Douglas Christopher Wilson
7f049164b7 Revert "fix behavior of multiple app.VERB for the same path"
This reverts commit 31b2e2d7b4.

fixes #2133
2014-05-23 18:35:20 -04:00
Douglas Christopher Wilson
4e12a72873 4.3.0 2014-05-21 02:13:03 -04:00
Douglas Christopher Wilson
91e0c27252 update example dependencies 2014-05-21 02:11:31 -04:00
Douglas Christopher Wilson
db4a061ed6 Merge tag '3.8.0' 2014-05-21 02:08:04 -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
e7e2592357 tests: add more app.param tests 2014-05-20 10:58:39 -04:00
Douglas Christopher Wilson
739586f96a add req.baseUrl to access stripped path in routes
fixes #2078
2014-05-19 00:39:26 -04:00
Douglas Christopher Wilson
4c0f1f53d3 update example dependencies 2014-05-18 23:07:59 -04:00
Douglas Christopher Wilson
359f12791a build: prevent failure from coveralls 2014-05-18 23:05:34 -04:00
Douglas Christopher Wilson
9354ab62dd build: prevent failure from coveralls 2014-05-18 23:02:25 -04:00
Douglas Christopher Wilson
23ff74bb3f tests: flow control with after 2014-05-18 16:33:11 -04:00
Douglas Christopher Wilson
98d17e2293 invoke router.param() only when necessary
fixes #2121
2014-05-18 16:21:01 -04:00
Douglas Christopher Wilson
ababa6ae5b fix issue routing requests among sub routers
fixes #2121
2014-05-18 15:27:28 -04:00
Douglas Christopher Wilson
097cd0c242 Merge tag '3.7.0' 2014-05-18 11:21:30 -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
31b2e2d7b4 fix behavior of multiple app.VERB for the same path
fixes #2116
2014-05-16 15:09:42 -04:00
Douglas Christopher Wilson
8fe8d74056 update type-is to 1.2.0 2014-05-14 00:25:39 -04:00
Douglas Christopher Wilson
fcc4742056 build: ignore Contributing 2014-05-14 00:22:47 -04:00
Douglas Christopher Wilson
d98e2e7498 deprecation messages are bright red on TTYs 2014-05-13 17:06:45 -04:00
Roman Shtylman
d37ffa1149 add Contributing.md
Hopefully this will guide some users when posting new issues. Feel free
to close any issues which don't follow the guidelines.
2014-05-12 15:45:54 -04:00
Douglas Christopher Wilson
cf709f3021 4.2.0 2014-05-11 22:00:30 -04:00
Douglas Christopher Wilson
7515ee6a78 deps: pin debug 2014-05-11 21:29:29 -04:00
Douglas Christopher Wilson
b8d6d258b0 update example dependencies 2014-05-11 21:25:42 -04:00
Douglas Christopher Wilson
35c50601bd Merge tag '3.6.0' 2014-05-09 17:33:26 -04:00
Douglas Christopher Wilson
f7be983a77 3.6.0 2014-05-09 17:03:23 -04:00
Douglas Christopher Wilson
bc9bcb0317 Fix req.next when inside router instance
fixes #2016
2014-05-09 16:53:15 -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
e4302b2120 tests: fixup new json tests 2014-05-08 23:22:18 -04:00
Douglas Christopher Wilson
3d6b4ba013 update example dependencies 2014-05-08 22:08:49 -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
555ffe37b2 update history with dep history 2014-05-08 14:57:44 -04:00
Douglas Christopher Wilson
165578a1da update history 2014-05-08 14:48:24 -04:00
Douglas Christopher Wilson
0bbbc84959 Merge branch '4.1.x' 2014-05-08 14:45:47 -04:00
Douglas Christopher Wilson
92d37671c5 4.1.2 2014-05-08 14:42:44 -04:00
Douglas Christopher Wilson
2901bd6916 Merge branch '3.5.x' into 4.1.x 2014-05-08 14:01:02 -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
dc31ea34b8 3.5.3 2014-05-08 13:51:26 -04:00
Douglas Christopher Wilson
d58ca520c8 Fix res.jsonp error if callback param is object
fixes #2104
2014-05-08 13:42:19 -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
c99fa6a192 Fix req.host for IPv6 literals
fixes #2102
2014-05-07 14:08:08 -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
Alberto Leal
be997fd654 Keep previous Content-Type for res.jsonp
closes #2094
2014-05-02 15:01:19 -04:00
Douglas Christopher Wilson
5c3852b91c tests: remove unused image fixture
closes #2091
2014-05-02 09:00:36 -04:00
Tiago Relvao
3c7310ebcb Include ETag in HEAD requests
fixes #2083
2014-04-30 09:34:10 -04:00
Douglas Christopher Wilson
61f2929a35 4.1.1 2014-04-27 19:49:05 -04:00
Douglas Christopher Wilson
c054581370 fix package.json to reflect supported node version
closes #2080
2014-04-27 19:46:53 -04:00
Douglas Christopher Wilson
dbea8312bb 4.1.0 2014-04-24 18:13:41 -04:00
Douglas Christopher Wilson
1536a2196e preserve casing of headers in res.header and res.set
fixes #2063
2014-04-24 18:00:43 -04:00
Douglas Christopher Wilson
374e6c3789 fix multipart example
closes #2061
2014-04-24 17:50:20 -04:00
Douglas Christopher Wilson
203fb05d3e update example dependencies 2014-04-24 17:05:20 -04:00
Douglas Christopher Wilson
ce8555c690 update should to 3.3.1 2014-04-24 16:51:49 -04:00
Douglas Christopher Wilson
9863fa0903 update serve-static to 1.1.0 2014-04-24 16:46:51 -04:00
Douglas Christopher Wilson
2fd3e72a19 Pass options from res.sendfile to send
fixes #2017
2014-04-24 16:44:06 -04:00
Douglas Christopher Wilson
8ccceacf91 Merge branch '3.x' 2014-04-24 16:43:51 -04:00
Douglas Christopher Wilson
d5815922ca 3.5.2 2014-04-24 16:36:37 -04:00
Douglas Christopher Wilson
972c01afc9 update supertest to 0.11.0 2014-04-24 16:30:58 -04:00
Douglas Christopher Wilson
8894e30869 Skip publishing benchmarks dir to npm 2014-04-24 16:27:00 -04:00
Douglas Christopher Wilson
5d8dba5fe0 update mocha to 1.18.2 2014-04-24 16:23:58 -04:00
Douglas Christopher Wilson
90fbc1a33e update cookie to 0.1.2 2014-04-24 16:21:22 -04:00
Douglas Christopher Wilson
55dea47b94 update mkdirp to 0.4.0 2014-04-24 16:20:31 -04:00
Douglas Christopher Wilson
c7791a207b update send to 0.3.0
fixes #2024
closes #2071
closes #2072
2014-04-24 15:50:55 -04:00
Douglas Christopher Wilson
30d18888b9 update connect to 2.14.5 2014-04-24 15:50:54 -04:00
Douglas Christopher Wilson
6ac6305b53 Merge branch '3.x' into HEAD 2014-04-24 15:42:41 -04:00
Roman Shtylman
29e8ccef4e Merge pull request #2067 from swrh/mvc-example-ejs-to-jade
MVC example: EJS -> Jade.
2014-04-23 10:21:26 -04:00
Fernando Silveira
ce17efd95b MVC example: EJS -> Jade. 2014-04-23 00:13:26 -03:00
Roman Shtylman
5480cb9571 Merge pull request #2060 from swrh/master
examples: Fix bugs in MVC example
2014-04-22 23:09:49 -04:00
Fernando Silveira
b38ffd7376 Fixing bug in MVC example pet view. 2014-04-22 23:30:17 -03:00
Fernando Silveira
8d8f44f352 Fix "method-override" dependency.
Depend on method-override@1.0.0 strictly, following the pattern for (almost) all other devDependencies.
2014-04-22 22:46:52 -03:00
Roman Shtylman
af5f21b2e2 Merge pull request #2065 from WebReflection/patch-1
trim_prefix causing --use-strict flag to fail
2014-04-22 20:23:58 -04:00
Andrea Giammarchi
4ac7474e4e trim_prefix causing --use-strict flag to fail
as specified in bug #2064 the `trim_prefix` function declaration within the `try/catch` causes problems when starting express with `--use_strict` directive.
2014-04-22 17:02:54 -07:00
Roman Shtylman
bdb3bb98f6 Merge pull request #2058 from thetalecrafter/content-disposition
support non-ascii filenames in content-disposition headers
2014-04-21 22:12:38 -04:00
Fernando Silveira
896609c859 Fixing bug when updating pet name in MVC example. 2014-04-21 12:10:21 -03:00
Fernando Silveira
50158b851c Express 4 now uses "method-override" external library. 2014-04-21 12:08:34 -03:00
Andy VanWagoner
56b672e657 support non-ascii filenames in content-disposition headers 2014-04-19 21:16:11 -06:00
Jonathan Ong
b9e9576083 Readme: add Roman as lead maintainer
so people start emailing him instead of TJ :D
2014-04-18 00:28:43 -07:00
Chris Andrejewski
79cc5a4d27 make old middleware properties configurable
closes #2054
2014-04-16 08:49:08 -04:00
Roman Shtylman
5f1d57704e Merge pull request #2053 from shawnzhu/fix-xhr-test
improve req.xhr test by verifying status code
2014-04-15 09:34:16 -04:00
Ke Zhu
19983272f3 improve req.xhr test by verifying status code 2014-04-15 00:11:20 -04:00
Roman Shtylman
e1ab302234 Merge pull request #2047 from Pana/patch-1
remove one feature description
2014-04-14 10:51:35 -04:00
Wang
6b1c443212 remove one feature description 2014-04-13 22:05:49 +08:00
Jonathan Ong
b79a271553 update type-is@1.1.0 2014-04-12 17:25:33 -07:00
Douglas Christopher Wilson
260141ee08 add soft testing on node.js 0.11 2014-04-10 17:04:19 -04:00
Douglas Christopher Wilson
fb1232043d update cookie to 0.1.1 2014-04-10 16:59:41 -04:00
Douglas Christopher Wilson
2377fc8bcf update serve-static to 1.0.4 2014-04-10 16:59:40 -04:00
Douglas Christopher Wilson
c610902b67 update accepts to 1.0.1 2014-04-10 16:59:38 -04:00
Douglas Christopher Wilson
a802405e19 update type-is to 1.0.1 2014-04-10 16:59:36 -04:00
Douglas Christopher Wilson
0dbaacfe12 update history 2014-04-10 16:57:00 -04:00
Roman Shtylman
b8dd60dec7 update readme to v4 release
- remove 3.0.0 from logo
- remove RC status for v4

close #2036
2014-04-10 09:57:30 -04:00
Roman Shtylman
147c2507c3 4.0.0 2014-04-09 16:38:40 -04:00
Roman Shtylman
d72f27909f handle thrown errors inside Route
close #2029
2014-04-08 14:50:26 -04:00
Roman Shtylman
1ee5329b1c Merge pull request #2025 from seanlinsley/patch-1
Use SVG badges in Readme
2014-04-05 17:08:09 -04:00
Sean Linsley
ba0b046a95 Use SVG badges in Readme 2014-04-05 15:35:46 -05:00
Jonathan Ong
412eb2a9ce Merge pull request #2018 from Devrama/patch-1
This example does not work with express 4.x.
2014-03-31 17:09:15 -07:00
WON JONG YOO
dd8e279cac Not work..
This example does not work with express 4.x.
bodyParser() does not have multipart() anymore.
Multiparty module and middleware are added.
2014-03-31 16:52:22 -04:00
Jonathan Ong
642432cb5e Merge pull request #2013 from kentcdodds/patch-1
Tell me what I'm using that isn't allowed
2014-03-28 22:57:03 -07:00
Kent C. Dodds
001c9380be Tell me what I'm using that isn't allowed
I'm migrating and it would be useful in a large application to know what I'm using that's not allowed. I think lots of people would feel this way. Let me know if you would prefer a different implementation.
2014-03-28 06:19:29 -06:00
Jonathan Ong
2e830fff99 Merge pull request #2008 from agchou/clean-up-code
some code clean up
2014-03-27 15:22:49 -07:00
agchou
06dcb22ae2 clean up code consistency 2014-03-27 09:15:27 -07:00
Jonathan Ong
0120874b8e Merge pull request #2004 from agchou/clean-up-code
some code clean up
2014-03-25 15:30:34 -07:00
agchou
13475977af some code cleanup 2014-03-25 15:23:04 -07:00
Roman Shtylman
df50669092 3.5.1 2014-03-25 16:56:54 -04:00
Roman Shtylman
abd6b7c5c3 pin down less-middleware
Things are broken in 0.2 version of less-middleware so use 0.1 line
2014-03-25 16:55:57 -04:00
Roman Shtylman
e36746363a 4.0.0-rc4 2014-03-24 22:53:35 -04:00
Roman Shtylman
1eba854f23 support arrays as middleware arguments to .VERB and .all
Express 3.x supported passing in arrays for sets of common middleware.
While there are better ways to do this, removing this feature causes
headache in upgrading for no real gain. We can support it without much
more code.
2014-03-23 21:29:59 -04:00
Roman Shtylman
6b19e3dc0a remove deprecation message about passing path as array 2014-03-23 21:07:37 -04:00
Roman Shtylman
cb1fbce46b don't call done twice for thrown errors inside parameterized routes
fixes #1995
2014-03-23 14:42:46 -04:00
Roman Shtylman
cc38cccae1 Merge pull request #1984 from blakeembrey/params-update
Override params each layer
2014-03-18 20:45:27 -04:00
Blake Embrey
efbe1779e3 Override params every layer. 2014-03-17 15:16:26 -03:00
Roman Shtylman
8e3d0a6569 remove license text from readme 2014-03-13 20:20:04 -04:00
Roman Shtylman
8bd5d54b0e update license year, tired of all the fluff issues about this 2014-03-13 16:27:34 -04:00
Roman Shtylman
4867cf1e7b Merge pull request #1964 from blakeembrey/modular-routing
keep route params from previous middleware matches
2014-03-13 12:29:48 -04:00
Roman Shtylman
3ea3250dbe examples: remove reference to app.router in static-files
closes #1974
2014-03-12 10:27:10 -04:00
Roman Shtylman
3bbcbfdcf9 4.0.0-rc3 2014-03-11 21:39:01 -04:00
Blake Embrey
c7e84d8044 Clean up code and syntax issues. 2014-03-10 13:19:03 -04:00
Blake Embrey
9ae1d0d22d Remove redundant .all function. 2014-03-10 13:18:23 -04:00
Blake Embrey
51e80ffd48 Add tests for dynamic mounting. 2014-03-10 13:16:39 -04:00
Blake Embrey
be52dbbaa1 Allow dynamic mounting with .use.
Retains params from the parent application.
2014-03-10 13:13:39 -04:00
Roman Shtylman
76147c78a1 change my contributor email to tag with expressjs 2014-03-09 22:49:33 -04:00
Jonathan Ong
59da745d6c add @jonathanong and @defunctzombie as contributors 2014-03-09 19:46:52 -07:00
Jonathan Ong
68996d7561 remove req.auth 2014-03-09 19:45:43 -07:00
Roman Shtylman
5e12bab5cc add better error messages for non-functions as middleware
Only functions are supported for [VERB](path, fn), use(path, fn) and
all(fn) calls in Router and Routes. This catches those errors earlier to
avoid checks during actual request processing.
2014-03-09 22:30:47 -04:00
Jonathan Ong
7693aa5464 use parseurl 2014-03-07 18:27:26 -08:00
Jonathan Ong
110f471efa lint 2014-03-07 18:04:03 -08:00
Jonathan Ong
2064f412cb remove res.charset usage 2014-03-07 16:46:54 -08:00
Jonathan Ong
3228fd3cbc remove res.charset support 2014-03-07 16:33:17 -08:00
Jonathan Ong
bad55f7977 move setHeader charset patch to .set
note that application/json no longer adds charset=utf-8. could be a
regression.

closes #1952
See also: https://github.com/broofa/node-mime/issues/86
2014-03-07 16:32:41 -08:00
Jonathan Ong
3cf7b2e39e refactor to use basic-auth 2014-03-07 16:18:51 -08:00
Jonathan Ong
b443da410a update History.md with 3.x branch 2014-03-06 14:59:36 -08:00
Jonathan Ong
87912103c9 3.5.0 2014-03-06 14:57:30 -08:00
Jonathan Ong
4ce1ee458e bump deps 2014-03-06 14:57:05 -08:00
Jonathan Ong
b0351a08de Update History.md 2014-03-05 22:45:36 -08:00
Jonathan Ong
3321055025 use path-to-regexp 2014-03-05 22:35:55 -08:00
Jonathan Ong
5572897998 some jshint cleanup
some more errors but whatever
2014-03-05 22:24:35 -08:00
Jonathan Ong
3112f92d08 move the patch to the response proto 2014-03-05 22:20:58 -08:00
Jonathan Ong
74f55a863a remove .writeHead patch
no middleware uses the “header” event anymore
2014-03-05 22:17:25 -08:00
Jonathan Ong
9ea18e10c9 throw errors when users try to access removed middleware 2014-03-05 22:13:54 -08:00
Jonathan Ong
d84457b9c2 remoive legacy docs bin 2014-03-05 22:07:56 -08:00
Jonathan Ong
643397ed21 remove unnecessary test/support/http 2014-03-05 22:06:14 -08:00
Jonathan Ong
85bf9ab76a uncomment a commented out test
lol @visionmedia
2014-03-05 22:01:36 -08:00
Jonathan Ong
6ec1904aac use serve-static for static middleware 2014-03-05 21:58:49 -08:00
Roman Shtylman
f1315b9efa fix examples for express 4 (separate middleware)
close #1947
2014-03-05 10:36:05 -05:00
Jonathan Ong
a0e6bb5cb2 npm start to start the server! 2014-03-05 03:36:36 -08:00
Roman Shtylman
eaf63d94f0 4.0.0-rc2 2014-03-05 01:32:48 -05:00
Jonathan Ong
45500fba74 Update Readme.md 2014-03-04 21:32:22 -08:00
Jonathan Ong
8472effab3 Update Readme.md 2014-03-04 21:31:50 -08:00
Roman Shtylman
d368aed150 fix Route#all before Route#verb
Properly handle calling a VERB after using .all()

close #1945
2014-03-03 17:50:13 -05:00
Jonathan Ong
e3b60e80c0 Update Readme.md for Express 4 2014-03-02 23:32:51 -08:00
Roman Shtylman
9df10674f0 4.0.0-rc1 2014-03-02 11:17:29 -05:00
Roman Shtylman
e3617fb8ab Merge pull request #1941 from deiga/patch-1
Fixed deprecated example doctype
2014-03-01 10:08:32 -05:00
Timo Sand
0fbfce58c6 Fixed deprecated example doctype 2014-03-01 12:32:42 +02:00
Roman Shtylman
f8b954bcd9 make express.Router() return a Router function instance
Similar to how express() returns an express `app` instance which is also
a function, express.Router() returns the Router instance which is also a
function and can be easily used via another router or the app.

app.use(express.Router());
2014-02-26 20:22:11 -05:00
Roman Shtylman
caa25b506d Merge pull request #1935 from visionmedia/router-params-middleware
Router: add parameter handling to middleware
2014-02-25 12:35:13 -05:00
Roman Shtylman
6911815171 Router: add parameter handling to middleware
Middleware (.use) can now specify parameter arguments to trigger
Router.param loading. This is handy if you want to `.use` additional
routers but need to load certain objects before the mounted middleware
runs.
2014-02-23 19:21:13 -05:00
Roman Shtylman
0719e5f402 implement app.route() 2014-02-23 11:31:43 -05:00
Roman Shtylman
07b731add0 bump cookie parser dependency to 1.0.1 2014-02-22 09:26:30 -05:00
Roman Shtylman
d42d8f5b07 move support for multiple res.cookie calls to lib/response
Patch.js is simpler and follows upstream node.js closer as a result.
2014-02-22 09:26:30 -05:00
Roman Shtylman
143e72dd85 remove support for node 0.8 2014-02-22 09:26:30 -05:00
Roman Shtylman
6835289564 remove ServerResonse.headerSent monkey patch
node.js ServerResponse contains a headersSent field. Use that instead of
our patched misnamed version.
2014-02-22 09:26:29 -05:00
Roman Shtylman
1396e0855d remove last pieces of connect dependency
- copy over patch.js to shim ServerResponse
- bundle `static` middleware
2014-02-22 09:26:29 -05:00
Roman Shtylman
6a7363e4ae use local copy of parseUrl 2014-02-22 09:26:29 -05:00
Roman Shtylman
9bc63d92a0 move connect.query() into our repo 2014-02-22 09:26:29 -05:00
TJ Holowaychuk
6b05f60bad update node-fresh 2014-02-19 15:29:39 -08:00
Jonathan Ong
25e6629bcc update history 2014-02-08 11:40:48 -08:00
Jonathan Ong
0796c1d2d2 test app.router: ignore connect method
so tests pass in 0.11. 0.11 client seems to throw errors more often, so
this is not an issue with express or node’s servers.
2014-02-08 11:39:26 -08:00
Jonathan Ong
aac1d52c4f res.location: remove resolving relative urls
closes #1804

this is an unnecessary maintenance burden (see the number of removed
tests), especially when supporting mounting. browsers handle relative
locations, and so should all clients.

a regression could be absolute locations on a mounted app, but 1. we
can fix that later when someone complains and 2) code-smell
2014-02-08 11:37:43 -08:00
Roman Shtylman
f41d09a3cf remove app.router and refactor middleware processing
This is an overhaul of middleware processing, Router and Route. Connect is no
longer used to process the middleware stack. This functionality has been
split into two parts: middleware stack and default error response.

The entry point for request processing is the `app.handle` method. It
sets up the default error response handle (to run in the event of no
other error handler) and then triggers the app router (instance of
Router) to handle the request.

The app router `handle` function contains the middleware dispatch layer
previously in the connect codebase. This layer handle the logic for
dispatching `.use` calls (stripping paths if needed). The app contains a
base router `app._router`. New routes can be created and `.use`d on this
router to organize routes into files.

Routers now have the following methods `.use`, `.all`, `.param` which
are all public.

Additionally, Routers have a `.route(path)` method which returns a new
instance of Route for the requested path. Route(s) are isolated
middleware stacks and contain methods for the HTTP verbs as well as an
`.all` method to act similar to middleware. These methods are chainable
to easily describe requirements for a route.

  var route = Router.route('/foo'); // or 'app.route('/foo')'

  route
  .all(auth)
  .get(function(...) {})
  .all(more_checks)
  .post(function(...) {})

Any Route and Router methods which accept handlers also accept error
(arity 4) handlers which will also behave as expected.

Finally, the `app.router` getter has been removed. Middleware and
handlers are run IN THE ORDER they are seen in the file. This means that
code which injected the `app.router` and then added error handlers (or
other middleware) will need to be updated to move those handlers after
any requests added on the app object. The examples have been updated
accordingly. This is the largest breaking change to codebases in this
commit.
2014-02-03 15:59:52 -05:00
Roman Shtylman
8aff64f89a backport dependency updates
- range parser 1.0.0
- fresh 0.2.1
- send 0.2.0
- cookie-signature 1.0.3
- merge-descriptors 0.0.2
2014-01-29 20:06:38 -05:00
Roman Shtylman
4bf9cfd477 update merge-descriptors 2014-01-29 20:01:10 -05:00
Roman Shtylman
08cbc442f5 update cookie-signature to 1.0.3
Fix for timing attack
2014-01-29 20:00:23 -05:00
Roman Shtylman
a02dd201e6 update send to 0.2.0 2014-01-29 19:58:53 -05:00
TJ Holowaychuk
a5f7dcee04 update node-fresh 2014-01-29 12:17:16 -08:00
Roman Shtylman
0ddd761904 update range parser to 1.0.0
- License

see #1912
2014-01-29 10:00:28 -05:00
Roman Shtylman
991c2a9d05 Merge pull request #1908 from visionmedia/locals-object
change res.locals to a plain js object.
2014-01-28 14:23:52 -08:00
Roman Shtylman
4983c38298 change res.locals to a plain js object.
Anyone who wants something fancier should use modules.

- fixes annoyance with not being able to set 'name' property on locals
2014-01-27 19:17:29 -05:00
Roman Shtylman
337ab24899 remove unused require 2014-01-24 19:31:32 -05:00
Roman Shtylman
63c6a9c5ad use escape-html module to escape html
Another util bites the dust.
2014-01-24 19:21:21 -05:00
Roman Shtylman
718e68ffae use utils-merge module to mixin object properties 2014-01-24 19:16:37 -05:00
Roman Shtylman
f56a5f01c4 remove deprecated express.createServer() method
This has been warning about deprecation for a long time. Use `express()`
to instantiate an express app.
2014-01-19 14:05:12 -05:00
Roman Shtylman
b77ffe0228 Merge pull request #1904 from popomore/master
delete semicolon
2014-01-19 09:18:29 -08:00
Haoliang Gao
fd6439bb36 delete semicolon 2014-01-19 23:53:48 +08:00
Jonathan Ong
121f8d02f3 Merge pull request #1889 from vesln/send-null-undefined
update the tests to show a difference between `send(null)` and `send(und...
2014-01-14 09:17:04 -08:00
Jonathan Ong
ff23423d34 3.4.8 2014-01-13 20:50:51 -08:00
Doug Patti
1d97599f8b prevent incorrect automatic OPTIONS responses
The router has automatic handling of OPTIONS based on the registered
routes, but if you make an OPTIONS request for an endpoint that does
not exist, then it will still return a 200 with nothing allowed.
Instead, we can let the request move on down the middleware chain. This
has two benefits: first, if the route was not defined and no other
middleware handles it, it will return with a 404. Secondly, if multiple
routers are used and a later one has the route or a custom OPTIONS
defined, the first router will not respond incorrectly.
2014-01-13 20:46:07 -08:00
Matheus Azzi
e465624fd0 Update layout.jade 2014-01-13 20:45:50 -08:00
Roman Shtylman
5ddbb6965f Merge pull request #1868 from dpatti/smarter-router-auto-options
Automatic OPTIONS response breaks with multiple routers
2014-01-13 14:45:06 -08:00
Doug Patti
a3b5f6d07f prevent incorrect automatic OPTIONS responses
The router has automatic handling of OPTIONS based on the registered
routes, but if you make an OPTIONS request for an endpoint that does
not exist, then it will still return a 200 with nothing allowed.
Instead, we can let the request move on down the middleware chain. This
has two benefits: first, if the route was not defined and no other
middleware handles it, it will return with a 404. Secondly, if multiple
routers are used and a later one has the route or a custom OPTIONS
defined, the first router will not respond incorrectly.
2014-01-13 17:40:42 -05:00
Roman Shtylman
ac2cbef8be Merge pull request #1899 from visionmedia/remove-configure
Remove app.configure
2014-01-11 15:42:55 -08:00
Roman Shtylman
dff22e9d09 update history file with configure changes 2014-01-11 10:54:13 -05:00
Roman Shtylman
7282b50ad0 remove app.configure() 2014-01-11 10:53:54 -05:00
Roman Shtylman
8c059469fd No 'json spaces' by default
Json rendering can be handled by user tools or overridden in their own
app to behave as desired. Minimizes the use of magic env settings.
2014-01-11 10:53:36 -05:00
Roman Shtylman
8c3f153dd4 remove use of app.configure for view cache setting 2014-01-11 10:52:38 -05:00
Jonathan Ong
185b526e60 Merge pull request #1892 from matheusazzi/patch-1
Update to valid Jade Doctype
2014-01-04 19:23:44 -08:00
Matheus Azzi
38996b30b1 Update layout.jade 2014-01-05 01:14:38 -02:00
TJ Holowaychuk
827dfed7c2 Merge pull request #1890 from oliversalzburg/patch-1
Value parameter of app.set() should be typed optional Object
2014-01-04 18:12:52 -08:00
Oliver Salzburg
28af21baeb Value parameter of app.set() is now typed optional mixed 2014-01-04 22:05:19 +01:00
Oliver Salzburg
951c70496b Value parameter of app.set() should be typed optional Object 2014-01-04 17:50:27 +01:00
Veselin Todorov
a36eeb96f3 update the tests to show a difference between send(null) and send(undefiend) 2014-01-03 19:47:57 +02:00
Jonathan Ong
7018d3d0e6 history: req.params 2014-01-03 03:00:48 -08:00
Jonathan Ong
3f14b4de1f Merge pull request #1835 from visionmedia/change-req-params-to-object
change req.params to an object instead of an array
2014-01-03 03:00:13 -08:00
Jonathan Ong
26c0be4c4e improve history.md 2014-01-03 02:57:24 -08:00
Jonathan Ong
cec0c06a70 refactor req.is and req.accepts* 2014-01-03 02:50:09 -08:00
Jonathan Ong
476f8deb07 remove binary 2014-01-03 02:33:00 -08:00
TJ Holowaychuk
dc5932d177 Merge pull request #1877 from reqshark/master
update express jade layout generator
2013-12-23 10:34:47 -08:00
Bent Cardan
cfd93b7529 update express jade layout generator
update doctype
2013-12-23 13:03:21 -05:00
Jonathan Ong
8b2208f394 Merge pull request #1876 from yosssi/dev
Updated the example file to use `doctype html` on  because  `doctype 5` was deprecated on Jade version 1.0.0.
2013-12-23 08:10:26 -08:00
yosssi
00a3b01f39 Changed doctype 5 to doctype html on the example file because the former was deprecated on Jade version 1.0.0. 2013-12-23 22:13:05 +09:00
TJ Holowaychuk
3baca251f0 use 8 threads for benchmarks 2013-12-22 08:57:04 -08:00
TJ Holowaychuk
72daae1d92 Merge pull request #1869 from yamatt/master
Error message now describes where the view was not able to be found.
2013-12-21 11:13:04 -08:00
Matt Copperwaite
fcbe53ddb5 Added appropriate test for more descriptive render error 2013-12-21 17:34:59 +00:00
Jonathan Ong
e9851672eb bench: remove --harmony-generators flag 2013-12-20 21:15:17 -08:00
TJ Holowaychuk
9a45f7bd3d add new benchmarks (to match koa) 2013-12-20 19:34:59 -08:00
Matt Copperwaite
85834fd146 Error message now describes where the view was not able to be found. Useful for debugging. 2013-12-20 11:39:31 +00:00
Roman Shtylman
a0c1ac7b45 add license field to package.json
close #1862
2013-12-18 10:16:56 -05:00
Alex Kocharin
7b0dca0f9c throw 400 in case of malformed paths 2013-12-11 17:14:44 -08:00
Jonathan Ong
34c83d7d29 3.4.7 2013-12-10 23:57:39 -08:00
Jonathan Ong
7c6882234e bump connect, mocha, and should 2013-12-10 23:54:07 -08:00
Jonathan Ong
2e68ddbae9 expose connect.middleware using Object.getOwnPropertyDescriptor()
closes #1853. no tests, but it should be fine.
2013-12-10 23:52:48 -08:00
Jonathan Ong
7724fc6af7 3.4.6 2013-12-01 12:21:08 -08:00
Roman Shtylman
2939075f03 Merge pull request #1836 from fluxusfrequency/patch-1
Grammar and punctuation fixes [ci skip]
2013-11-28 08:18:42 -08:00
Ben Lewis
606f68de02 Grammar and punctuation fixes [ci skip] 2013-11-28 06:36:21 -07:00
TJ Holowaychuk
c6c71abf4d change req.params to an object instead of an array 2013-11-27 19:46:39 -08:00
Jonathan Ong
863160ae49 3.4.5 2013-11-27 15:54:41 -08:00
TJ Holowaychuk
edd39fb194 fix weird variable name in example 2013-11-26 23:39:35 -08:00
TJ Holowaychuk
a71d264d45 fix weird variable name in example 2013-11-26 23:39:08 -08:00
TJ Holowaychuk
8a7a695836 ocd 2013-11-26 11:12:56 -08:00
TJ Holowaychuk
de54af4061 Merge pull request #1829 from michaelficarra/patch-1
fixes #1826: res.redirect('toString') fails with 500
2013-11-26 11:12:13 -08:00
Michael Ficarra
2f2a652bc9 fixes #1826: res.redirect('toString') fails with 500
Removed the unused map and corrected the doc comment.
2013-11-26 13:11:15 -06:00
TJ Holowaychuk
1e638663de Merge pull request #1822 from yakubori/auth-buffer-call-removal
Removed Buffer call with 'binary' encoding option in auth example.
2013-11-21 12:32:39 -08:00
Rick Yakubowski
1684a8792a Removed Buffer call with 'binary' encoding option in auth example.
According to the Node.js documentation for Buffer objects regarding the
'binary' encoding option:

"This encoding method is deprecated and should be avoided in favor of
Buffer objects where possible. This encoding will be removed in future
versions of Node."

Simply calling toString() with a 'base64' argument on the hash seems to
accomplish the same thing; this makes the code compatible with current
documentation as well as being a bit easier to follow.
2013-11-21 14:01:56 -05:00
Roman Shtylman
f47c0d9774 add Router.all() method
Similar to app.all() but specifically for attaching handlers to all
methods under a standalone router. This is useful for isolating routers
that require "middleware" like features for all routes managed by the
router.
2013-11-19 18:52:04 -05:00
Roman Shtylman
89e7264e53 pin marked devDep to protect out tests
marked has shown that it cannot be trusted with patch level changes!
2013-11-09 22:31:17 -05:00
Roman Shtylman
cada9f61c8 pin devDependencies using ~
If tests are passing and everything works, don't let things change out
from under us as much. Really we should do hard pinning, but will be a
bit lenient for now.
2013-11-09 22:26:09 -05:00
Jonathan Ong
373fa55981 fix markdown example test
marked 0.2.10 adds ids to header elements now.
2013-11-09 19:08:25 -08:00
Jonathan Ong
2bc703cfc2 Merge pull request #1802 from kapouer/patch-1
Remove leading ./ when using res.location('./relative')
2013-11-02 15:09:30 -07:00
Jérémy Lal
c9865b821d Test location with leading ./ and containing .. 2013-11-02 02:28:54 +01:00
Jérémy Lal
9c0de23645 Update tests expectancy of location headers 2013-11-02 02:28:49 +01:00
Jérémy Lal
b7a38af41d Use url.resolve to compute location header of relative paths 2013-11-02 02:28:29 +01:00
Jérémy Lal
661914781e semicolons 2013-11-02 00:39:32 +01:00
Jonathan Ong
6e3f3887e9 pin deps using semver1
somebody is going to complain that they can't install stuff because
they haven't upgraded npm
2013-10-30 20:55:11 -07:00
Jonathan Ong
a66d6bb034 pin dev deps to semver compatible versions 2013-10-30 20:51:10 -07:00
Jonathan Ong
2e197e2b98 be less picky with ENOENT errors in tests
closes #1580
2013-10-30 20:37:01 -07:00
Jonathan Ong
55d1a4f964 always send ETag when content-length > 0
closes #1780
2013-10-30 20:34:16 -07:00
Jonathan Ong
82a7d7a977 no semver2 so travis stops crying 2013-10-29 22:44:01 -07:00
Jonathan Ong
dae54b456f 3.4.4 2013-10-29 10:33:32 -07:00
Jonathan Ong
1b7a044f33 bump connect 2013-10-29 10:30:26 -07:00
Jonathan Ong
18264403b1 bump supertest to 0.8.1 2013-10-28 15:24:48 -07:00
Jonathan Ong
04d43b7039 remove .gitmodules
it's empty
2013-10-28 14:38:46 -07:00
TJ Holowaychuk
2dfecfb661 update methods for SEARCH 2013-10-28 12:02:24 -07:00
Jonathan Ong
250f1f5f6e Merge pull request #1796 from malixsys/patch-1
2013-100-23 -> 2013-10-23
2013-10-25 10:59:48 -07:00
M Alix
3ac718763f 2013-100-23 -> 2013-10-23 2013-10-25 14:28:24 +02:00
Jonathan Ong
05e1555c0d Merge pull request #1795 from chirag04/master
replace bodyparser with json and urlencoded
2013-10-25 03:31:24 -07:00
chirag04
855d1e2bf5 replace bodyparser with json and urlencoded 2013-10-25 15:45:25 +05:30
Jonathan Ong
f0bfb3b2b2 3.4.3 2013-10-23 11:19:48 -07:00
Jonathan Ong
4b4db0f7fb 3.4.2 2013-10-18 19:03:41 -07:00
Jonathan Ong
9bed2b80ee lint: remove unused stuff 2013-10-18 01:18:56 -07:00
Jonathan Ong
bb157c0cbf replace old contributors info with github's contributors 2013-10-17 13:06:13 -07:00
Jonathan Ong
1ef05d4a28 downgrade commander. closes #1783 2013-10-17 12:57:39 -07:00
TJ Holowaychuk
0b88208022 Merge branch 'master' of github.com:visionmedia/express 2013-10-16 19:52:00 -07:00
TJ Holowaychuk
c9d9ed3493 fix res.sendfile() callback
what the hell... I was just told readable streams have finish not end,
make up your mind node!
2013-10-17 02:51:01 +00:00
TJ Holowaychuk
bd8b9f5781 Merge branch 'master' of github.com:visionmedia/express 2013-10-16 19:21:09 -07:00
TJ Holowaychuk
9cbcf23df0 docs 2013-10-16 19:17:49 -07:00
Jonathan Ong
36e42db05b mocha globals - readable-stream defines globals
isaac you bastard
2013-10-15 18:33:47 -07:00
Jonathan Ong
2bf6a1d813 3.4.1 2013-10-15 18:28:49 -07:00
Jonathan Ong
e8373d3564 Merge pull request #1779 from visionmedia/jsonp-typeof-callback
check existence of jsonp callback
2013-10-15 18:22:01 -07:00
Jonathan Ong
e218377a3d check existence of jsonp callback 2013-10-15 12:39:32 -07:00
Jonathan Ong
7d1aed4955 update commander. closes #1693
i hope this doesn't break anything
2013-10-14 21:22:19 -07:00
Jonathan Ong
b4acbcf1fe use path.join for 'views' setting. closes #1427 2013-10-14 21:16:57 -07:00
Jonathan Ong
50cb62c5d2 fix tests for should.js 2013-10-14 18:35:46 -07:00
Jonathan Ong
4cf868bd74 Merge pull request #1776 from ykumar6/master
Add Runnable.com button
2013-10-14 18:33:34 -07:00
Yash Kumar
baa5a7c3e9 Add Runnable.com button 2013-10-14 14:16:18 -07:00
Jonathan Ong
ee228f7aea Merge pull request #1759 from muratgu/patch-1
fixes #1600
2013-09-19 12:43:42 -07:00
Jonathan Ong
d5b11c7d1b Merge pull request #1760 from jseip1679/master
documentation language fix
2013-09-19 12:38:21 -07:00
Jake Seip
ed7db34bab documentation language fix 2013-09-19 10:41:45 -07:00
muratgu
57e45e3af8 fixes #1600 2013-09-19 10:27:21 -07:00
Jonathan Ong
1dc46478cb README: add more links to expressjs.com 2013-09-17 00:35:23 -07:00
TJ Holowaychuk
113ed0927d fix test label typo 2013-09-16 23:34:16 +00:00
TJ Holowaychuk
ab8be2d741 remove second signed cookie test
for now
2013-09-16 23:33:42 +00:00
TJ Holowaychuk
3b53b11fcd fix signed cookies test 2013-09-16 23:32:34 +00:00
TJ Holowaychuk
9fb661559b refactor signed cookie tests 2013-09-16 23:24:54 +00:00
Jonathan Ong
04882cf72c Merge pull request #1735 from lxe/malformed-capture-route
Wrapped encodeURIcomponent in try-catch to eliminate errors on malformed captures.
2013-09-16 15:32:57 -07:00
lxe
288176bbc9 Added safe encodeURIcomponent to eliminate errors on malformed captures. 2013-09-16 14:57:31 -04:00
Jonathan Ong
5638a4fc62 Merge pull request #1688 from menzoic/issue/menzoic-1
removed unnecessary require statement
2013-09-09 21:45:19 -07:00
TJ Holowaychuk
3b4ce91fa3 refactor res.format() with a little ocd 2013-09-08 09:30:59 -07:00
TJ Holowaychuk
1c87e5e9a8 Merge pull request #1747 from sorribas/master
res.format() now includes charset.
2013-09-08 09:30:23 -07:00
Eduardo Sorribas
a887e6a881 Minor refactor of res.format 2013-09-08 02:34:52 -04:00
Eduardo Sorribas
69290cad6f res.format() now includes charset. Fixes #1744 2013-09-08 02:10:46 -04:00
Jonathan Ong
b66c7da05f Merge pull request #1659 from dresende/patch-1
Fixes typo in index.js vhost example
2013-09-07 21:43:45 -07:00
Jonathan Ong
92ddf77453 Merge pull request #1729 from patelatharva/patch-1
Improved variable names and updated comments for better clarity of example
2013-09-07 21:43:06 -07:00
TJ Holowaychuk
8e2f538983 refactor res.links() 2013-09-07 15:26:12 -07:00
TJ Holowaychuk
2817d8caf2 Merge pull request #1746 from sorribas/master
Allow multiple call concatenation for res.links.
2013-09-07 15:24:50 -07:00
TJ Holowaychuk
b7f08fb159 Release 3.4.0 2013-09-07 12:25:00 -07:00
TJ Holowaychuk
0c2768f5bd update connect 2013-09-07 12:24:24 -07:00
Eduardo Sorribas
09bede1a92 Fix the links test so it resets the header for each test. 2013-09-07 01:10:13 -04:00
Eduardo Sorribas
7059d3b71e Allow multiple call concatenation for res.links. Fixes #1683 2013-09-06 21:44:03 -04:00
cjihrig
e5de08faa1 add res.vary(). Closes #1682 2013-09-02 09:10:14 -07:00
TJ Holowaychuk
e43ff076fd Merge pull request #1740 from superic/master
Updated Util.isAbsolute(path) to return true for Azure absolute paths
2013-09-02 08:56:03 -07:00
TJ Holowaychuk
3ea7381dea Merge pull request #1711 from jonjenkins/master
Fixes from pull request #1643
2013-09-02 08:55:23 -07:00
TJ Holowaychuk
f1c46f51e5 Release 3.3.8 2013-09-02 08:01:07 -07:00
TJ Holowaychuk
297fb4e0b0 update connect 2013-09-02 08:00:48 -07:00
Eric Willis
9e406dfee2 Updated Util.isAbsolute(path) to return true for Azure absolute paths
- Azure absolute paths look like \\ip_address\volume\guid\guid\site\wwwroot\...file.js.
  Changed Util.isAbsolute to return true for paths that start with two backslashes.
2013-08-31 16:16:49 -07:00
TJ Holowaychuk
30b7aa8a17 update connect 2013-08-28 10:03:42 -07:00
TJ Holowaychuk
c1d16e0016 Release 3.3.7 2013-08-28 09:39:31 -07:00
TJ Holowaychuk
929ffb8d77 update connect 2013-08-28 09:37:45 -07:00
TJ Holowaychuk
197a2e3b54 Release 3.3.6 2013-08-27 13:49:09 -07:00
TJ Holowaychuk
6cf6c8b918 Revert "remove charset from json responses. Closes #1631"
This reverts commit 138d74aefa.
2013-08-27 13:48:18 -07:00
Atharva
058d7ec2ea Improved variable names and updated comments for better clarity of example 2013-08-24 16:44:44 +05:30
TJ Holowaychuk
752b5f705e Merge branch 'master' of github.com:visionmedia/express 2013-08-17 01:07:17 -07:00
TJ Holowaychuk
e7fa579637 update license 2013-08-17 01:07:09 -07:00
TJ Holowaychuk
97781d4112 Merge pull request #1723 from gmethvin/accepts
Make req.accepts take an argument list
2013-08-16 16:32:48 -07:00
Greg Methvin
3ddd8e66a7 Make req.accepts take an argument list 2013-08-16 15:19:33 -07:00
TJ Holowaychuk
8a1e865e37 remove silly out-of-date dep badge 2013-08-15 08:18:01 -07:00
TJ Holowaychuk
e850cb3ea3 Release 3.3.5 2013-08-11 07:50:51 +10:00
TJ Holowaychuk
13d3efe8df update fresh 2013-08-11 07:49:15 +10:00
TJ Holowaychuk
d6ecf785a2 Merge pull request #1710 from hacksparrow/master
Fixed test cases for res.format
2013-08-09 15:03:45 -07:00
Jon Jenkins
19cb39869f Fixes from pull request #1643, array method correction 2013-08-04 12:46:50 -05:00
Hage Yaapa
a38bdf6758 fixed test cases for res.format 2013-08-04 20:32:08 +05:30
Jon Jenkins
bdbdab7fcc Fixes from pull request #1643 2013-08-03 16:33:15 -05:00
TJ Holowaychuk
5aa9670120 Merge pull request #1685 from CharlesHolbrow/master
Fix typo in app.param comment
2013-08-02 14:46:40 -07:00
TJ Holowaychuk
8ad8cb93cc refactor 2013-08-02 14:46:25 -07:00
TJ Holowaychuk
610e172fcf Merge pull request #1694 from kavu/add_disable_etag
Add application setting to disable ETag (again)
2013-08-02 14:45:37 -07:00
TJ Holowaychuk
6942070a21 add [dir] to express(1) --help output. Closes #1699 2013-08-02 14:44:52 -07:00
TJ Holowaychuk
e283200511 remove comma-first from express(1)-generated app 2013-08-01 11:10:21 -07:00
Max Riveiro
54a192a5c5 Add application setting to disable ETag completely 2013-07-21 12:49:28 +04:00
TJ Holowaychuk
c3bd65eda2 Revert "remove old OPTIONS default response"
This reverts commit 2bba69f633.
2013-07-16 11:22:02 -07:00
Esco Obong
7c2ed1d2d6 removed unnecessary require statement 2013-07-15 02:13:32 -04:00
Charles Holbrow
3de81e0147 Fix typo in app.param comment 2013-07-13 16:32:02 -07:00
TJ Holowaychuk
8fe1e2a5b4 Release 3.3.4 2013-07-08 14:42:45 -07:00
TJ Holowaychuk
909dbb81d5 update send and connect 2013-07-08 14:40:02 -07:00
TJ Holowaychuk
26802a689c fix package.json conflict 2013-07-04 13:39:36 -07:00
TJ Holowaychuk
37239fb67f Release 3.3.3 2013-07-04 07:37:17 -07:00
TJ Holowaychuk
018dc40b32 update connect 2013-07-04 07:36:57 -07:00
TJ Holowaychuk
52440955e6 Release 3.3.2 2013-07-03 11:25:54 -07:00
TJ Holowaychuk
c2f3d6ce2b update connect 2013-07-03 11:25:15 -07:00
TJ Holowaychuk
be858f5d07 update send 2013-07-03 11:24:31 -07:00
TJ Holowaychuk
1f14734f91 Merge pull request #1664 from paulmillr/topics/update-deps
Update commander and mkdirp dependencies.
2013-07-01 11:14:15 -07:00
Paul Miller
6d1d694dbb Update commander and mkdirp dependencies. 2013-06-28 19:15:32 +03:00
TJ Holowaychuk
ba5c48aa86 remove .version export 2013-06-27 08:38:53 -07:00
TJ Holowaychuk
320d7807a9 Release 3.3.1 2013-06-27 08:32:37 -07:00
TJ Holowaychuk
6650a312b7 update connect 2013-06-27 08:32:20 -07:00
TJ Holowaychuk
832c3b3744 Release 3.3.0 2013-06-26 10:07:34 -07:00
TJ Holowaychuk
76691bfd6b update connect 2013-06-26 10:05:40 -07:00
TJ Holowaychuk
29fe5ea785 Merge pull request #1657 from ralphtheninja/master
use send 0.1.1 to get rid of npm warning during install
2013-06-26 09:54:24 -07:00
Diogo Resende
7a31a1d311 Fixes typo in index.js vhost example 2013-06-23 23:23:58 +02:00
Lars-Magnus Skog
52a820113f use send 0.1.1 to get rid of npm warning 2013-06-23 00:49:20 +02:00
TJ Holowaychuk
aec3428489 Merge pull request #1650 from printercu/master
move .app to req's & res's prototypes
2013-06-11 12:39:59 -07:00
TJ Holowaychuk
a10f695b6f pin jade dev dep so tests do not break 2013-06-11 12:24:17 -07:00
Max Melentiev
a3c9eacaf1 move .app to req's & res's prototypes 2013-06-11 19:42:30 +04:00
TJ Holowaychuk
19d685b152 return actual booleans from req.accept* functions 2013-06-06 13:47:18 -07:00
TJ Holowaychuk
8ab44081d4 add support for multiple X-Forwarded-Proto values. Closes #1646 2013-06-05 12:05:45 -07:00
TJ Holowaychuk
0431d22822 add req.secure tests 2013-06-05 11:59:47 -07:00
TJ Holowaychuk
138d74aefa remove charset from json responses. Closes #1631 2013-06-05 11:51:59 -07:00
TJ Holowaychuk
28562b2cf8 Merge pull request #1643 from jonjenkins/master
Fixed issue with callback querystring failure
2013-06-03 14:52:49 -07:00
TJ Holowaychuk
e0afda444f Release 3.2.6 2013-06-02 17:15:39 -07:00
TJ Holowaychuk
5a4cac58af update connect 2013-06-02 17:15:14 -07:00
TJ Holowaychuk
545dca6c4d Merge pull request #1642 from jade-bot/master
Update jade files [bot-update#1]
2013-06-02 16:04:50 -07:00
TJ Holowaychuk
e59a882389 Merge pull request #1634 from joshlangner/patch-1
added some additional explanation for clarity
2013-06-02 15:58:25 -07:00
TJ Holowaychuk
ccd9828535 Merge pull request #1630 from EvanHahn/patch-1
Remove dead link from readme's "More Information"
2013-06-02 15:50:45 -07:00
TJ Holowaychuk
41f0d32355 Merge pull request #1622 from saintedlama/master
Fixes indentation for css engines when using express to scaffold an application
2013-06-02 15:47:36 -07:00
Jenkins
2f19b4fefc Corrected callback crashing app when array 2013-05-26 21:35:52 -05:00
jade-bot
cd31cecfd1 Update to maintain compatability with the latest version of jade 2013-05-26 14:51:34 -07:00
joshlangner
fd73bd006e added some additional explanation for clarity 2013-05-19 22:47:04 -04:00
Evan Hahn
7388c2c223 Remove dead link from readme's "More Information" 2013-05-17 11:37:21 -06:00
saintelama
8b2f1bba95 fix indentation for css engine support 2013-05-12 23:43:52 +02:00
180 changed files with 5389 additions and 3585 deletions

5
.gitignore vendored
View File

@@ -1,6 +1,5 @@
coverage.html
coverage/
.DS_Store
lib-cov
*.seed
*.log
*.csv
@@ -13,7 +12,5 @@ benchmarks/graphs
testing
node_modules/
testing
.coverage_data
cover_html
test.js
.idea

0
.gitmodules vendored
View File

View File

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

View File

@@ -1,4 +1,10 @@
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"

25
Contributing.md Normal file
View File

@@ -0,0 +1,25 @@
## Website Issues
Issues for the expressjs.com website go here https://github.com/visionmedia/expressjs.com
## PRs and Code contributions
* Tests must pass.
* Follow existing coding style.
* If you fix a bug, add a test.
## Issues which are questions
We will typically close any vague issues or questions that are specific to some app you are writing. Please double check the docs and other references before being trigger happy with posting a question issue.
Things that will help get your question issue looked at:
* Full and runnable JS code.
* Clear description of the problem or unexpected behavior.
* Clear description of the expected result.
* Steps you have taken to debug it yourself.
If you post a question and do not outline the above items or make it easy for us to understand and reproduce your issue, it will be closed.

View File

@@ -1,5 +1,509 @@
4.4.5 / 2014-06-26
==================
3.2.5 / 2013-05-21
* deps: cookie-signature@1.0.4
- fix for timing attacks
4.4.4 / 2014-06-20
==================
* fix `res.attachment` Unicode filenames in Safari
* fix "trim prefix" debug message in `express:router`
* deps: accepts@~1.0.5
* deps: buffer-crc32@0.2.3
4.4.3 / 2014-06-11
==================
* fix persistence of modified `req.params[name]` from `app.param()`
* deps: accepts@1.0.3
- deps: negotiator@0.4.6
* deps: debug@1.0.2
* deps: send@0.4.3
- Do not throw un-catchable error on file open race condition
- Use `escape-html` for HTML escaping
- deps: debug@1.0.2
- deps: finished@1.2.2
- deps: fresh@0.2.2
* deps: serve-static@1.2.3
- Do not throw un-catchable error on file open race condition
- deps: send@0.4.3
4.4.2 / 2014-06-09
==================
* fix catching errors from top-level handlers
* use `vary` module for `res.vary`
* deps: debug@1.0.1
* deps: proxy-addr@1.0.1
* deps: send@0.4.2
- fix "event emitter leak" warnings
- deps: debug@1.0.1
- deps: finished@1.2.1
* deps: serve-static@1.2.2
- fix "event emitter leak" warnings
- deps: send@0.4.2
* deps: type-is@1.2.1
4.4.1 / 2014-06-02
==================
* deps: methods@1.0.1
* deps: send@0.4.1
- Send `max-age` in `Cache-Control` in correct format
* deps: serve-static@1.2.1
- use `escape-html` for escaping
- deps: send@0.4.1
4.4.0 / 2014-05-30
==================
* custom etag control with `app.set('etag', val)`
- `app.set('etag', function(body, encoding){ return '"etag"' })` custom etag generation
- `app.set('etag', 'weak')` weak tag
- `app.set('etag', 'strong')` strong etag
- `app.set('etag', false)` turn off
- `app.set('etag', true)` standard etag
* mark `res.send` ETag as weak and reduce collisions
* update accepts to 1.0.2
- Fix interpretation when header not in request
* update send to 0.4.0
- Calculate ETag with md5 for reduced collisions
- Ignore stream errors after request ends
- deps: debug@0.8.1
* update serve-static to 1.2.0
- Calculate ETag with md5 for reduced collisions
- Ignore stream errors after request ends
- deps: send@0.4.0
4.3.2 / 2014-05-28
==================
* fix handling of errors from `router.param()` callbacks
4.3.1 / 2014-05-23
==================
* revert "fix behavior of multiple `app.VERB` for the same path"
- this caused a regression in the order of route execution
4.3.0 / 2014-05-21
==================
* add `req.baseUrl` to access the path stripped from `req.url` in routes
* fix behavior of multiple `app.VERB` for the same path
* fix issue routing requests among sub routers
* invoke `router.param()` only when necessary instead of every match
* proper proxy trust with `app.set('trust proxy', trust)`
- `app.set('trust proxy', 1)` trust first hop
- `app.set('trust proxy', 'loopback')` trust loopback addresses
- `app.set('trust proxy', '10.0.0.1')` trust single IP
- `app.set('trust proxy', '10.0.0.1/16')` trust subnet
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
- `app.set('trust proxy', false)` turn off
- `app.set('trust proxy', true)` trust everything
* set proper `charset` in `Content-Type` for `res.send`
* update type-is to 1.2.0
- support suffix matching
4.2.0 / 2014-05-11
==================
* 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)`
* fix `req.next` when inside router instance
* include `ETag` header in `HEAD` requests
* keep previous `Content-Type` for `res.jsonp`
* support PURGE method
- add `app.purge`
- add `router.purge`
- include PURGE in `app.all`
* update debug to 0.8.0
- add `enable()` method
- change from stderr to stdout
* update methods to 1.0.0
- add PURGE
4.1.2 / 2014-05-08
==================
* fix `req.host` for IPv6 literals
* fix `res.jsonp` error if callback param is object
4.1.1 / 2014-04-27
==================
* fix package.json to reflect supported node version
4.1.0 / 2014-04-24
==================
* 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
- deps: negotiator@0.4.0
* update cookie to 0.1.2
- Fix for maxAge == 0
- made compat with expires field
* update send to 0.3.0
- Accept API options in options object
- Coerce option types
- Control whether to generate etags
- Default directory access to 403 when index disabled
- Fix sending files with dots without root set
- Include file path in etag
- Make "Can't set headers after they are sent." catchable
- Send full entity-body for multi range requests
- Set etags to "weak"
- Support "If-Range" header
- Support multiple index paths
- deps: mime@1.2.11
* update serve-static to 1.1.0
- Accept options directly to `send` module
- Resolve relative paths at middleware setup
- Use parseurl to parse the URL from request
- deps: send@0.3.0
* update type-is to 1.1.0
- add non-array values support
- add `multipart` as a shorthand
4.0.0 / 2014-04-09
==================
* 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
3.10.5 / 2014-06-11
===================
* deps: connect@2.19.6
- deps: body-parser@1.3.1
- deps: compression@1.0.7
- deps: debug@1.0.2
- deps: serve-index@1.1.1
- deps: serve-static@1.2.3
* deps: debug@1.0.2
* deps: send@0.4.3
- Do not throw un-catchable error on file open race condition
- Use `escape-html` for HTML escaping
- deps: debug@1.0.2
- deps: finished@1.2.2
- deps: fresh@0.2.2
3.10.4 / 2014-06-09
===================
* deps: connect@2.19.5
- fix "event emitter leak" warnings
- deps: csurf@1.2.1
- deps: debug@1.0.1
- deps: serve-static@1.2.2
- deps: type-is@1.2.1
* deps: debug@1.0.1
* deps: send@0.4.2
- fix "event emitter leak" warnings
- deps: finished@1.2.1
- deps: debug@1.0.1
3.10.3 / 2014-06-05
===================
* use `vary` module for `res.vary`
* deps: connect@2.19.4
- deps: errorhandler@1.0.2
- deps: method-override@2.0.2
- deps: serve-favicon@2.0.1
* deps: debug@1.0.0
3.10.2 / 2014-06-03
===================
* deps: connect@2.19.3
- deps: compression@1.0.6
3.10.1 / 2014-06-03
===================
* deps: connect@2.19.2
- deps: compression@1.0.4
* deps: proxy-addr@1.0.1
3.10.0 / 2014-06-02
===================
* deps: connect@2.19.1
- deprecate `methodOverride()` -- use `method-override` module directly
- deps: body-parser@1.3.0
- deps: method-override@2.0.1
- deps: multiparty@3.2.8
- deps: response-time@2.0.0
- deps: serve-static@1.2.1
* deps: methods@1.0.1
* deps: send@0.4.1
- Send `max-age` in `Cache-Control` in correct format
3.9.0 / 2014-05-30
==================
* custom etag control with `app.set('etag', val)`
- `app.set('etag', function(body, encoding){ return '"etag"' })` custom etag generation
- `app.set('etag', 'weak')` weak tag
- `app.set('etag', 'strong')` strong etag
- `app.set('etag', false)` turn off
- `app.set('etag', true)` standard etag
* Include ETag in HEAD requests
* mark `res.send` ETag as weak and reduce collisions
* update connect to 2.18.0
- deps: compression@1.0.3
- deps: serve-index@1.1.0
- deps: serve-static@1.2.0
* update send to 0.4.0
- Calculate ETag with md5 for reduced collisions
- Ignore stream errors after request ends
- deps: debug@0.8.1
3.8.1 / 2014-05-27
==================
* update connect to 2.17.3
- deps: body-parser@1.2.2
- deps: express-session@1.2.1
- deps: method-override@1.0.2
3.8.0 / 2014-05-21
==================
* keep previous `Content-Type` for `res.jsonp`
* set proper `charset` in `Content-Type` for `res.send`
* update connect to 2.17.1
- fix `res.charset` appending charset when `content-type` has one
- deps: express-session@1.2.0
- deps: morgan@1.1.1
- deps: serve-index@1.0.3
3.7.0 / 2014-05-18
==================
* proper proxy trust with `app.set('trust proxy', trust)`
- `app.set('trust proxy', 1)` trust first hop
- `app.set('trust proxy', 'loopback')` trust loopback addresses
- `app.set('trust proxy', '10.0.0.1')` trust single IP
- `app.set('trust proxy', '10.0.0.1/16')` trust subnet
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
- `app.set('trust proxy', false)` turn off
- `app.set('trust proxy', true)` trust everything
* update connect to 2.16.2
- deprecate `res.headerSent` -- use `res.headersSent`
- deprecate `res.on("header")` -- use on-headers module instead
- fix edge-case in `res.appendHeader` that would append in wrong order
- json: use body-parser
- urlencoded: use body-parser
- dep: bytes@1.0.0
- dep: cookie-parser@1.1.0
- dep: csurf@1.2.0
- dep: express-session@1.1.0
- dep: method-override@1.0.1
3.6.0 / 2014-05-09
==================
* 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
==================
* fix `req.host` for IPv6 literals
* fix `res.jsonp` error if callback param is object
3.5.2 / 2014-04-24
==================
* update connect to 2.14.5
* update cookie to 0.1.2
* update mkdirp to 0.4.0
* update send to 0.3.0
3.5.1 / 2014-03-25
==================
* pin less-middleware in generated app
3.5.0 / 2014-03-06
==================
* bump deps
3.4.8 / 2014-01-13
==================
* prevent incorrect automatic OPTIONS responses #1868 @dpatti
* update binary and examples for jade 1.0 #1876 @yossi, #1877 @reqshark, #1892 @matheusazzi
* throw 400 in case of malformed paths @rlidwka
3.4.7 / 2013-12-10
==================
* update connect
3.4.6 / 2013-12-01
==================
* update connect (raw-body)
3.4.5 / 2013-11-27
==================
* update connect
* res.location: remove leading ./ #1802 @kapouer
* res.redirect: fix `res.redirect('toString') #1829 @michaelficarra
* res.send: always send ETag when content-length > 0
* router: add Router.all() method
3.4.4 / 2013-10-29
==================
* update connect
* update supertest
* update methods
* express(1): replace bodyParser() with urlencoded() and json() #1795 @chirag04
3.4.3 / 2013-10-23
==================
* update connect
3.4.2 / 2013-10-18
==================
* update connect
* downgrade commander
3.4.1 / 2013-10-15
==================
* update connect
* update commander
* jsonp: check if callback is a function
* router: wrap encodeURIComponent in a try/catch #1735 (@lxe)
* res.format: now includes chraset @1747 (@sorribas)
* res.links: allow multiple calls @1746 (@sorribas)
3.4.0 / 2013-09-07
==================
* add res.vary(). Closes #1682
* update connect
3.3.8 / 2013-09-02
==================
* update connect
3.3.7 / 2013-08-28
==================
* update connect
3.3.6 / 2013-08-27
==================
* Revert "remove charset from json responses. Closes #1631" (causes issues in some clients)
* add: req.accepts take an argument list
3.3.4 / 2013-07-08
==================
* update send and connect
3.3.3 / 2013-07-04
==================
* update connect
3.3.2 / 2013-07-03
==================
* update connect
* update send
* remove .version export
3.3.1 / 2013-06-27
==================
* update connect
3.3.0 / 2013-06-26
==================
* update connect
* add support for multiple X-Forwarded-Proto values. Closes #1646
* change: remove charset from json responses. Closes #1631
* change: return actual booleans from req.accept* functions
* fix jsonp callback array throw
3.2.6 / 2013-06-02
==================
* update connect
3.2.5 / 2013-05-21
==================
* update connect
@@ -7,23 +511,23 @@
* add: throw a meaningful error when there is no default engine
* change generation of ETags with res.send() to GET requests only. Closes #1619
3.2.4 / 2013-05-09
3.2.4 / 2013-05-09
==================
* fix `req.subdomains` when no Host is present
* fix `req.host` when no Host is present, return undefined
3.2.3 / 2013-05-07
3.2.3 / 2013-05-07
==================
* update connect / qs
3.2.2 / 2013-05-03
3.2.2 / 2013-05-03
==================
* update qs
3.2.1 / 2013-04-29
3.2.1 / 2013-04-29
==================
* add app.VERB() paths array deprecation warning
@@ -31,27 +535,27 @@
* update qs and remove all ~ semver crap
* fix: accept number as value of Signed Cookie
3.2.0 / 2013-04-15
3.2.0 / 2013-04-15
==================
* add "view" constructor setting to override view behaviour
* add req.acceptsEncoding(name)
* add req.acceptedEncodings
* revert cookie signature change causing session race conditions
* fix sorting of Accept values of the same quality
* fix sorting of Accept values of the same quality
3.1.2 / 2013-04-12
3.1.2 / 2013-04-12
==================
* add support for custom Accept parameters
* update cookie-signature
3.1.1 / 2013-04-01
3.1.1 / 2013-04-01
==================
* add X-Forwarded-Host support to `req.host`
* fix relative redirects
* update mkdirp
* fix relative redirects
* update mkdirp
* update buffer-crc32
* remove legacy app.configure() method from app template.

View File

@@ -1,6 +1,6 @@
(The MIT License)
Copyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2009-2014 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,33 +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) \
$(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
benchmark:
@./support/bench
clean:
rm -f coverage.html
rm -fr lib-cov
.PHONY: test test-unit test-acceptance benchmark clean

141
Readme.md
View File

@@ -1,6 +1,11 @@
![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)
[![express logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). [![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express) [![Dependency Status](https://gemnasium.com/visionmedia/express.png)](https://gemnasium.com/visionmedia/express)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
[![NPM version](https://badge.fury.io/js/express.svg)](http://badge.fury.io/js/express)
[![Build Status](https://travis-ci.org/visionmedia/express.svg?branch=master)](https://travis-ci.org/visionmedia/express)
[![Coverage Status](https://img.shields.io/coveralls/visionmedia/express.svg)](https://coveralls.io/r/visionmedia/express)
[![Gittip](http://img.shields.io/gittip/visionmedia.svg)](https://www.gittip.com/visionmedia/)
```js
var express = require('express');
@@ -13,17 +18,22 @@ 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 -g express
$ npm install express
## Quick Start
The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:
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
Create the app:
$ npm install -g express
$ express /tmp/foo && cd /tmp/foo
Install dependencies:
@@ -32,149 +42,72 @@ app.listen(3000);
Start the server:
$ node app
$ npm start
## 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
## Philosophy
The Express philosophy is to provide small, robust tooling for HTTP servers. Making
The Express philosophy is to provide small, robust tooling for HTTP servers, making
it a great solution for single page applications, web sites, hybrids, or public
HTTP APIs.
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)
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)
* Join #express on freenode
* [Google Group](http://groups.google.com/group/express-js) for discussion
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
* 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)
* [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito)
* [Русскоязычная документация](http://jsman.ru/express/)
* Run express examples [online](https://runnable.com/express)
## Viewing Examples
Clone the Express repo, then install the dev dependencies to install all the example / test suite deps:
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
$ git clone git://github.com/visionmedia/express.git --depth 1
$ cd express
$ npm install
then run whichever tests you want:
Then run whichever tests you want:
$ node examples/content-negotiation
You can also view live examples here:
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
## Running Tests
To run the test suite first invoke the following command within the repo, installing the development dependencies:
To run the test suite, first invoke the following command within the repo, installing the development dependencies:
$ npm install
then run the tests:
Then run the tests:
$ make test
```sh
$ npm test
```
## Contributors
```
project: express
commits: 3559
active : 468 days
files : 237
authors:
1891 Tj Holowaychuk 53.1%
1285 visionmedia 36.1%
182 TJ Holowaychuk 5.1%
54 Aaron Heckmann 1.5%
34 csausdev 1.0%
26 ciaranj 0.7%
21 Robert Sköld 0.6%
6 Guillermo Rauch 0.2%
3 Dav Glass 0.1%
3 Nick Poulden 0.1%
2 Randy Merrill 0.1%
2 Benny Wong 0.1%
2 Hunter Loftis 0.1%
2 Jake Gordon 0.1%
2 Brian McKinney 0.1%
2 Roman Shtylman 0.1%
2 Ben Weaver 0.1%
2 Dave Hoover 0.1%
2 Eivind Fjeldstad 0.1%
2 Daniel Shaw 0.1%
1 Matt Colyer 0.0%
1 Pau Ramon 0.0%
1 Pero Pejovic 0.0%
1 Peter Rekdal Sunde 0.0%
1 Raynos 0.0%
1 Teng Siong Ong 0.0%
1 Viktor Kelemen 0.0%
1 ctide 0.0%
1 8bitDesigner 0.0%
1 isaacs 0.0%
1 mgutz 0.0%
1 pikeas 0.0%
1 shuwatto 0.0%
1 tstrimple 0.0%
1 ewoudj 0.0%
1 Adam Sanderson 0.0%
1 Andrii Kostenko 0.0%
1 Andy Hiew 0.0%
1 Arpad Borsos 0.0%
1 Ashwin Purohit 0.0%
1 Benjen 0.0%
1 Darren Torpey 0.0%
1 Greg Ritter 0.0%
1 Gregory Ritter 0.0%
1 James Herdman 0.0%
1 Jim Snodgrass 0.0%
1 Joe McCann 0.0%
1 Jonathan Dumaine 0.0%
1 Jonathan Palardy 0.0%
1 Jonathan Zacsh 0.0%
1 Justin Lilly 0.0%
1 Ken Sato 0.0%
1 Maciej Małecki 0.0%
1 Masahiro Hayashi 0.0%
```
Author: [TJ Holowaychuk](http://github.com/visionmedia)
Lead Maintainer: [Roman Shtylman](https://github.com/defunctzombie)
Contributors: https://github.com/visionmedia/express/graphs/contributors
## License
(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.
MIT

13
benchmarks/Makefile Normal file
View File

@@ -0,0 +1,13 @@
all:
@./run 1 middleware
@./run 5 middleware
@./run 10 middleware
@./run 15 middleware
@./run 20 middleware
@./run 30 middleware
@./run 50 middleware
@./run 100 middleware
@echo
.PHONY: all

23
benchmarks/middleware.js Normal file
View File

@@ -0,0 +1,23 @@
var http = require('http');
var express = require('..');
var app = express();
// number of middleware
var n = parseInt(process.env.MW || '1', 10);
console.log(' %s middleware', n);
while (n--) {
app.use(function(req, res, next){
next();
});
}
var body = new Buffer('Hello World');
app.use(function(req, res, next){
res.send(body);
});
app.listen(3333);

16
benchmarks/run Executable file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
echo
MW=$1 node $2 &
pid=$!
sleep 2
wrk 'http://localhost:3333/?foo[bar]=baz' \
-d 3 \
-c 50 \
-t 8 \
| grep 'Requests/sec' \
| awk '{ print " " $2 }'
kill $pid

View File

@@ -1,422 +0,0 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var exec = require('child_process').exec
, program = require('commander')
, mkdirp = require('mkdirp')
, pkg = require('../package.json')
, version = pkg.version
, os = require('os')
, fs = require('fs');
// CLI
program
.version(version)
.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 5'
, '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\')'
, ' , routes = require(\'./routes\')'
, ' , user = require(\'./routes/user\')'
, ' , http = require(\'http\')'
, ' , path = require(\'path\');'
, ''
, 'var app = express();'
, ''
, '// all environments'
, 'app.set(\'port\', process.env.PORT || 3000);'
, 'app.set(\'views\', __dirname + \'/views\');'
, 'app.set(\'view engine\', \':TEMPLATE\');'
, 'app.use(express.favicon());'
, 'app.use(express.logger(\'dev\'));'
, 'app.use(express.bodyParser());'
, '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 {
program.confirm('destination is not empty, continue? ', function(ok){
if (ok) {
process.stdin.destroy();
createApplicationAt(path);
} else {
abort('aborting');
}
});
}
});
})(path);
/**
* 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: __dirname + \'/public\' }));');
break;
case 'stylus':
app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware(__dirname + \'/public\'));');
break;
default:
app = app.replace('{css}', '');
}
// Session support
app = app.replace('{sess}', program.sessions
? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());'
: '');
// 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'] = '*';
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,8 +2,10 @@
* Module dependencies.
*/
var express = require('../..')
, hash = require('./pass').hash;
var express = require('../..');
var hash = require('./pass').hash;
var bodyParser = require('body-parser');
var session = require('express-session');
var app = module.exports = express();
@@ -14,15 +16,14 @@ app.set('views', __dirname + '/views');
// middleware
app.use(express.bodyParser());
app.use(express.cookieParser('shhhh, very secret'));
app.use(express.session());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ secret: 'shhhh, very secret' }));
// Session-persisted message middleware
app.use(function(req, res, next){
var err = req.session.error
, msg = req.session.success;
var err = req.session.error;
var msg = req.session.success;
delete req.session.error;
delete req.session.success;
res.locals.message = '';
@@ -62,7 +63,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) {
@@ -75,7 +76,7 @@ function restrict(req, res, next) {
}
app.get('/', function(req, res){
res.redirect('login');
res.redirect('/login');
});
app.get('/restricted', restrict, function(req, res){
@@ -98,9 +99,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;
@@ -113,11 +114,12 @@ app.post('/login', function(req, res){
req.session.error = 'Authentication failed, please check your '
+ ' username and password.'
+ ' (use "tj" and "foobar")';
res.redirect('login');
res.redirect('/login');
}
});
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -32,7 +32,7 @@ var iterations = 12000;
exports.hash = function (pwd, salt, fn) {
if (3 == arguments.length) {
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
fn(err, (new Buffer(hash, 'binary')).toString('base64'));
fn(err, hash.toString('base64'));
});
} else {
fn = salt;
@@ -41,8 +41,8 @@ exports.hash = function (pwd, salt, fn) {
salt = salt.toString('base64');
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
if (err) return fn(err);
fn(null, salt, (new Buffer(hash, 'binary')).toString('base64'));
fn(null, salt, hash.toString('base64'));
});
});
}
};
};

View File

@@ -1,6 +1,10 @@
/**
* Module dependencies.
*/
var express = require('../..')
, app = express();
var express = require('../..');
var logger = require('morgan');
var app = express();
app.set('views', __dirname);
app.set('view engine', 'jade');
@@ -14,11 +18,14 @@ while (n--) {
pets.push({ name: 'Jane', age: 6, species: 'ferret' });
}
app.use(express.logger('dev'));
app.use(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,4 +1,4 @@
style
style.
body {
padding: 50px;
font: 16px "Helvetica Neue", Helvetica;

View File

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

View File

@@ -1,8 +1,9 @@
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
// for expected response in index.js
app.get('/', function(req, res){
res.format({
html: function(){
@@ -20,22 +21,24 @@ app.get('/', function(req, res){
json: function(){
res.json(users);
}
})
});
});
// or you could write a tiny middleware like
// this to abstract make things a bit more declarative:
// this to add a layer of abstraction
// and make things a bit more declarative:
function format(mod) {
var obj = require(mod);
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,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -7,14 +6,8 @@ var express = require('../../');
var app = module.exports = express();
// ignore GET /favicon.ico
app.use(express.favicon());
// pass a secret to cookieParser() for signed cookies
app.use(express.cookieParser('manny is cool'));
// add req.session cookie support
app.use(express.cookieSession());
app.use(cookieSession({ secret: 'manny is cool' }));
// do something with the session
app.use(count);
@@ -26,7 +19,8 @@ function count(req, res) {
res.send('viewed ' + n + ' times\n');
}
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express server listening on port 3000');
}
console.log('Express started on port 3000');
}

View File

@@ -1,31 +1,24 @@
/**
* Module dependencies.
*/
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(express.favicon());
var express = require('../../');
var app = module.exports = express();
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
// custom log format
if ('test' != process.env.NODE_ENV)
app.use(express.logger(':method :url'));
if ('test' != process.env.NODE_ENV) app.use(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(express.cookieParser('my secret here'));
app.use(cookieParser('my secret here'));
// parses json, x-www-form-urlencoded, and multipart/form-data
app.use(express.bodyParser());
// parses x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
app.get('/', function(req, res){
if (req.cookies.remember) {
@@ -48,7 +41,8 @@ app.post('/', function(req, res){
res.redirect('back');
});
if (!module.parent){
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
}

View File

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

View File

@@ -1,44 +1,36 @@
/**
* Module dependencies.
*/
var express = require('../../')
, app = module.exports = express();
var express = require('../../');
var 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>'
+ '<li>Download <a href="/files/CCTV大赛上海分赛区.txt">CCTV大赛上海分赛区.txt</a>.</li>'
+ '</ul>');
});
// /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
, path = __dirname + '/files/' + file;
var file = req.params.file;
var path = __dirname + '/files/' + file;
res.download(path);
});
// error handling middleware. Because it's
// below our routes, you will be able to
// "intercept" errors, otherwise Connect
// will respond with 500 "Internal Server Error".
app.use(function(err, req, res, next){
// special-case 404s,
// remember you could
// render a 404 template here
if (404 == err.status) {
res.download(path, function(err){
if (!err) return; // file sent
if (err && err.status !== 404) return next(err); // non-404 error
// file for download not found
res.statusCode = 404;
res.send('Cant find that file, sorry!');
} else {
next(err);
}
});
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
}

View File

@@ -0,0 +1,2 @@
Only for test.
The file name is faked.

View File

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

View File

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

View File

@@ -2,9 +2,10 @@
* Module dependencies.
*/
var express = require('../../')
, app = module.exports = express()
, silent = 'test' == process.env.NODE_ENV;
var express = require('../../');
var app = module.exports = express();
var logger = require('morgan');
var silent = 'test' == process.env.NODE_ENV;
// general config
app.set('views', __dirname + '/views');
@@ -17,22 +18,36 @@ 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(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.use(app.router);
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
// Since this is the last non-error-handling
// middleware use()d, we assume 404, as nothing else
@@ -44,7 +59,7 @@ app.use(app.router);
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.render('404', { url: req.url });
@@ -81,32 +96,9 @@ app.use(function(err, req, res, next){
res.render('500', { error: err });
});
// Routes
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!'));
});
/* 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

@@ -1,20 +1,13 @@
/**
* Module dependencies.
*/
var express = require('../../')
, app = module.exports = express()
, test = app.get('env') == 'test';
var express = require('../../');
var logger = require('morgan');
var app = module.exports = express();
var test = app.get('env') == 'test';
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);
if (!test) app.use(logger('dev'));
// error handling middleware have an arity of 4
// instead of the typical (req, res, next),
@@ -42,7 +35,13 @@ 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,6 +1,7 @@
var express = require('../..')
, app = express();
var express = require('../..');
var logger = require('morgan');
var app = express();
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
@@ -20,10 +21,10 @@ User.prototype.toJSON = function(){
return {
id: this.id,
name: this.name
}
};
};
app.use(express.logger('dev'));
app.use(logger('dev'));
// earlier on expose an object
// that we can tack properties on.
@@ -56,5 +57,8 @@ app.get('/user', function(req, res){
res.render('page');
});
app.listen(3000);
console.log('app listening on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,7 +1,7 @@
html
head
title Express
script
script.
// call this whatever you like,
// or dump them into individual
// props like "var user ="
@@ -10,5 +10,5 @@ html
h1 Expose client data
p The following was exposed to the client:
pre
script
script.
document.write(JSON.stringify(data, null, 2))

View File

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

View File

@@ -1,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -12,9 +11,7 @@ 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
@@ -41,5 +38,14 @@ app.get('/', function(req, res){
res.render('users', { users: users });
});
app.listen(3000);
console.log('Express app started on port 3000');
// 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);
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,5 +1,5 @@
!!! 5
doctype html
html
include header
body
block content
block content

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
/**
* Module dependencies.
*/
var db = require('../../db');
exports.engine = 'jade';
exports.before = function(req, res, next){
var pet = db.pets[req.params.pet_id];
if (!pet) return next(new Error('Pet not found'));
if (!pet) return next('route');
req.pet = pet;
next();
};
@@ -20,7 +21,7 @@ exports.edit = function(req, res, next){
exports.update = function(req, res, next){
var body = req.body;
req.pet.name = body.user.name;
req.pet.name = body.pet.name;
res.message('Information updated!');
res.redirect('/pet/' + req.pet.id);
};

View File

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

View File

@@ -1,3 +1,6 @@
/**
* Module dependencies.
*/
var db = require('../../db');
@@ -8,7 +11,7 @@ exports.create = function(req, res, next){
var id = req.params.user_id;
var user = db.users[id];
var body = req.body;
if (!user) return next(new Error('User not found'));
if (!user) return next('route');
var pet = { name: body.pet.name };
pet.id = db.pets.push(pet) - 1;
user.pets.push(pet);

View File

@@ -1,3 +1,6 @@
/**
* Module dependencies.
*/
var db = require('../../db');
@@ -8,11 +11,11 @@ exports.before = function(req, res, next){
process.nextTick(function(){
req.user = db.users[id];
// cant find that user
if (!req.user) return next(new Error('User not found'));
if (!req.user) return next('route');
// found it, move on to the routes
next();
});
}
};
exports.list = function(req, res, next){
res.render('list', { users: db.users });

View File

@@ -1,12 +0,0 @@
<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

@@ -0,0 +1,11 @@
link(rel='stylesheet', href='/style.css')
h1= user.name
form(action='/user/#{user.id}?_method=put', method='post')
label= 'Name: '
input(type='text', name='user[name]', value='#{user.name}')
input(type='submit', value='Update')
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

@@ -1,8 +0,0 @@
<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

@@ -0,0 +1,7 @@
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

@@ -1,21 +0,0 @@
<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

@@ -0,0 +1,17 @@
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,4 +1,3 @@
// faux database
var pets = exports.pets = [];

View File

@@ -1,14 +1,20 @@
/**
* Module dependencies.
*/
var express = require('../..');
var logger = require('morgan');
var session = require('express-session');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
var app = module.exports = express();
// settings
// map .renderFile to ".html" files
app.engine('html', require('ejs').renderFile);
// make ".html" the default
app.set('view engine', 'html');
// set our default template engine to "jade"
// which prevents the need for extensions
app.set('view engine', 'jade');
// set views for error and 404 pages
app.set('views', __dirname + '/views');
@@ -25,20 +31,19 @@ app.response.message = function(msg){
};
// log
if (!module.parent) app.use(express.logger('dev'));
if (!module.parent) app.use(logger('dev'));
// serve static files
app.use(express.static(__dirname + '/public'));
// session support
app.use(express.cookieParser('some secret here'));
app.use(express.session());
app.use(session({ secret: 'some secret here' }));
// parse request bodies (req.body)
app.use(express.bodyParser());
app.use(bodyParser.urlencoded({ extended: true }));
// support _method (PUT in forms etc)
app.use(express.methodOverride());
// allow overriding methods in query (?_method=put)
app.use(methodOverride('_method'));
// expose the "messages" local variable when views are rendered
app.use(function(req, res, next){
@@ -66,16 +71,9 @@ app.use(function(req, res, next){
// load controllers
require('./lib/boot')(app, { verbose: !module.parent });
// assume "not found" in the error msgs
// is a 404. this is somewhat silly, but
// valid, you can do whatever you like, set
// properties, use instanceof etc.
app.use(function(err, req, res, next){
// treat as 404
if (~err.message.indexOf('not found')) return next();
// log it
console.error(err.stack);
if (!module.parent) console.error(err.stack);
// error page
res.status(500).render('5xx');
@@ -86,7 +84,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,32 +1,26 @@
/**
* Module dependencies.
*/
var express = require('../../..')
, fs = require('fs');
var express = require('../../..');
var 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)
, name = obj.name || name
, prefix = obj.prefix || ''
, app = express()
, method
, path;
var obj = require('./../controllers/' + name);
var name = obj.name || name;
var prefix = obj.prefix || '';
var app = express();
var handler;
var method;
var path;
// allow specifying the view engine
if (obj.engine) app.set('view engine', obj.engine);
app.set('views', __dirname + '/../controllers/' + name + '/views');
// before middleware support
if (obj.before) {
path = '/' + name + '/:' + name + '_id';
app.all(path, obj.before);
verbose && console.log(' ALL %s -> before', path);
path = '/' + name + '/:' + name + '_id/*';
app.all(path, obj.before);
verbose && console.log(' ALL %s -> before', path);
}
// generate routes based
// on the exported methods
for (var key in obj) {
@@ -59,15 +53,25 @@ module.exports = function(parent, options){
path = '/';
break;
default:
/* istanbul ignore next */
throw new Error('unrecognized route: ' + name + '.' + key);
}
// setup
handler = obj[key];
path = prefix + path;
app[method](path, obj[key]);
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
// before middleware support
if (obj.before) {
app[method](path, obj.before, handler);
verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), path, key);
} else {
app[method](path, obj[key]);
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
}
}
// mount the app
parent.use(app);
});
};
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,9 @@
/**
* Module dependencies.
*/
var express = require('../../')
, app = module.exports = express();
var express = require('../../');
var app = module.exports = express();
// Faux database
@@ -18,9 +17,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();
@@ -58,13 +57,14 @@ app.get('/user/:user', function(req, res, next){
*/
app.get('/users/:from-:to', function(req, res, next){
var from = req.params.from
, to = req.params.to
, names = users.map(function(user){ return user.name; });
var from = req.params.from;
var to = req.params.to;
var 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,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -12,13 +11,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)
, b = parseInt(req.params.b, 10)
, format = req.params.format;
var a = parseInt(req.params.a, 10);
var b = parseInt(req.params.b, 10);
var 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
@@ -41,8 +43,7 @@ var User = {
show: function(req, res){
res.send(users[req.params.id] || { error: 'Cannot find user' });
},
destroy: function(req, res){
var id = req.params.id;
destroy: function(req, res, id){
var destroyed = id in users;
delete users[id];
res.send(destroyed ? 'destroyed' : 'Cannot find user');
@@ -82,10 +83,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,7 +1,12 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express')
, verbose = process.env.NODE_ENV != 'test'
, app = module.exports = express();
var express = require('../../lib/express');
var verbose = process.env.NODE_ENV != 'test';
var app = module.exports = express();
app.map = function(a, route){
route = route || '';
@@ -29,7 +34,7 @@ var users = {
res.send('user ' + req.params.uid);
},
del: function(req, res){
delete: function(req, res){
res.send('delete users');
}
};
@@ -39,7 +44,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);
}
};
@@ -47,17 +52,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,22 +1,24 @@
/**
* Module dependencies.
*/
var express = require('../..')
, app = express()
, site = require('./site')
, post = require('./post')
, user = require('./user');
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');
// Config
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(logger('dev'));
app.use(methodOverride('_method'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname + '/public'));
// General
@@ -36,5 +38,8 @@ app.put('/user/:id/edit', user.update);
app.get('/posts', post.list);
app.listen(3000);
console.log('Express app started on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

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

View File

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

View File

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

View File

@@ -3,8 +3,7 @@ extends ../layout
block content
h1 Editing #{user.name}
#user
form(method="post")
input(type="hidden", value="put", name="_method")
form(action="?_method=put", method="post")
p Name:
input(type="text", value= user.name, name="user[name]")
p Email:

View File

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

View File

@@ -1,4 +1,3 @@
// first:
// $ npm install redis
// $ redis-server
@@ -7,9 +6,10 @@
* Module dependencies.
*/
var express = require('../..')
, redis = require('redis')
, db = redis.createClient();
var express = require('../..');
var redis = require('redis');
var 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,8 +1,8 @@
!!! 5
doctype
html
head
title Search example
style
style.
body {
font: 14px "Helvetica Neue", Helvetica;
padding: 50px;

View File

@@ -1,21 +1,14 @@
// first:
// $ npm install redis
// $ redis-server
var express = require('../..');
var session = require('express-session');
var app = express();
app.use(express.logger('dev'));
// Required by session() middleware
// pass the secret for signed cookies
// (required by session())
app.use(express.cookieParser('keyboard cat'));
// Populates req.session
app.use(express.session());
app.use(session({ secret: 'keyboard cat' }));
app.get('/', function(req, res){
var body = '';
@@ -28,5 +21,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,22 +1,21 @@
/**
* Module dependencies.
*/
var express = require('../..');
var logger = require('morgan');
var session = require('express-session');
// pass the express to the connect redis module
// allowing it to inherit from express.session.Store
var RedisStore = require('connect-redis')(express);
// allowing it to inherit from session.Store
var RedisStore = require('connect-redis')(session);
var app = express();
app.use(express.logger('dev'));
// Required by session() middleware
// pass the secret for signed cookies
// (required by session())
app.use(express.cookieParser('keyboard cat'));
app.use(logger('dev'));
// Populates req.session
app.use(express.session({ store: new RedisStore }));
app.use(session({ store: new RedisStore, secret: 'keyboard cat' }));
app.get('/', function(req, res){
var body = '';

View File

@@ -1,9 +1,13 @@
/**
* Module dependencies.
*/
var express = require('../..');
var logger = require('morgan');
var app = express();
// log requests
app.use(express.logger('dev'));
app.use(logger('dev'));
// express on its own has no notion
// of a "file". The express.static()
@@ -28,17 +32,9 @@ 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,6 +3,8 @@
*/
var express = require('../..');
var logger = require('morgan');
var vhost = require('vhost');
/*
edit /etc/hosts:
@@ -12,35 +14,38 @@ edit /etc/hosts:
127.0.0.1 example.com
*/
// Main app
// Main server app
var main = express();
main.use(express.logger('dev'));
if (!module.parent) main.use(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){
res.send('requsted ' + req.params.sub);
res.send('requested ' + req.params.sub);
});
// Redirect app
var redirect = express();
redirect.all('*', function(req, res){
console.log(req.subdomains);
res.redirect('http://example.com:3000/' + req.subdomains[0]);
redirect.use(function(req, res){
if (!module.parent) console.log(req.vhost);
res.redirect('http://example.com:3000/' + req.vhost[0]);
});
// Main app
// Vhost app
var app = express();
var app = module.exports = express();
app.use(express.vhost('*.example.com', redirect))
app.use(express.vhost('example.com', main));
app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app
app.use(vhost('example.com', main)); // Serves top level domain via Main server app
app.listen(3000);
console.log('Express app started on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,11 +1,10 @@
/**
* Module dependencies.
*/
var http = require('http')
, path = require('path')
, extname = path.extname
var http = require('http');
var path = require('path');
var extname = path.extname;
/**
* Expose `GithubView`.

View File

@@ -1,12 +1,11 @@
/**
* Module dependencies.
*/
var express = require('../../')
, http = require('http')
, GithubView = require('./github-view')
, md = require('marked').parse;
var express = require('../../');
var http = require('http');
var GithubView = require('./github-view');
var md = require('marked').parse;
var app = module.exports = express();
@@ -16,12 +15,12 @@ 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');
@@ -34,13 +33,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
res.render('Readme.md');
})
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1,7 +1,10 @@
/**
* Module dependencies.
*/
var express = require('../..')
, User = require('./user')
, app = express();
var express = require('../..');
var User = require('./user');
var app = express();
app.set('views', __dirname);
app.set('view engine', 'jade');
@@ -69,7 +72,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
@@ -142,5 +145,8 @@ app.all('/api/*', function(req, res, next){
*/
app.listen(3000);
console.log('Application listening on port 3000');
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,8 +1,8 @@
doctype 5
doctype html
html
head
title= title
style
style.
body {
padding: 50px;
font: 16px Helvetica, Arial;

View File

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

View File

@@ -1,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -40,29 +39,6 @@ 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
@@ -102,14 +78,33 @@ app.get('/api/repos', function(req, res, next){
});
app.get('/api/user/:name/repos', function(req, res, next){
var name = req.params.name
, user = userRepos[name];
var name = req.params.name;
var 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,17 +2,19 @@
* Module dependencies.
*/
var connect = require('connect')
, Router = require('./router')
, methods = require('methods')
, middleware = require('./middleware')
, debug = require('debug')('express:application')
, locals = require('./utils').locals
, View = require('./view')
, utils = connect.utils
, path = require('path')
, http = require('http')
, join = path.join;
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 compileETag = require('./utils').compileETag;
var compileTrust = require('./utils').compileTrust;
var deprecate = require('./utils').deprecate;
var resolve = require('path').resolve;
/**
* Application prototype.
@@ -46,13 +48,13 @@ app.init = function(){
app.defaultConfiguration = function(){
// default settings
this.enable('x-powered-by');
this.set('env', process.env.NODE_ENV || 'development');
this.set('etag', 'weak');
var env = process.env.NODE_ENV || 'development';
this.set('env', env);
this.set('subdomain offset', 2);
debug('booting in %s mode', this.get('env'));
this.set('trust proxy', false);
// implicit middleware
this.use(connect.query());
this.use(middleware.init(this));
debug('booting in %s mode', env);
// inherit protos
this.on('mount', function(parent){
@@ -62,62 +64,128 @@ app.defaultConfiguration = function(){
this.settings.__proto__ = parent.settings;
});
// 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;
});
// setup locals
this.locals = locals(this);
this.locals = Object.create(null);
// top-most app is mounted at /
this.mountpath = '/';
// default locals
this.locals.settings = this.settings;
// default configuration
this.set('view', View);
this.set('views', process.cwd() + '/views');
this.set('views', resolve('views'));
this.set('jsonp callback name', 'callback');
this.configure('development', function(){
this.set('json spaces', 2);
});
this.configure('production', function(){
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.');
}
});
};
/**
* Proxy `connect#use()` to apply settings to
* mounted applications.
* 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.
*
* @param {String|Function|Server} route
* @param {Function|Server} fn
* @return {app} for chaining
* @api public
*/
app.use = function(route, fn){
var app;
var mount_app;
// default route to '/'
if ('string' != typeof route) fn = route, route = '/';
// express app
if (fn.handle && fn.set) app = fn;
if (fn.handle && fn.set) mount_app = fn;
// restore .app property on req and res
if (app) {
app.route = route;
if (mount_app) {
debug('.use app under %s', route);
mount_app.mountpath = route;
fn = function(req, res, next) {
var orig = req.app;
app.handle(req, res, function(err){
req.app = res.app = orig;
mount_app.handle(req, res, function(err) {
req.__proto__ = orig.request;
res.__proto__ = orig.response;
next(err);
@@ -125,17 +193,33 @@ app.use = function(route, fn){
};
}
connect.proto.use.call(this, route, fn);
this.lazyrouter();
this._router.use(route, fn);
// mounted an app
if (app) {
app.parent = this;
app.emit('mount', this);
if (mount_app) {
mount_app.parent = this;
mount_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`.
@@ -178,30 +262,10 @@ app.engine = function(ext, fn){
};
/**
* Map the given param placeholder `name`(s) to the given callback(s).
* Proxy to `Router#param()` with one added api feature. The _name_ parameter
* can be an array of names.
*
* 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 samesignature as middleware, the only differencing
* 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'));
* }
* });
* });
* See the Router#param() docs for more details.
*
* @param {String|Array} name
* @param {Function} fn
@@ -210,27 +274,17 @@ app.engine = function(ext, fn){
*/
app.param = function(name, fn){
var self = this
, fns = [].slice.call(arguments, 1);
var self = this;
self.lazyrouter();
// array
if (Array.isArray(name)) {
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);
name.forEach(function(key) {
self.param(key, fn);
});
return this;
}
self._router.param(name, fn);
return this;
};
@@ -244,18 +298,33 @@ app.param = function(name, fn){
* Mounted servers inherit their parent server's settings.
*
* @param {String} setting
* @param {String} val
* @param {*} [val]
* @return {Server} for chaining
* @api public
*/
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;
};
/**
@@ -274,7 +343,7 @@ app.set = function(setting, val){
app.path = function(){
return this.parent
? this.parent.path() + this.route
? this.parent.path() + this.mountpath
: '';
};
@@ -340,60 +409,6 @@ 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;
};
/**
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
*/
@@ -402,16 +417,10 @@ methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
// deprecated
if (Array.isArray(path)) {
console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
}
this.lazyrouter();
// if no router attached yet, attach the router
if (!this._usedRouter) this.use(this.router);
// setup route
this._router[method].apply(this._router, arguments);
var route = this._router.route(path);
route[method].apply(route, [].slice.call(arguments, 1));
return this;
};
});
@@ -427,16 +436,20 @@ methods.forEach(function(method){
*/
app.all = function(path){
var args = arguments;
this.lazyrouter();
var route = this._router.route(path);
var args = [].slice.call(arguments, 1);
methods.forEach(function(method){
app[method].apply(this, args);
}, this);
route[method].apply(route, args);
});
return this;
};
// del -> delete alias
app.del = app.delete;
app.del = deprecate(app.delete, 'app.del: Use app.delete instead');
/**
* Render the given view `name` name with `options`
@@ -456,10 +469,10 @@ app.del = app.delete;
*/
app.render = function(name, options, fn){
var opts = {}
, cache = this.cache
, engines = this.engines
, view;
var opts = {};
var cache = this.cache;
var engines = this.engines;
var view;
// support callback function as second arg
if ('function' == typeof options) {
@@ -467,13 +480,13 @@ app.render = function(name, options, fn){
}
// merge app.locals
utils.merge(opts, this.locals);
mixin(opts, this.locals);
// merge options._locals
if (options._locals) utils.merge(opts, options._locals);
if (options._locals) mixin(opts, options._locals);
// merge options
utils.merge(opts, options);
mixin(opts, options);
// set .cache unless explicitly provided
opts.cache = null == opts.cache
@@ -492,7 +505,7 @@ app.render = function(name, options, fn){
});
if (!view.path) {
var err = new Error('Failed to lookup view "' + name + '"');
var err = new Error('Failed to lookup view "' + name + '" in views directory "' + view.root + '"');
err.view = view;
return fn(err);
}

View File

@@ -2,13 +2,13 @@
* Module dependencies.
*/
var connect = require('connect')
, proto = require('./application')
, Route = require('./router/route')
, Router = require('./router')
, req = require('./request')
, res = require('./response')
, utils = connect.utils;
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');
/**
* Expose `createApplication()`.
@@ -16,18 +16,6 @@ var connect = require('connect')
exports = module.exports = createApplication;
/**
* Framework version.
*/
exports.version = '3.2.5';
/**
* Expose mime.
*/
exports.mime = connect.mime;
/**
* Create an express application.
*
@@ -36,41 +24,19 @@ exports.mime = connect.mime;
*/
function createApplication() {
var app = connect();
utils.merge(app, proto);
app.request = { __proto__: req };
app.response = { __proto__: res };
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, proto);
mixin(app, EventEmitter.prototype);
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.
*/
for (var key in connect.middleware) {
Object.defineProperty(
exports
, key
, Object.getOwnPropertyDescriptor(connect.middleware, key));
}
/**
* Error on createServer().
*/
exports.createServer = function(){
console.warn('Warning: express.createServer() is deprecated, express');
console.warn('applications no longer inherit from http.Server,');
console.warn('please use:');
console.warn('');
console.warn(' var express = require("express");');
console.warn(' var app = express();');
console.warn('');
return createApplication();
};
/**
* Expose the prototypes.
*/
@@ -86,7 +52,42 @@ exports.response = res;
exports.Route = Route;
exports.Router = Router;
// Error handler title
/**
* Expose middleware
*/
exports.errorHandler.title = 'Express';
exports.query = require('./middleware/query');
exports.static = require('serve-static');
/**
* 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,10 +1,3 @@
/**
* Module dependencies.
*/
var utils = require('./utils');
/**
* Initialization middleware, exposing the
* request and response to eachother, as well
@@ -17,7 +10,6 @@ var utils = require('./utils');
exports.init = function(app){
return function expressInit(req, res, next){
req.app = res.app = app;
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
req.res = res;
res.req = req;
@@ -26,8 +18,9 @@ exports.init = function(app){
req.__proto__ = app.request;
res.__proto__ = app.response;
res.locals = res.locals || utils.locals(res);
res.locals = res.locals || Object.create(null);
next();
}
};
};

39
lib/middleware/query.js Normal file
View File

@@ -0,0 +1,39 @@
/**
* 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,15 +1,14 @@
/**
* Module dependencies.
*/
var http = require('http')
, utils = require('./utils')
, connect = require('connect')
, fresh = require('fresh')
, parseRange = require('range-parser')
, parse = connect.utils.parseUrl
, mime = connect.mime;
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 proxyaddr = require('proxy-addr');
/**
* Request prototype.
@@ -56,6 +55,8 @@ 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".
@@ -63,6 +64,7 @@ req.header = function(name){
* The `type` value may be a single mime type string
* such as "application/json", the extension name
* such as "json", a comma-delimted list such as "json, html, text/plain",
* an argument list such as `"json", "html", "text/plain"`,
* or an array `["json", "html", "text/plain"]`. When a list
* or array is given the _best_ match, if any is returned.
*
@@ -89,6 +91,7 @@ req.header = function(name){
*
* // Accept: text/*;q=.5, application/json
* req.accepts(['html', 'json']);
* req.accepts('html', 'json');
* req.accepts('html, json');
* // => "json"
*
@@ -97,8 +100,9 @@ req.header = function(name){
* @api public
*/
req.accepts = function(type){
return utils.accepts(type, this.get('Accept'));
req.accepts = function(){
var accept = accepts(this);
return accept.types.apply(accept, arguments);
};
/**
@@ -109,11 +113,15 @@ req.accepts = function(type){
* @api public
*/
req.acceptsEncoding = function(encoding){
return ~this.acceptedEncodings.indexOf(encoding);
req.acceptsEncoding = // backwards compatibility
req.acceptsEncodings = function(){
var accept = accepts(this);
return accept.encodings.apply(accept, arguments);
};
/**
* To do: update docs.
*
* Check if the given `charset` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
@@ -122,14 +130,15 @@ req.acceptsEncoding = function(encoding){
* @api public
*/
req.acceptsCharset = function(charset){
var accepted = this.acceptedCharsets;
return accepted.length
? ~accepted.indexOf(charset)
: true;
req.acceptsCharset = // backwards compatibility
req.acceptsCharsets = function(){
var accept = accepts(this);
return accept.charsets.apply(accept, arguments);
};
/**
* To do: update docs.
*
* Check if the given `lang` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
@@ -138,11 +147,10 @@ req.acceptsCharset = function(charset){
* @api public
*/
req.acceptsLanguage = function(lang){
var accepted = this.acceptedLanguages;
return accepted.length
? ~accepted.indexOf(lang)
: true;
req.acceptsLanguage = // backwards compatibility
req.acceptsLanguages = function(){
var accept = accepts(this);
return accept.languages.apply(accept, arguments);
};
/**
@@ -171,98 +179,6 @@ 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`.
*
@@ -272,7 +188,7 @@ req.__defineGetter__('acceptedCharsets', function(){
*
* To utilize request bodies, `req.body`
* should be an object. This can be done by using
* the `connect.bodyParser()` middleware.
* the `bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} [defaultValue]
@@ -316,40 +232,36 @@ req.param = function(name, defaultValue){
* @api public
*/
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);
req.is = function(types){
if (!Array.isArray(types)) types = [].slice.call(arguments);
return typeis(this, types);
};
/**
* Return the protocol string "http" or "https"
* when requested with TLS. When the "trust proxy"
* setting is enabled the "X-Forwarded-Proto" header
* field will be trusted. If you're running behind
* a reverse proxy that supplies https for you this
* may be enabled.
* setting trusts the socket address, the
* "X-Forwarded-Proto" header field will be trusted.
* If you're running behind a reverse proxy that
* supplies https for you this may be enabled.
*
* @return {String}
* @api public
*/
req.__defineGetter__('protocol', function(){
var trustProxy = this.app.get('trust proxy');
return this.connection.encrypted
? 'https'
: trustProxy
? (this.get('X-Forwarded-Proto') || 'http')
var trust = this.app.get('trust proxy fn');
if (!trust(this.connection.remoteAddress)) {
return this.connection.encrypted
? 'https'
: 'http';
}
// Note: X-Forwarded-Proto is normally only ever a
// single value, but this is to be safe.
var proto = this.get('X-Forwarded-Proto') || 'http';
return proto.split(/\s*,\s*/)[0];
});
/**
@@ -366,66 +278,36 @@ 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(/ *, */)
: [];
});
/**
* 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(){
// missing
var auth = this.get('Authorization');
if (!auth) return;
// malformed
var parts = auth.split(' ');
if ('basic' != parts[0].toLowerCase()) return;
if (!parts[1]) return;
auth = parts[1];
// credentials
auth = new Buffer(auth, 'base64').toString().match(/^([^:]*):(.*)$/);
if (!auth) return;
return { username: auth[1], password: auth[2] };
var trust = this.app.get('trust proxy fn');
var addrs = proxyaddr.all(this, trust);
return addrs.slice(1).reverse();
});
/**
@@ -465,16 +347,33 @@ 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;
return host.split(':')[0];
// IPv6 literal support
var offset = host[0] === '['
? host.indexOf(']') + 1
: 0;
var index = host.indexOf(':', offset);
return ~index
? host.substring(0, index)
: host;
});
/**

View File

@@ -2,21 +2,23 @@
* Module dependencies.
*/
var http = require('http')
, path = require('path')
, connect = require('connect')
, utils = connect.utils
, sign = require('cookie-signature').sign
, normalizeType = require('./utils').normalizeType
, normalizeTypes = require('./utils').normalizeTypes
, etag = require('./utils').etag
, statusCodes = http.STATUS_CODES
, cookie = require('cookie')
, send = require('send')
, mime = connect.mime
, basename = path.basename
, extname = path.extname
, join = path.join;
var escapeHtml = require('escape-html');
var http = require('http');
var path = require('path');
var mixin = require('utils-merge');
var sign = require('cookie-signature').sign;
var normalizeType = require('./utils').normalizeType;
var normalizeTypes = require('./utils').normalizeTypes;
var setCharset = require('./utils').setCharset;
var contentDisposition = require('./utils').contentDisposition;
var deprecate = require('./utils').deprecate;
var statusCodes = http.STATUS_CODES;
var cookie = require('cookie');
var send = require('send');
var basename = path.basename;
var extname = path.extname;
var mime = send.mime;
var vary = require('vary');
/**
* Response prototype.
@@ -55,7 +57,9 @@ res.status = function(code){
*/
res.links = function(links){
return this.set('Link', Object.keys(links).map(function(rel){
var link = this.get('Link') || '';
if (link) link += ', ';
return this.set('Link', link + Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
}).join(', '));
};
@@ -71,17 +75,19 @@ res.links = function(links){
* res.send(404, 'Sorry, cant find that');
* res.send(404);
*
* @param {Mixed} body or status
* @param {Mixed} body
* @return {ServerResponse}
* @api public
*/
res.send = function(body){
var req = this.req;
var head = 'HEAD' == req.method;
var type;
var encoding;
var len;
// settings
var app = this.app;
// allow status / body
if (2 == arguments.length) {
// res.send(body, status) backwards compat
@@ -102,10 +108,7 @@ res.send = function(body){
break;
// string defaulting to html
case 'string':
if (!this.get('Content-Type')) {
this.charset = this.charset || 'utf-8';
this.type('html');
}
if (!this.get('Content-Type')) this.type('html');
break;
case 'boolean':
case 'object':
@@ -119,18 +122,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 (len > 1024 && '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);
}
}
@@ -146,7 +162,8 @@ res.send = function(body){
}
// respond
this.end(head ? null : body);
this.end((head ? null : body), encoding);
return this;
};
@@ -160,9 +177,6 @@ res.send = function(body){
* res.json(500, 'oh noes!');
* res.json(404, 'I dont have that');
*
* @param {Mixed} obj or status
* @param {Mixed} obj
* @return {ServerResponse}
* @api public
*/
@@ -172,6 +186,9 @@ res.json = function(obj){
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
return 'number' === typeof obj
? jsonNumDeprecated.call(this, obj)
: jsonDeprecated.call(this, obj);
} else {
this.statusCode = obj;
obj = arguments[1];
@@ -185,12 +202,17 @@ 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);
};
var jsonDeprecated = deprecate(res.json,
'res.json(obj, status): Use res.json(status, obj) instead');
var jsonNumDeprecated = deprecate(res.json,
'res.json(num, status): Use res.status(status).json(num) instead');
/**
* Send JSON response with JSONP callback support.
*
@@ -201,9 +223,6 @@ res.json = function(obj){
* res.jsonp(500, 'oh noes!');
* res.jsonp(404, 'I dont have that');
*
* @param {Mixed} obj or status
* @param {Mixed} obj
* @return {ServerResponse}
* @api public
*/
@@ -213,6 +232,9 @@ res.jsonp = function(obj){
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
return 'number' === typeof obj
? jsonpNumDeprecated.call(this, obj)
: jsonpDeprecated.call(this, obj);
} else {
this.statusCode = obj;
obj = arguments[1];
@@ -229,19 +251,29 @@ res.jsonp = function(obj){
var callback = this.req.query[app.get('jsonp callback name')];
// content-type
this.charset = this.charset || 'utf-8';
this.set('Content-Type', 'application/json');
this.get('Content-Type') || this.set('Content-Type', 'application/json');
// fixup callback
if (Array.isArray(callback)) {
callback = callback[0];
}
// jsonp
if (callback) {
if (callback && 'string' === typeof callback) {
this.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = cb + ' && ' + cb + '(' + body + ');';
body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');';
}
return this.send(body);
};
var jsonpDeprecated = deprecate(res.json,
'res.jsonp(obj, status): Use res.jsonp(status, obj) instead');
var jsonpNumDeprecated = deprecate(res.json,
'res.jsonp(num, status): Use res.status(status).jsonp(num) instead');
/**
* Transfer the file at the given `path`.
*
@@ -255,6 +287,9 @@ res.jsonp = function(obj){
*
* - `maxAge` defaulting to 0
* - `root` root directory for relative filenames
* - `hidden` serve hidden files, defaulting to false
*
* Other options are passed along to `send`.
*
* Examples:
*
@@ -276,18 +311,16 @@ res.jsonp = function(obj){
* });
* });
*
* @param {String} path
* @param {Object|Function} options or fn
* @param {Function} fn
* @api public
*/
res.sendfile = function(path, options, fn){
var self = this
, req = self.req
, next = this.req.next
, options = options || {}
, done;
options = options || {};
var self = this;
var req = self.req;
var next = this.req.next;
var done;
// support function as second arg
if ('function' == typeof options) {
@@ -305,23 +338,23 @@ res.sendfile = function(path, options, fn){
// clean up
cleanup();
if (!self.headerSent) self.removeHeader('Content-Disposition');
if (!self.headersSent) self.removeHeader('Content-Disposition');
// callback available
if (fn) return fn(err);
// list in limbo if there's no callback
if (self.headerSent) return;
if (self.headersSent) return;
// delegate
next(err);
}
// streaming
function stream() {
function stream(stream) {
if (done) return;
cleanup();
if (fn) self.on('finish', fn);
if (fn) stream.on('end', fn);
}
// cleanup
@@ -329,10 +362,11 @@ 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);
if (options.root) file.root(options.root);
file.maxage(options.maxAge || 0);
var file = send(req, path, options);
file.on('error', error);
file.on('directory', next);
file.on('stream', stream);
@@ -346,13 +380,10 @@ res.sendfile = function(path, options, fn){
* Optionally providing an alternate attachment `filename`,
* and optional callback `fn(err)`. The callback is invoked
* when the data transfer is complete, or when an error has
* ocurred. Be sure to check `res.headerSent` if you plan to respond.
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
*
* This method uses `res.sendfile()`.
*
* @param {String} path
* @param {String|Function} filename or fn
* @param {Function} fn
* @api public
*/
@@ -364,7 +395,7 @@ res.download = function(path, filename, fn){
}
filename = filename || path;
this.set('Content-Disposition', 'attachment; filename="' + basename(filename) + '"');
this.set('Content-Disposition', contentDisposition(filename));
return this.sendfile(path, fn);
};
@@ -450,8 +481,8 @@ res.type = function(type){
*/
res.format = function(obj){
var req = this.req
, next = req.next;
var req = this.req;
var next = req.next;
var fn = obj.default;
if (fn) delete obj.default;
@@ -459,7 +490,7 @@ res.format = function(obj){
var key = req.accepts(keys);
this.set('Vary', 'Accept');
this.vary("Accept");
if (key) {
this.set('Content-Type', normalizeType(key).value);
@@ -486,9 +517,7 @@ res.format = function(obj){
res.attachment = function(filename){
if (filename) this.type(extname(filename));
this.set('Content-Disposition', filename
? 'attachment; filename="' + basename(filename) + '"'
: 'attachment');
this.set('Content-Disposition', contentDisposition(filename));
return this;
};
@@ -515,6 +544,10 @@ 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) {
@@ -548,7 +581,7 @@ res.get = function(field){
res.clearCookie = function(name, options){
var opts = { expires: new Date(1), path: '/' };
return this.cookie(name, '', options
? utils.merge(opts, options)
? mixin(opts, options)
: opts);
};
@@ -576,10 +609,10 @@ res.clearCookie = function(name, options){
*/
res.cookie = function(name, val, options){
options = utils.merge({}, options);
options = mixin({}, options);
var secret = this.req.secret;
var signed = options.signed;
if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
if (signed && !secret) throw new Error('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);
@@ -588,7 +621,18 @@ res.cookie = function(name, val, options){
options.maxAge /= 1000;
}
if (null == options.path) options.path = '/';
this.set('Set-Cookie', cookie.serialize(name, String(val), options));
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);
return this;
};
@@ -596,57 +640,24 @@ res.cookie = function(name, val, options){
/**
* Set the location header to `url`.
*
* The given `url` can also be the name of a mapped url, for
* example by default express supports "back" which redirects
* The given `url` can also be "back", which redirects
* to the _Referrer_ or _Referer_ headers or "/".
*
* Examples:
*
* res.location('/foo/bar').;
* res.location('http://example.com');
* 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');
* res.location('../login');
*
* @param {String} url
* @api public
*/
res.location = function(url){
var app = this.app
, req = this.req;
var req = this.req;
// setup redirect map
var map = { back: req.get('Referrer') || '/' };
// perform redirect
url = map[url] || url;
// relative
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
var path
// relative to path
if ('.' == url[0]) {
path = req.originalUrl.split('?')[0]
url = path + ('/' == path[path.length - 1] ? '' : '/') + url;
// relative to mount-point
} else if ('/' != url[0]) {
path = app.path();
url = path + '/' + url;
}
}
// "back" is an alias for the referrer
if ('back' == url) url = req.get('Referrer') || '/';
// Respond
this.set('Location', url);
@@ -675,10 +686,9 @@ res.location = function(url){
*/
res.redirect = function(url){
var app = this.app
, head = 'HEAD' == this.req.method
, status = 302
, body;
var head = 'HEAD' == this.req.method;
var status = 302;
var body;
// allow status / url
if (2 == arguments.length) {
@@ -701,7 +711,7 @@ res.redirect = function(url){
},
html: function(){
var u = utils.escape(url);
var u = escapeHtml(url);
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
},
@@ -716,6 +726,25 @@ res.redirect = function(url){
this.end(head ? null : body);
};
/**
* Add `field` to Vary. If already present in the Vary set, then
* this call is simply ignored.
*
* @param {Array|String} field
* @param {ServerResponse} for chaining
* @api public
*/
res.vary = function(field){
// checks for back-compat
if (!field) return this;
if (Array.isArray(field) && !field.length) return this;
vary(this, field);
return this;
};
/**
* Render `view` with the given `options` and optional callback `fn`.
* When a callback function is given a response will _not_ be made
@@ -726,17 +755,14 @@ res.redirect = function(url){
* - `cache` boolean hinting to the engine it should cache
* - `filename` filename of the view being rendered
*
* @param {String} view
* @param {Object|Function} options or callback function
* @param {Function} fn
* @api public
*/
res.render = function(view, options, fn){
var self = this
, options = options || {}
, req = this.req
, app = req.app;
options = options || {};
var self = this;
var req = this.req;
var app = req.app;
// support callback function as second arg
if ('function' == typeof options) {

View File

@@ -2,48 +2,75 @@
* Module dependencies.
*/
var Route = require('./route')
, utils = require('../utils')
, methods = require('methods')
, debug = require('debug')('express:router')
, parse = require('connect').utils.parseUrl;
/**
* Expose `Router` constructor.
*/
exports = module.exports = Router;
var Route = require('./route');
var Layer = require('./layer');
var methods = require('methods');
var debug = require('debug')('express:router');
var parseUrl = require('parseurl');
var slice = Array.prototype.slice;
/**
* Initialize a new `Router` with the given `options`.
*
* @param {Object} options
* @api private
*/
function Router(options) {
options = options || {};
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);
};
}
/**
* Register a param callback `fn` for the given `name`.
*
* @param {String|Function} name
* @param {Function} fn
* @return {Router} for chaining
* @return {Router} which is an callable function
* @api public
*/
Router.prototype.param = function(name, fn){
var proto = module.exports = function(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;
};
/**
* Map the given param placeholder `name`(s) to the given callback.
*
* 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 {Function} fn
* @return {app} for chaining
* @api public
*/
proto.param = function(name, fn){
// param logic
if ('function' == typeof name) {
this._params.push(name);
@@ -51,9 +78,13 @@ Router.prototype.param = function(name, fn){
}
// apply param functions
var params = this._params
, len = params.length
, ret;
var params = this._params;
var len = params.length;
var ret;
if (name[0] === ':') {
name = name.substr(1);
}
for (var i = 0; i < len; ++i) {
if (ret = params[i](name, fn)) {
@@ -72,202 +103,350 @@ Router.prototype.param = function(name, fn){
};
/**
* Route dispatcher aka the route "middleware".
* Dispatch a req, res into the router.
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @param {Function} next
* @api private
*/
Router.prototype._dispatch = function(req, res, next){
var params = this.params
, self = this;
proto.handle = function(req, res, done) {
var self = this;
debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
debug('dispatching %s %s', req.method, req.url);
// route dispatch
(function pass(i, err){
var paramCallbacks
, paramIndex = 0
, paramVal
, route
, keys
, key;
var method = req.method.toLowerCase();
// match next route
function nextRoute(err) {
pass(req._route_index + 1, err);
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;
var paramcalled = {};
// store options for OPTIONS request
// only used if OPTIONS request
var options = [];
// middleware and routes
var stack = self.stack;
// manage inter-router variables
var parent = req.next;
var parentUrl = req.baseUrl || '';
done = wrap(done, function(old, err) {
req.baseUrl = parentUrl;
req.next = parent;
old(err);
});
req.next = next;
// for options requests, respond with a default if nothing else responds
if (method === 'options') {
done = wrap(done, function(old, err) {
if (err || options.length === 0) return old(err);
var body = options.join(',');
return res.set('Allow', body).send(body);
});
}
next();
function next(err) {
if (err === 'route') {
err = undefined;
}
// match route
req.route = route = self.matchRequest(req, i);
var layer = stack[idx++];
var layerPath;
// no route
if (!route) return next(err);
debug('matched %s %s', route.method, route.path);
if (!layer) {
return done(err);
}
// we have a route
// start at param 0
req.params = route.params;
keys = route.keys;
i = 0;
if (slashAdded) {
req.url = req.url.substr(1);
slashAdded = false;
}
// param callbacks
function param(err) {
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
req.baseUrl = parentUrl;
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());
}
}
// Capture one-time layer values
req.params = layer.params;
layerPath = layer.path;
// this should be done for the layer
return self.process_params(layer, paramcalled, 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[layerPath.length];
if (c && '/' != c && '.' != c) return next(err);
// Trim off the part of the url that matches the route
// middleware (.use stuff) needs to have the path stripped
removed = layerPath;
if (removed.length) {
debug('trim prefix (%s) from url %s', layerPath, req.url);
req.url = protohost + req.url.substr(protohost.length + removed.length);
}
// Ensure leading slash
if (!fqdn && req.url[0] !== '/') {
req.url = '/' + req.url;
slashAdded = true;
}
// Setup base URL (no trailing slash)
if (removed.length && removed.substr(-1) === '/') {
req.baseUrl = parentUrl + removed.substring(0, removed.length - 1);
} else {
req.baseUrl = parentUrl + removed;
}
debug('%s %s : %s', layer.handle.name || 'anonymous', layerPath, req.originalUrl);
var arity = layer.handle.length;
try {
if ('route' == err) {
nextRoute();
} else if (err) {
i = 0;
callbacks(err);
} else if (paramCallbacks && undefined !== paramVal) {
paramCallback();
} else if (key) {
param();
if (err && arity === 4) {
layer.handle(err, req, res, next);
} else if (!err && arity < 4) {
layer.handle(req, res, next);
} else {
i = 0;
callbacks();
next(err);
}
} catch (err) {
param(err);
next(err);
}
}
}
function wrap(old, fn) {
return function () {
var args = [old].concat(slice.call(arguments));
fn.apply(this, args);
};
}
};
/**
* Process any parameters for the layer.
*
* @api private
*/
proto.process_params = function(layer, called, req, res, done) {
var params = this.params;
// captured parameters from the layer, keys and values
var keys = layer.keys;
// fast track
if (!keys || keys.length === 0) {
return done();
}
var i = 0;
var name;
var paramIndex = 0;
var key;
var paramVal;
var paramCallbacks;
var paramCalled;
// process params in order
// param callbacks can be async
function param(err) {
if (err) {
return done(err);
}
if (i >= keys.length ) {
return done();
}
paramIndex = 0;
key = keys[i++];
if (!key) {
return done();
}
name = key.name;
paramVal = req.params[name];
paramCallbacks = params[name];
paramCalled = called[name];
if (paramVal === undefined || !paramCallbacks) {
return param();
}
// param previously called with same value or error occurred
if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
// restore value
req.params[name] = paramCalled.value;
// next param
return param(paramCalled.error);
}
called[name] = paramCalled = {
error: null,
match: paramVal,
value: paramVal
};
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);
};
/**
* 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
*/
Router.prototype.matchRequest = function(req, i, head){
var method = req.method.toLowerCase()
, url = parse(req)
, path = url.pathname
, routes = this.map
, i = i || 0
, route;
// HEAD support
if (!head && 'head' == method) {
route = this.matchRequest(req, i, true);
if (route) return route;
method = 'get';
}
// routes for this method
if (routes = routes[method]) {
// matching routes
for (var len = routes.length; i < len; ++i) {
route = routes[i];
if (route.match(path)) {
req._route_index = i;
return route;
}
try {
return paramCallback();
} catch (err) {
return done(err);
}
}
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
// store updated value
paramCalled.value = req.params[key.name];
if (err) {
// store error
paramCalled.error = err;
param(err);
return;
}
if (!fn) return param();
fn(req, res, paramCallback, paramVal, key.name);
}
param();
};
/**
* Attempt to match a route for `method`
* and `url` with optional starting
* index of `i` defaulting to 0.
* Use the given middleware function, with optional path, defaulting to "/".
*
* @param {String} method
* @param {String} url
* @param {Number} i
* @return {Route}
* @api private
* 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
*/
Router.prototype.match = function(method, url, i, head){
var req = { method: method, url: url };
return this.matchRequest(req, i, head);
};
proto.use = function(route, fn){
// default route to '/'
if ('string' != typeof route) {
fn = route;
route = '/';
}
/**
* 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, i){
if ('function' == typeof fn) return;
if (typeof fn !== 'function') {
var type = {}.toString.call(fn);
var msg = '.' + method + '() requires callback functions but got a ' + type;
var msg = 'Router.use() requires callback functions but got a ' + type;
throw new Error(msg);
});
}
// create the route
debug('defined %s %s', method, path);
var route = new Route(method, path, callbacks, {
// strip trailing slash
if ('/' == route[route.length - 1]) {
route = route.slice(0, -1);
}
var layer = new Layer(route, {
sensitive: this.caseSensitive,
strict: this.strict
});
strict: this.strict,
end: false
}, fn);
// add it
(this.map[method] = this.map[method] || []).push(route);
// add the middleware
debug('use %s %s', route || '/', fn.name || 'anonymous');
this.stack.push(layer);
return this;
};
methods.forEach(function(method){
Router.prototype[method] = function(path){
var args = [method].concat([].slice.call(arguments));
this.route.apply(this, args);
/**
* 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;
};
// 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));
return this;
};
});

67
lib/router/layer.js Normal file
View File

@@ -0,0 +1,67 @@
/**
* 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,8 +1,9 @@
/**
* Module dependencies.
*/
var debug = require('debug')('express:router:route');
var methods = require('methods');
var utils = require('../utils');
/**
@@ -12,61 +13,179 @@ var utils = require('../utils');
module.exports = Route;
/**
* Initialize `Route` with the given HTTP `method`, `path`,
* and an array of `callbacks` and `options`.
* Initialize `Route` with the given `path`,
*
* 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(method, path, callbacks, options) {
options = options || {};
function Route(path) {
debug('new %s', path);
this.path = path;
this.method = method;
this.callbacks = callbacks;
this.regexp = utils.pathRegexp(path
, this.keys = []
, options.sensitive
, options.strict);
this.stack = undefined;
// route handlers for various http methods
this.methods = {};
}
/**
* Check if this route matches `path`, if so
* populate `.params`.
*
* @param {String} path
* @return {Boolean}
* @return {Array} supported HTTP methods
* @api private
*/
Route.prototype.match = function(path){
var keys = this.keys
, params = this.params = []
, m = this.regexp.exec(path);
Route.prototype._options = function(){
return Object.keys(this.methods).map(function(method) {
return method.toUpperCase();
});
};
if (!m) return false;
/**
* dispatch req, res into this route
*
* @api private
*/
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
Route.prototype.dispatch = function(req, res, done){
var self = this;
var method = req.method.toLowerCase();
var val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
: m[i];
if (key) {
params[key.name] = val;
} else {
params.push(val);
}
if (method === 'head' && !this.methods['head']) {
method = 'get';
}
return true;
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();
}
try {
layer.handle(req, res, next_layer);
} catch (err) {
next_layer(err);
}
})();
};
/**
* 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,46 +1,85 @@
/**
* Module dependencies.
*/
var mime = require('connect').mime
, crc32 = require('buffer-crc32');
var mime = require('send').mime;
var crc32 = require('buffer-crc32');
var crypto = require('crypto');
var basename = require('path').basename;
var deprecate = require('util').deprecate;
var proxyaddr = require('proxy-addr');
/**
* toString ref.
* Simple detection of charset parameter in content-type
*/
var toString = {}.toString;
var charsetRegExp = /;\s*charset\s*=/;
/**
* Return ETag for `body`.
* Deprecate function, like core `util.deprecate`,
* but with NODE_ENV and color support.
*
* @param {String|Buffer} body
* @return {String}
* @api private
*/
exports.etag = function(body){
return '"' + crc32.signed(body) + '"';
};
/**
* Make `locals()` bound to the given `obj`.
*
* This is used for `app.locals` and `res.locals`.
*
* @param {Object} obj
* @param {Function} fn
* @param {String} msg
* @return {Function}
* @api private
*/
exports.locals = function(obj){
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
};
exports.deprecate = function(fn, msg){
if (process.env.NODE_ENV === 'test') return fn;
return locals;
// prepend module name
msg = 'express: ' + msg;
if (process.stderr.isTTY) {
// colorize
msg = '\x1b[31;1m' + msg + '\x1b[0m';
}
return deprecate(fn, msg);
};
/**
* Return strong ETag for `body`.
*
* @param {String|Buffer} body
* @param {String} [encoding]
* @return {String}
* @api private
*/
exports.etag = function etag(body, encoding){
if (body.length === 0) {
// fast-path empty body
return '"1B2M2Y8AsgTpgAmY7PhCfg=="'
}
var hash = crypto
.createHash('md5')
.update(body, encoding)
.digest('base64')
return '"' + hash + '"'
};
/**
* Return weak ETag for `body`.
*
* @param {String|Buffer} body
* @param {String} [encoding]
* @return {String}
* @api private
*/
exports.wetag = function wetag(body, encoding){
if (body.length === 0) {
// fast-path empty body
return 'W/"0-0"'
}
var buf = Buffer.isBuffer(body)
? body
: new Buffer(body, encoding)
var len = buf.length
return 'W/"' + len.toString(16) + '-' + crc32.unsigned(buf) + '"'
};
/**
@@ -54,6 +93,7 @@ exports.locals = function(obj){
exports.isAbsolute = function(path){
if ('/' == path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true;
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
};
/**
@@ -65,8 +105,8 @@ exports.isAbsolute = function(path){
*/
exports.flatten = function(arr, ret){
var ret = ret || []
, len = arr.length;
ret = ret || [];
var len = arr.length;
for (var i = 0; i < len; ++i) {
if (Array.isArray(arr[i])) {
exports.flatten(arr[i], ret);
@@ -110,125 +150,25 @@ exports.normalizeTypes = function(types){
};
/**
* Return the acceptable type in `types`, if any.
* Generate Content-Disposition header appropriate for the filename.
* non-ascii filenames are urlencoded and a filename* parameter is added
*
* @param {Array} types
* @param {String} str
* @param {String} filename
* @return {String}
* @api private
*/
exports.acceptsArray = function(types, str){
// accept anything when Accept is not present
if (!str) return types[0];
// 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];
}
}
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 + '"';
}
};
/**
* 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;
}
});
return ret;
};
/**
@@ -258,56 +198,95 @@ function acceptParams(str, index) {
}
/**
* Escape special characters in the given string of html.
* Compile "etag" value to function.
*
* @param {String} html
* @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.escape = function(html) {
return String(html)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
exports.setCharset = function(type, charset){
if (!type || !charset) return type;
var exists = charsetRegExp.test(type);
// removing existing charset
if (exists) {
var parts = type.split(';');
for (var i = 1; i < parts.length; i++) {
if (charsetRegExp.test(';' + parts[i])) {
parts.splice(i, 1);
break;
}
}
type = parts.join(';');
}
return type + '; charset=' + charset;
};
/**
* 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');
}

View File

@@ -2,14 +2,14 @@
* Module dependencies.
*/
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;
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;
/**
* Expose `View`.

View File

@@ -1,50 +1,16 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.2.5",
"version": "4.4.5",
"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"
}
"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": {
"connect": "2.7.10",
"commander": "0.6.1",
"range-parser": "0.0.4",
"mkdirp": "0.3.4",
"cookie": "0.1.0",
"buffer-crc32": "0.2.1",
"fresh": "0.1.0",
"methods": "0.0.1",
"send": "0.1.0",
"cookie-signature": "1.0.1",
"debug": "*"
},
"devDependencies": {
"ejs": "*",
"mocha": "*",
"jade": "*",
"hjs": "*",
"stylus": "*",
"should": "*",
"connect-redis": "*",
"marked": "*",
"supertest": "0.6.0"
},
"keywords": [
"express",
"framework",
@@ -56,16 +22,55 @@
"app",
"api"
],
"repository": "git://github.com/visionmedia/express",
"main": "index",
"bin": {
"express": "./bin/express"
"repository": "visionmedia/express",
"license": "MIT",
"dependencies": {
"accepts": "~1.0.5",
"buffer-crc32": "0.2.3",
"debug": "1.0.2",
"escape-html": "1.0.1",
"methods": "1.0.1",
"parseurl": "1.0.1",
"proxy-addr": "1.0.1",
"range-parser": "1.0.0",
"send": "0.4.3",
"serve-static": "1.2.3",
"type-is": "1.2.1",
"vary": "0.1.0",
"cookie": "0.1.2",
"fresh": "0.2.2",
"cookie-signature": "1.0.4",
"merge-descriptors": "0.0.2",
"utils-merge": "1.0.0",
"qs": "0.6.6",
"path-to-regexp": "0.1.2"
},
"devDependencies": {
"after": "0.8.1",
"istanbul": "0.2.10",
"mocha": "~1.20.1",
"should": "~4.0.4",
"supertest": "~0.13.0",
"connect-redis": "~2.0.0",
"ejs": "~1.0.0",
"jade": "~1.3.1",
"marked": "0.3.2",
"multiparty": "~3.2.4",
"hjs": "~0.0.6",
"body-parser": "~1.4.3",
"cookie-parser": "~1.3.1",
"express-session": "~1.5.0",
"method-override": "2.0.2",
"morgan": "1.1.1",
"vhost": "2.0.0"
},
"engines": {
"node": ">= 0.10.0"
},
"scripts": {
"prepublish": "npm prune",
"test": "make test"
},
"engines": {
"node": "*"
"test": "mocha --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/"
}
}

View File

@@ -9,7 +9,6 @@ var app = express()
, blog = express()
, admin = express();
// app.use(express.logger('dev'))
blog.use('/admin', admin);
app.use('/blog', blog);
app.set('views', __dirname + '/views');

View File

@@ -1,32 +0,0 @@
#!/usr/bin/env bash
NODE_ENV=production node ./support/app &
pid=$!
bench() {
ab -n 5000 -c 50 -k -q http://127.0.0.1:8000$1 \
| grep "Requests per" \
| cut -d ' ' -f 7 \
| xargs echo "$2:"
}
bench_conditional() {
ab -n 5000 -c 50 -H "If-None-Match: $3" -k -q http://127.0.0.1:8000$1 \
| grep "Requests per" \
| cut -d ' ' -f 7 \
| xargs echo "$2:"
}
sleep .5
bench / "Hello World"
bench /blog "Mounted Hello World"
bench /blog/admin "Mounted 2 Hello World"
bench /middleware "Middleware"
bench /match "Router"
bench /render "Render"
bench /json "JSON tiny"
bench /json/15 "JSON small"
bench /json/50 "JSON medium"
bench /json/150 "JSON large"
kill -9 $pid

View File

@@ -1,21 +0,0 @@
#!/usr/bin/env node
var buf = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(chunk){
buf += chunk;
}).on('end', function(){
var comments = JSON.parse(buf);
comments.forEach(function(comment){
if (comment.ignore) return;
if (comment.isPrivate) return;
if (!comment.ctx) return;
if (!comment.description.full.indexOf('Module dep')) return;
var ctx = comment.ctx;
console.log();
console.log('# %s', ctx.string);
console.log();
console.log(comment.description.full.trim().replace(/^/gm, ' '));
});
console.log();
}).resume();

190
test/Route.js Normal file
View File

@@ -0,0 +1,190 @@
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' }, {});
});
it('should handle throwing inside error handlers', function(done) {
var route = new Route('');
route.get(function(req, res, next){
throw new Error('boom!');
});
route.get(function(err, req, res, next){
throw new Error('oops');
});
route.get(function(err, req, res, next){
assert.equal(err.message, 'oops');
done();
});
route.dispatch({ url: '/', method: 'GET' }, {}, done);
});
})
})

View File

@@ -1,103 +1,321 @@
var after = require('after');
var express = require('../')
, Router = express.Router
, request = require('./support/http')
, methods = require('methods')
, assert = require('assert');
describe('Router', function(){
var router, app;
it('should return a function with router methods', function() {
var router = Router();
assert(typeof router == 'function');
beforeEach(function(){
router = new Router;
app = express();
})
var router = new Router();
assert(typeof router == '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(){});
assert(typeof router.get == 'function');
assert(typeof router.handle == 'function');
assert(typeof router.use == 'function');
});
var method = 'GET';
var url = '/foo?bar=baz';
it('should support .use of other routers', function(done){
var router = new Router();
var another = new Router();
var route = router.match(method, url, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
another.get('/bar', function(req, res){
res.end();
});
router.use('/foo', another);
var route = router.match(method, url, 1);
route.path.should.equal('/foob?');
router.handle({ url: '/foo/bar', method: 'GET' }, { end: done });
});
var route = router.match(method, url, 2);
assert(!route);
it('should support dynamic routes', function(done){
var router = new Router();
var another = new Router();
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' };
another.get('/:bar', function(req, res){
req.params.bar.should.equal('route');
res.end();
});
router.use('/:foo', another);
var route = router.matchRequest(req, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
router.handle({ url: '/test/route', method: 'GET' }, { end: done });
});
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(){
describe('.handle', function(){
it('should dispatch', function(done){
router.route('get', '/foo', function(req, res){
var router = new Router();
router.route('/foo').get(function(req, res){
res.send('foo');
});
app.use(router.middleware);
request(app)
.get('/foo')
.expect('foo', done);
var res = {
send: function(val) {
val.should.equal('foo');
done();
}
}
router.handle({ url: '/foo', method: 'GET' }, res);
})
})
describe('.multiple callbacks', function(){
it('should throw if a callback is null', function(){
assert.throws(function () {
router.route('get', '/foo', null, function(){});
var router = new Router();
router.route('/foo').all(null);
})
})
it('should throw if a callback is undefined', function(){
assert.throws(function () {
router.route('get', '/foo', undefined, function(){});
var router = new Router();
router.route('/foo').all(undefined);
})
})
it('should throw if a callback is not a function', function(){
assert.throws(function () {
router.route('get', '/foo', 'not a function', function(){});
var router = new Router();
router.route('/foo').all('not a function');
})
})
it('should not throw if all callbacks are functions', function(){
router.route('get', '/foo', function(){}, function(){});
var router = new Router();
router.route('/foo').all(function(){}).all(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' }, {}, function() {});
});
it('should handle throwing in handler after async param', function(done) {
var router = new Router();
router.param('user', function(req, res, next, val){
process.nextTick(function(){
req.user = val;
next();
});
});
router.use('/:user', function(req, res, next){
throw new Error('oh no!');
});
router.use(function(err, req, res, next){
assert.equal(err.message, 'oh no!');
done();
});
router.handle({ url: '/bob', method: 'GET' }, {}, function() {});
});
it('should handle throwing inside error handlers', function(done) {
var router = new Router();
router.use(function(req, res, next){
throw new Error('boom!');
});
router.use(function(err, req, res, next){
throw new Error('oops');
});
router.use(function(err, req, res, next){
assert.equal(err.message, 'oops');
done();
});
router.handle({ url: '/', method: 'GET' }, {}, done);
});
})
describe('.all', function() {
it('should support using .all to capture all http verbs', function(done){
var router = new Router();
var count = 0;
router.all('/foo', function(){ count++; });
var url = '/foo?bar=baz';
methods.forEach(function testMethod(method) {
router.handle({ url: url, method: method }, {}, function() {});
});
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);
});
it('should only call once per request', function(done) {
var count = 0;
var req = { url: '/foo/bob/bar', method: 'get' };
var router = new Router();
var sub = new Router();
sub.get('/bar', function(req, res, next) {
next();
});
router.param('user', function(req, res, next, user) {
count++;
req.user = user;
next();
});
router.use('/foo/:user/', new Router());
router.use('/foo/:user/', sub);
router.handle(req, {}, function(err) {
if (err) return done(err);
assert.equal(count, 1);
assert.equal(req.user, 'bob');
done();
});
});
it('should call when values differ', function(done) {
var count = 0;
var req = { url: '/foo/bob/bar', method: 'get' };
var router = new Router();
var sub = new Router();
sub.get('/bar', function(req, res, next) {
next();
});
router.param('user', function(req, res, next, user) {
count++;
req.user = user;
next();
});
router.use('/foo/:user/', new Router());
router.use('/:user/bob/', sub);
router.handle(req, {}, function(err) {
if (err) return done(err);
assert.equal(count, 2);
assert.equal(req.user, 'foo');
done();
});
});
});
describe('parallel requests', function() {
it('should not mix requests', function(done) {
var req1 = { url: '/foo/50/bar', method: 'get' };
var req2 = { url: '/foo/10/bar', method: 'get' };
var router = new Router();
var sub = new Router();
done = after(2, done);
sub.get('/bar', function(req, res, next) {
next();
});
router.param('ms', function(req, res, next, ms) {
ms = parseInt(ms, 10);
req.ms = ms;
setTimeout(next, ms);
});
router.use('/foo/:ms/', new Router());
router.use('/foo/:ms/', sub);
router.handle(req1, {}, function(err) {
assert.ifError(err);
assert.equal(req1.ms, 50);
assert.equal(req1.originalUrl, '/foo/50/bar');
done();
});
router.handle(req2, {}, function(err) {
assert.ifError(err);
assert.equal(req2.ms, 10);
assert.equal(req2.originalUrl, '/foo/10/bar');
done();
});
});
});
})

View File

@@ -1,13 +1,5 @@
var app = require('../../examples/auth/app')
, request = require('../support/http');
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

@@ -1,5 +1,5 @@
var request = require('../support/http')
var request = require('supertest')
, app = require('../../examples/content-negotiation');
describe('content-negotiation', function(){
@@ -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

@@ -1,6 +1,6 @@
var app = require('../../examples/cookies/app')
, request = require('../support/http');
, request = require('supertest');
describe('cookies', function(){
describe('GET /', function(){
@@ -18,17 +18,59 @@ describe('cookies', function(){
done()
})
})
it('should respond to cookie', function(done){
request(app)
.post('/')
.type('urlencoded')
.send({ remember: 1 })
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/')
.set('Cookie', res.headers['set-cookie'][0])
.expect(200, /Remembered/, done)
})
})
})
describe('GET /forget', function(){
it('should clear cookie', function(done){
request(app)
.post('/')
.type('urlencoded')
.send({ remember: 1 })
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/forget')
.set('Cookie', res.headers['set-cookie'][0])
.expect('Set-Cookie', /remember=;/)
.expect(302, done)
})
})
})
describe('POST /', function(){
it('should set a cookie', function(done){
request(app)
.post('/')
.type('urlencoded')
.send({ remember: 1 })
.end(function(err, res){
.expect(302, function(err, res){
res.headers.should.have.property('set-cookie')
done()
})
})
it('should no set cookie w/o reminder', function(done){
request(app)
.post('/')
.send({})
.expect(302, function(err, res){
res.headers.should.not.have.property('set-cookie')
done()
})
})
})
})
})

View File

@@ -1,6 +1,6 @@
var app = require('../../examples/downloads/app')
, request = require('../support/http');
, request = require('supertest');
describe('downloads', function(){
describe('GET /', function(){
@@ -30,4 +30,4 @@ describe('downloads', function(){
.expect(404, done)
})
})
})
})

View File

@@ -1,5 +1,5 @@
var request = require('../support/http')
var request = require('supertest')
, app = require('../../examples/ejs');
describe('ejs', function(){
@@ -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/error-pages')
, request = require('../support/http');
, request = require('supertest');
describe('error-pages', function(){
describe('GET /', function(){

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