Compare commits

...

322 Commits

Author SHA1 Message Date
TJ Holowaychuk
2fe46b3905 Release 3.2.5 2013-05-21 21:01:24 -07:00
TJ Holowaychuk
24974f1f8f update connect 2013-05-21 20:56:08 -07:00
TJ Holowaychuk
e2210b0b92 Merge pull request #1625 from ForbesLindesay/patch-1
Throw a meaningful error when there is no default engine
2013-05-15 08:27:47 -07:00
Forbes Lindesay
30919be2a0 Throw a meaningful error when there is no default engine 2013-05-15 12:39:06 +01:00
TJ Holowaychuk
10b21b41f7 Revert "fix infinite loop when res.send(status) is undefined. Closes #1623"
This reverts commit 28b8a3b5f7.
2013-05-13 13:23:23 -07:00
TJ Holowaychuk
28b8a3b5f7 fix infinite loop when res.send(status) is undefined. Closes #1623 2013-05-13 13:22:31 -07:00
TJ Holowaychuk
c805d80a9b Merge pull request #1592 from bartsqueezy/eb1bbb9
Removing dependency which is no longer supported
2013-05-11 15:36:39 -07:00
TJ Holowaychuk
d876778d22 Merge pull request #1597 from Cauldrath/cookie_version
Version bump for node-cookie
2013-05-11 15:35:30 -07:00
TJ Holowaychuk
412e571600 Merge pull request #1618 from pwmckenna/patch-1
Flush messages exposed to locals *after* the view has the chance to proces...
2013-05-11 15:26:22 -07:00
TJ Holowaychuk
3296ed9cb3 change generation of ETags with res.send() to GET requests only. Closes #1619
if for some reason this is not ideal for your use-case please let me know and comment in the issue
2013-05-10 14:43:59 -07:00
Patrick Williams
91835e6816 Flush messages exposed to locals after the view has the chance to process them. 2013-05-10 09:05:37 -06:00
TJ Holowaychuk
f976625281 Release 3.2.4 2013-05-09 09:17:48 -07:00
TJ Holowaychuk
028d9d8a0c Merge pull request #1598 from colynb/patch-1
the file is hosts not vhosts
2013-05-09 09:12:54 -07:00
TJ Holowaychuk
8559c0e2a4 fix req.subdomains when no Host is present 2013-05-09 09:10:52 -07:00
TJ Holowaychuk
06ead58240 fix req.host when no Host is present, return undefined 2013-05-09 09:06:11 -07:00
TJ Holowaychuk
28ca1b5221 add req.host tests 2013-05-09 09:03:52 -07:00
TJ Holowaychuk
6d872e6693 remove qs dep 2013-05-07 07:58:54 -07:00
TJ Holowaychuk
0b09c8981f Release 3.2.3 2013-05-07 07:55:06 -07:00
TJ Holowaychuk
a1d5676ecb update connect / qs 2013-05-07 07:54:48 -07:00
TJ Holowaychuk
f862ad29f5 Release 3.2.2 2013-05-03 12:54:52 -07:00
TJ Holowaychuk
15496da8fd remove ./client.js 2013-05-03 12:54:28 -07:00
TJ Holowaychuk
802fb1632c update qs 2013-05-03 12:53:50 -07:00
colynb
69453ff889 the file is hosts not vhosts 2013-05-01 16:27:29 -07:00
Benjamin Hanes
28752cc3c0 Version bump for node-cookie 2013-05-01 15:25:14 -04:00
TJ Holowaychuk
9f06d9b03f Release 3.2.1 2013-04-29 19:17:08 -07:00
TJ Holowaychuk
3fb7c4e1db update connect 2013-04-29 19:16:33 -07:00
Steve Bartnesky
5fa685b602 removing github-flavored-markdown as a dependency as it is no longer supported. switch to use marked instead 2013-04-29 09:12:29 -05:00
Steve Bartnesky
eb1bbb92c0 removing github-flavored-markdown as a dependency as it is no longer supported. switch to use marked instead 2013-04-29 08:59:52 -05:00
TJ Holowaychuk
d0e49f1a8a update qs and remove all ~ semver crap 2013-04-26 13:12:33 -07:00
TJ Holowaychuk
ea2664a4b8 Merge branch 'master' of github.com:visionmedia/express 2013-04-25 16:29:50 -07:00
TJ Holowaychuk
da6524bd06 Merge pull request #1589 from hacksparrow/master
Signed cookies can now accept numbers as values, like unsigned cookies
2013-04-25 16:29:39 -07:00
TJ Holowaychuk
a231406931 Merge branch 'master' of github.com:visionmedia/express 2013-04-25 16:26:44 -07:00
Hack Sparrow
6d39ed8ef7 Accept number as value of Signed Cookie 2013-04-23 22:53:35 +05:30
TJ Holowaychuk
f2563f4dde Merge pull request #1586 from yields/master
removed some spaces from bin/express
2013-04-21 18:18:19 -07:00
Amir Abu Shareb
3df265b36a remove spaces when a session is enabled. 2013-04-21 15:41:42 +03:00
TJ Holowaychuk
e382e6adc7 update supertest dev dep 2013-04-16 06:49:56 -07:00
TJ Holowaychuk
91c71d6c2e add app.VERB() paths array deprecation warning 2013-04-15 15:18:28 -07:00
TJ Holowaychuk
0d40c65b7f Release 3.2.0 2013-04-15 12:34:41 -07:00
TJ Holowaychuk
58f2057ba7 revert cookie signature change causing session race conditions 2013-04-15 12:33:12 -07:00
TJ Holowaychuk
37179109db Revert "fix res.cookie() tests"
This reverts commit ed273448b9.
2013-04-15 12:29:42 -07:00
TJ Holowaychuk
579857cfaa fix example port 2013-04-13 10:14:23 -07:00
Caridy Patino
0b4e2df480 add "view" constructor setting to override view behaviour 2013-04-13 09:53:50 -07:00
TJ Holowaychuk
49cc1a70b1 Merge pull request #1571 from jlubawy/master
Change to crypto.pbkdf2 in Node v0.10 broke auth example
2013-04-13 09:35:11 -07:00
TJ Holowaychuk
f8a33d137a refactor 2013-04-13 09:16:15 -07:00
TJ Holowaychuk
2db135dfc7 Merge pull request #1566 from daguej/v8-context-fix
Possible fix for #1557
2013-04-13 09:14:41 -07:00
TJ Holowaychuk
99bc628ad1 fix long list params test 2013-04-13 09:07:48 -07:00
TJ Holowaychuk
5ba6c301d7 Merge pull request #1578 from Notificare/master
Correct sorting of long list of accept header
2013-04-13 09:06:09 -07:00
silentjohnny
88273a59f8 Added originalIndex to parseQuality to correctly sort long lists (v8 does unstable quicksort for length > 10) 2013-04-13 12:36:35 +02:00
TJ Holowaychuk
2e53cb72ec add req.acceptsEncoding(name) 2013-04-12 12:56:50 -07:00
TJ Holowaychuk
3b1597d79e add req.acceptedEncodings 2013-04-12 12:55:53 -07:00
TJ Holowaychuk
776ee26bc3 Release 3.1.2 2013-04-12 12:14:02 -07:00
TJ Holowaychuk
ed273448b9 fix res.cookie() tests 2013-04-12 12:13:12 -07:00
TJ Holowaychuk
4bb91b3f67 update connect 2013-04-12 12:10:48 -07:00
TJ Holowaychuk
c5f866098e update cookie-signature 2013-04-12 12:07:43 -07:00
TJ Holowaychuk
6cfd01be6b Merge branch 'master' of github.com:visionmedia/express 2013-04-11 08:42:05 -07:00
TJ Holowaychuk
53b8e25731 ocd 2013-04-11 08:36:52 -07:00
Pavel Brylov
9e684d45bc add support for custom Accept parameters 2013-04-11 08:34:10 -07:00
TJ Holowaychuk
09d9201787 Merge pull request #1575 from jsmarkus/patch-1
Changed URL of russian docs in Readme.md
2013-04-09 15:47:31 -07:00
Mark
a732d6d471 Changed URL of russian docs in Readme.md 2013-04-09 21:18:50 +03:00
Josh Lubawy
ee9d50c128 Modified hash to return base64 encoded strings. 2013-04-04 23:26:27 -07:00
TJ Holowaychuk
d1bafa0685 docs 2013-04-03 15:11:58 -07:00
TJ Holowaychuk
2604be5491 Merge branch 'master' of github.com:visionmedia/express 2013-04-03 08:26:23 -07:00
TJ Holowaychuk
c52d9cdfbe add --check-leaks for mocha 2013-04-03 08:14:11 -07:00
TJ Holowaychuk
aab6b7e721 Merge pull request #1567 from guybrush/fixTravis
fix .travis.yml
2013-04-02 14:42:06 -07:00
Patrick Pfeiffer
d13cea46d5 fix .travis.yml 2013-04-02 16:16:30 +02:00
TJ Holowaychuk
82731dae6e Merge pull request #1503 from shesek/settings-inheritance
Inherit settings from parent application using [[Prototype]]
2013-04-01 14:29:20 -07:00
TJ Holowaychuk
476fba3e8b Release 3.1.1 2013-04-01 11:25:58 -07:00
TJ Holowaychuk
a566624f2d refactor 2013-04-01 11:22:16 -07:00
TJ Holowaychuk
c6d7352f5c Merge branch 'master' of github.com:visionmedia/express 2013-04-01 11:19:02 -07:00
TJ Holowaychuk
771573be30 Merge pull request #1516 from PatternConsulting/master
Fix Dotted Relative Redirects in Applications Mounted on Nested Paths
2013-04-01 11:18:50 -07:00
TJ Holowaychuk
b7afa4f0f4 Merge pull request #1523 from thomseddon/fix-whitespace
Remove some superfluous trailing whitespace
2013-04-01 11:14:07 -07:00
TJ Holowaychuk
4a1fa58704 refactor req.host 2013-04-01 11:09:23 -07:00
TJ Holowaychuk
6654b7162c Merge branch 'master' of github.com:visionmedia/express 2013-04-01 11:07:05 -07:00
TJ Holowaychuk
f26a3cc806 Merge pull request #1564 from cdauth/master
Consider X-Forwarded-Host if proxy is trusted
2013-04-01 11:06:39 -07:00
Josh Dague
57e48c4767 Possible fix for #1557, allows routes to be created using literal regexes across V8 contexts. Removes all uses of instanceof. 2013-04-01 14:03:32 -04:00
TJ Holowaychuk
66d9a4ad43 Merge branch 'master' of github.com:visionmedia/express 2013-04-01 11:02:56 -07:00
TJ Holowaychuk
78d9c98187 update connect 2013-04-01 11:02:29 -07:00
Candid Dauth
b686ec1182 Considering X-Forwarded-Host header if proxy is trusted 2013-03-31 01:28:34 +01:00
TJ Holowaychuk
158f452b50 Merge pull request #1534 from lennym/patch-1
Made quotes consistent in generated app.js
2013-03-13 15:43:18 -07:00
TJ Holowaychuk
916acd1dd3 replace 0.6.x travis with 0.10.x 2013-03-12 17:26:24 -07:00
TJ Holowaychuk
b4f612474b Merge pull request #1540 from fern4lvarez/master
Use End-of-line Node constant
2013-03-12 17:24:36 -07:00
fern4lvarez
9a884aa9ee Use End-of-line Node constant 2013-03-12 14:02:31 +01:00
TJ Holowaychuk
db5636199e Merge pull request #1502 from qjcg/app-template-noconfigure
Remove legacy app.configure() method from app template.
2013-03-11 08:59:24 -07:00
Leonard Martin
8211562cf6 Made quotes consistent
One-off use of double quotes aggravated my OCD.
2013-03-07 11:12:22 +00:00
TJ Holowaychuk
9df93d6dec Merge pull request #1533 from shesek/old-viewcallbacks
Removed old references to viewCallbacks
2013-03-06 14:31:45 -08:00
Nadav Ivgi
1e251af8d3 Removed old references to viewCallbacks
Was part of the deprecated locals.use() functionallity
2013-03-07 00:12:50 +02:00
Thom Seddon
eed0f598a0 Remove some superfluous trailing whitespace 2013-03-01 07:47:30 +00:00
TJ Holowaychuk
ec4d4a792a Merge pull request #1519 from yawnt/master
Fix explicit .js on project creation
2013-02-28 12:16:27 -08:00
yawnt
84e745f67c [fix] add .js, fixes haibu compatibility 2013-02-26 18:19:06 +01:00
Michael Ahlers
97edb23dba See comments. 2013-02-24 18:54:17 -05:00
Michael Ahlers
856782c81c Never mind. 2013-02-24 18:47:43 -05:00
Michael Ahlers
a7266392f9 Although unrelated to #1516, this broken test case is causing headaches. (This is a reasonable fix in any case.) 2013-02-24 18:43:19 -05:00
Michael Ahlers
04b0c44bdf Test cases document this. 2013-02-24 18:05:11 -05:00
Michael Ahlers
99c9eecde5 When in Rome… 2013-02-24 18:03:29 -05:00
Michael Ahlers
4d65bbf612 Test cases for pull-request #1516. 2013-02-24 18:01:50 -05:00
Michael Ahlers
956aa0cfff This works as expected, and has limited scope. 2013-02-24 13:40:51 -05:00
Michael Ahlers
d874476f0b Proposal to allow relative redirects for applications that have been mounted at multiple paths. 2013-02-24 13:10:49 -05:00
TJ Holowaychuk
30f9805539 Merge pull request #1513 from killmenot/master
minor typo issue
2013-02-22 08:35:39 -08:00
Alexey Kucherenko
46536dee39 fixed typo 2013-02-22 15:52:42 +04:00
TJ Holowaychuk
24087d94df link to runnable 2013-02-20 09:18:42 -08:00
TJ Holowaychuk
d02df2ebd5 update connect 2013-02-19 15:50:23 -08:00
TJ Holowaychuk
16ba1f62a3 Merge branch 'master' of github.com:visionmedia/express 2013-02-13 10:56:35 -08:00
TJ Holowaychuk
684dd1a3c6 update mkdirp 2013-02-13 10:56:22 -08:00
TJ Holowaychuk
8bcdcfeedd update buffer-crc32 2013-02-13 10:55:55 -08:00
TJ Holowaychuk
3bc372aa33 Merge pull request #1505 from gravis/patch-1
Update Readme.md
2013-02-13 10:54:35 -08:00
Philippe Lafoucrière
fc1c024041 Update Readme.md
Add dependancies status badge.
The badge looks bigger than Travis, because it's using a more recent version:
https://github.com/olivierlacan/shields/
2013-02-13 15:28:31 +01:00
TJ Holowaychuk
89427228d1 typo 2013-02-08 08:42:47 -08:00
Nadav Ivgi
420225f370 inherit settings from parent application using [[Prototype]] 2013-02-08 12:58:07 +02:00
John Gosset
44a3fa6359 Remove legacy app.configure() method from app template. 2013-02-07 11:14:06 -05:00
TJ Holowaychuk
d853c833f0 Release 3.1.0 2013-01-25 20:28:58 -08:00
TJ Holowaychuk
03a796c460 Merge pull request #1478 from ericf/settings-view-engine-test
Add test for "view engine" setting with leading ".".
2013-01-24 08:33:24 -08:00
Eric Ferraiuolo
75e47f2883 Add test for "view engine" setting with leading ".". 2013-01-24 00:55:55 -05:00
TJ Holowaychuk
a4b2e48dfe refactor res.set() array support 2013-01-23 20:31:25 -08:00
TJ Holowaychuk
57cda1578d Merge pull request #1477 from gmethvin/set_array
Allow setting an array of header values in the response
2013-01-23 20:29:17 -08:00
TJ Holowaychuk
6b1d7a94ff Merge branch 'integrate' 2013-01-23 20:21:43 -08:00
TJ Holowaychuk
49abd7bec1 merge 2013-01-23 20:21:36 -08:00
TJ Holowaychuk
0ebebd80fe Merge pull request #1466 from ericf/settings-view-engine
Add full extname support to the "view engine" setting; e.g., ".jade".
2013-01-23 20:17:42 -08:00
TJ Holowaychuk
d157d47c6e add node 0.8.x to travis.yml 2013-01-23 20:16:34 -08:00
TJ Holowaychuk
e3ac2c5b02 change req.subdomain styling back 2013-01-23 20:11:14 -08:00
TJ Holowaychuk
cd54faa4af move "subdomain offset" defaulting to config 2013-01-23 20:10:29 -08:00
TJ Holowaychuk
5beb1c4e30 Merge pull request #1475 from gmethvin/subdomain_offset
Add subdomain offset setting
2013-01-23 20:09:25 -08:00
Greg Methvin
4031aaa591 Allow setting an array of header values in the response
Make setting multiple header values using an array work as expected.
If the header value is an array, coerce the values to strings instead
of the entire array.

Fixes #1419.
2013-01-22 18:32:22 -08:00
Greg Methvin
ba00e23630 Add subdomain offset setting
Add a setting "subdomain offset" for the app, which can be used to
change the behavior of req.subdomains. This is useful when our "base"
domain contains more than two parts, e.g. example.co.uk, and also
when we are running locally with domains like xxx.local.

The default behavior is still to return all but the last two parts.
2013-01-20 19:27:58 -08:00
TJ Holowaychuk
8beb1f21ef change prev commit to use app.enabled() 2013-01-18 14:38:50 -08:00
TJ Holowaychuk
fa8eec449b use app.get() for x-powered-by setting
see: http://stackoverflow.com/questions/14285050/broke-up-express-app-into-submodules-now-my-custom-x-powered-by-does-not-wor
2013-01-18 14:36:52 -08:00
TJ Holowaychuk
ab75fa048e refactor vhost example 2013-01-14 09:51:56 +01:00
TJ Holowaychuk
bb29da5980 refactor vhost example 2013-01-13 11:32:53 -08:00
Julian Gruber
a4d7b75129 implemented res.location 2013-01-13 16:07:11 +01:00
Eric Ferraiuolo
0fdceb3de3 Add full extname support to the "view engine" setting; e.g., ".jade".
This allows View to support a `defaultEngine` (a.k.a. an app's
"view engine" setting) which contains a ".", for example:

```
app.engine('.jade', jadeEngine);
app.set('view engine', '.jade');
```

This brings View's handling of template filename extensions to parity
with `app.engine()`.

This allows an app's "view engine" setting to be a full extension name,
including the ".".
2013-01-10 21:41:24 -05:00
TJ Holowaychuk
480d0064e1 Merge pull request #1462 from gmethvin/colon_auth
Allow colons in passwords for req.auth
2013-01-09 12:47:29 -08:00
Greg Methvin
17bf04d1ef Allow colons in passwords for req.auth
Passwords in basic auth can contain colons (as per RFC2617), while
usernames cannot, so assume everything after the colon is a password.
This makes req.auth return the correct value if the user uses a colon
in his password.
2013-01-06 03:02:40 -05:00
TJ Holowaychuk
3ab30210a2 Release 3.0.6 2013-01-04 18:52:04 -08:00
TJ Holowaychuk
14fcfdee7e update connect 2013-01-04 18:37:21 -08:00
TJ Holowaychuk
d4e56c1fa2 Merge pull request #1458 from gmethvin/cookie_options
Don't mangle the options object in res.cookie
2013-01-03 12:25:57 -08:00
Greg Methvin
39ee6f8e79 Don't mangle the options object in res.cookie
Make a copy of the cookie options before mutating it to pass to
cookie.serialize. This prevents unexpected things from happening when
we try to use the same options object multiple times.

Also add a test to verify that the options object does not change
after a request is made.
2013-01-03 02:00:15 -05:00
TJ Holowaychuk
8d21f1e45c change router callback check error message
to read:

Error: .get() requires callback functions but got a [object String]
2012-12-29 08:52:33 -07:00
TJ Holowaychuk
618484a4fe Merge pull request #1454 from shtylman/router-http-methods
add http verbs methods to Router
2012-12-28 09:30:20 -08:00
Guillermo Rauch
64a234958a fix jsonp whitespace escape. Closes #1132 2012-12-28 10:24:55 -07:00
Roman Shtylman
e4907ce8e8 add http verbs methods to Router
By having the method verbs available on the router, users can set up
disjoint routers and organized paths easier.

It is now possible to have a .js file export the router.middleware and
attach these paths using an `app.use('/path', middleware)` call. This
means that any routes written in the separate file do not need to have a
full path hardcoded as they can be mounted by the application anywhere.

This is already possible using `router.route(verb, args)` however is
needlessly verbose without this patch.
2012-12-25 16:43:56 -05:00
TJ Holowaychuk
33eaa8329c Release 3.0.5 2012-12-19 13:46:16 -08:00
TJ Holowaychuk
3c4fd57e51 Merge pull request #1451 from aweeks/fix-304-must-not-contain-body
Explicitly remove Transfer-Encoding header from 204 and 304 responses
2012-12-19 13:34:16 -08:00
Alex Weeks
a1e42ac33f Explicitly remove Transfer-Encoding header from 204 and 304 responses
Per RFC 2616 §10.3.6 & §10.2.5 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) "The [204/304] response MUST NOT contain a message-body, and thus is always terminated by the first empty line after the header fields."
2012-12-19 10:53:17 -08:00
Thorsten Lorenz
ce7d7bfd8d add throwing when a non-function is passed to a route 2012-12-14 15:06:17 -08:00
TJ Holowaychuk
9bd86cdddc Revert "add 'etag' option"
This reverts commit 6f6eec7d8d.
2012-12-06 15:15:49 -08:00
TJ Holowaychuk
0117464ac2 Release 3.0.4 2012-12-05 17:10:59 -08:00
Max Riveiro
6f6eec7d8d add 'etag' option 2012-12-05 16:49:09 -08:00
TJ Holowaychuk
a4e93c0fb8 update connect 2012-12-05 16:35:44 -08:00
TJ Holowaychuk
e2ad0d3d6e merge 2012-11-21 08:46:36 -08:00
TJ Holowaychuk
763be5e631 Merge pull request #1426 from piscis/master
change crc generator because of license issue
2012-11-21 08:45:42 -08:00
TJ Holowaychuk
c8526932f3 Merge branch 'master' of github.com:visionmedia/express 2012-11-21 08:43:13 -08:00
TJ Holowaychuk
5cf29a3d29 Merge pull request #1425 from gmethvin/encode_text_redirect
Escape URLs in text/plain res.redirect response
2012-11-21 08:42:33 -08:00
Alexander Pirsig
18a3cc03ee use buffer-crc32 module for ETag CRC generator 2012-11-21 12:44:07 +01:00
Greg Methvin
ea5e254c7d Escape URLs in text/plain res.redirect response
Escape the URL printed by res.redirect using URL encoding. This
prevents some browsers (primarily old versions of IE) from attempting
to sniff the Content-Type and evaluate it as HTML, which causes a
cross-site scripting vulnerability.
2012-11-21 02:22:37 -05:00
TJ Holowaychuk
060653bd4c Merge branch 'master' of github.com:visionmedia/express 2012-11-20 14:25:30 -08:00
TJ Holowaychuk
b709009bc9 Release 3.0.3 2012-11-13 09:14:13 -08:00
TJ Holowaychuk
b80d7ec257 update connect 2012-11-13 09:12:47 -08:00
TJ Holowaychuk
ff0384e610 update cookie module 2012-11-13 09:12:32 -08:00
TJ Holowaychuk
ab9c275bde fix cookie max-age
the cookie module we depend on never used to
set this value, however now it does :)
2012-11-13 09:08:37 -08:00
TJ Holowaychuk
c70db96b06 Update examples/cors/index.js 2012-11-08 13:52:20 -08:00
TJ Holowaychuk
1c616e29e2 Merge branch 'master' of github.com:visionmedia/express 2012-11-08 09:20:29 -08:00
TJ Holowaychuk
cb7518435f Merge pull request #1406 from DmitryBochkarev/patch-1
remove connect.static.send import from response.js
2012-11-08 09:20:20 -08:00
TJ Holowaychuk
79f81c0a25 Merge pull request #1408 from Laboratory/master
delete unused variables
2012-11-08 09:20:08 -08:00
TJ Holowaychuk
72b9e48331 Release 3.0.2 2012-11-08 09:15:59 -08:00
TJ Holowaychuk
eba6aa1767 fix .version 2012-11-08 09:15:24 -08:00
Vitaly
534fbdb307 delete unused variables 2012-11-08 17:07:08 +04:00
Dmitry Bochkarev
6b309a4457 remove connect.static.send import from response.js 2012-11-08 12:43:38 +05:00
TJ Holowaychuk
af5e38c31a Update Readme.md 2012-11-05 09:10:34 -08:00
TJ Holowaychuk
4283f38698 add OPTIONS to cors example. Closes #1398 2012-11-04 13:21:55 -08:00
TJ Holowaychuk
2d49c0d1f3 Merge branch 'master' of github.com:visionmedia/express 2012-11-04 12:28:53 -08:00
TJ Holowaychuk
a7ca3817db fix route chaining regression. Closes #1397 2012-11-04 12:28:35 -08:00
TJ Holowaychuk
1f044547ee Release 3.0.1 2012-11-01 17:27:53 -07:00
TJ Holowaychuk
612fc47044 update connect 2012-11-01 17:27:33 -07:00
TJ Holowaychuk
8959ff155b Merge pull request #1388 from shtylman/unused-variables
remove unused variables
2012-10-27 13:10:14 -07:00
Roman Shtylman
dadf57cc8b remove unused variables
Tests and examples with unused variables are unchanged.
2012-10-27 16:04:00 -04:00
TJ Holowaychuk
764b6c61d9 Merge pull request #1387 from valllabh/patch-1
Update Readme.md
2012-10-26 06:44:53 -07:00
vallabh
582dadf787 Update Readme.md
Text alignment in Contributors list
2012-10-26 12:24:01 +05:30
TJ Holowaychuk
12d97169d7 Release 3.0.0 2012-10-23 15:29:20 -07:00
TJ Holowaychuk
c29cfa823d change res.redirect() to utilize a pathname-relative Location again. Closes #1382
nginx does not seem to set Host correctly,
though Location should be relative as per RFC
myself nor anyone else can report a client
that does not handle relative correctly so
this will be fine until then
2012-10-23 14:08:18 -07:00
TJ Holowaychuk
e183a9694e add make clean 2012-10-19 11:47:36 -07:00
TJ Holowaychuk
d6cb449011 add "Basic" check to req.auth 2012-10-19 11:46:46 -07:00
TJ Holowaychuk
7bf4ad30fc add req.auth test coverage 2012-10-19 11:44:44 -07:00
TJ Holowaychuk
b3936b96e5 add cb && cb(payload) to res.jsonp(). Closes #1374 2012-10-16 11:57:37 -07:00
TJ Holowaychuk
44d0625e91 fix view-locals example. Closes #1370
this was from 2x, doesnt need to be there at all
2012-10-15 16:07:56 -07:00
TJ Holowaychuk
c492cde048 Merge branch 'examples' 2012-10-09 19:31:56 -07:00
TJ Holowaychuk
c2fa6cc94d fix route-separation example 2012-10-09 19:31:52 -07:00
TJ Holowaychuk
464608025b refactor ejs example some more 2012-10-09 19:26:55 -07:00
TJ Holowaychuk
d653d2308b refactor ejs example 2012-10-09 19:26:17 -07:00
TJ Holowaychuk
a52b1f121c add EJS title example to auth 2012-10-09 19:21:50 -07:00
TJ Holowaychuk
9edd8be520 remove user footer info from auth example
looks a little weird
2012-10-09 19:20:30 -07:00
TJ Holowaychuk
aa1d47600a fix auth example acceptance tests 2012-10-09 19:19:54 -07:00
sakateka
be7ec8b40e improved ejs Example 2012-10-09 19:16:54 -07:00
sakateka
8e12dd9c17 Checked all the examples 2012-10-09 19:16:54 -07:00
TJ Holowaychuk
fee0f0dce0 Merge branch 'error-handling-routes' 2012-10-09 19:09:21 -07:00
TJ Holowaychuk
0dd80e7b50 refactor routes.error tests 2012-10-09 19:09:12 -07:00
TJ Holowaychuk
78d5b72081 rename a test file 2012-10-09 19:06:23 -07:00
Sean Soong
e0df865401 make application routing callback behave the same as middleware, fix logic and add test code 2012-10-09 19:05:30 -07:00
Sean Soong
2abcbed67a make application routing callback behave the same as middleware 2012-10-09 19:05:30 -07:00
TJ Holowaychuk
02b9d0b2e6 fix express.mime reference, should be connect.mime 2012-10-09 18:59:44 -07:00
TJ Holowaychuk
927e181db7 update connect 2012-10-09 18:56:47 -07:00
TJ Holowaychuk
dc411b3175 Merge pull request #1364 from isaacs/patch-1
package.json: Remove publishConfig field
2012-10-09 08:43:37 -07:00
Isaac Z. Schlueter
1e870f0f0d package.json: Remove publishConfig field
Fixes #1359
2012-10-09 08:14:36 -07:00
TJ Holowaychuk
3cd01598a5 Merge pull request #1355 from lihanli/view-cache-test
set the NODE_ENV to development for the development view cache test
2012-10-06 15:25:07 -07:00
lihan
3b6d683b7f set the NODE_ENV to development for the development view cache test 2012-10-06 17:58:17 -04:00
TJ Holowaychuk
eeef763f6d Merge branch 'master' of github.com:visionmedia/express 2012-10-06 09:58:27 -07:00
TJ Holowaychuk
76d3ec583d move default of "views" out so multiple cwd() calls are not made 2012-10-06 09:58:17 -07:00
TJ Holowaychuk
077d8b9247 Merge pull request #1354 from sakateka/master
examples/auth/app.js
2012-10-05 09:17:18 -07:00
sakateka
92840b9f68 Fixed: notify of a successful auth. 2012-10-05 12:23:11 +04:00
TJ Holowaychuk
bf60d5a041 update connect 2012-09-27 12:39:26 -07:00
TJ Holowaychuk
56ffab045e add backwards compat for res.redirect() status. Closes #1336 2012-09-26 09:07:22 -07:00
TJ Holowaychuk
496ee401d7 Merge branch 'master' of github.com:visionmedia/express 2012-09-26 09:04:35 -07:00
TJ Holowaychuk
f0d5b3b368 remove non-primitive string support for res.send()
kinda silly.. valueOf() before if you happen to use
boxed strings, otherwise it is unfair that dont support
other boxed primitives all over, and quite frankly no one does
2012-09-26 09:04:26 -07:00
TJ Holowaychuk
88072a56ea refactor 2012-09-26 09:01:55 -07:00
TJ Holowaychuk
0d3a637389 add support for res.json() to retain previously defined Content-Types. Closes #1349 2012-09-26 09:00:52 -07:00
TJ Holowaychuk
dc44f3c226 Update Readme.md 2012-09-25 13:55:57 -07:00
TJ Holowaychuk
1029b0b97f adjust locals tests 2012-09-25 08:45:09 -07:00
TJ Holowaychuk
84f8228b8c update send 2012-09-19 11:37:46 -07:00
TJ Holowaychuk
968e8f0121 Release 3.0.0rc5 2012-09-18 11:48:20 -07:00
TJ Holowaychuk
9eb1da4568 update connect 2012-09-18 11:44:50 -07:00
TJ Holowaychuk
315e79e9f0 add redis search example 2012-09-18 09:31:12 -07:00
TJ Holowaychuk
363d0d4f41 Merge branch 'master' of github.com:visionmedia/express 2012-09-13 09:52:46 -07:00
TJ Holowaychuk
5ac631a270 add "x-powered-by" setting 2012-09-13 09:52:35 -07:00
TJ Holowaychuk
75fc882001 Merge pull request #1319 from pyrotechnick/patch-1
typo
2012-09-04 12:50:46 -07:00
Nicholas Kinsey
d7cb213eeb Update lib/application.js
typo: http -> https
2012-09-05 05:20:37 +10:00
TJ Holowaychuk
568e0d297a add "application/octet-stream" redirect Accept test case. Closes #1317 2012-09-03 16:17:49 -07:00
TJ Holowaychuk
8edf358739 refactor res.format(), dont pass "default" to req.accepts() 2012-09-03 16:16:46 -07:00
TJ Holowaychuk
40be3ed05d update connect dep 2012-09-03 14:49:46 -07:00
TJ Holowaychuk
2c174d6b3b add app.router to static-files example 2012-09-03 08:54:39 -07:00
TJ Holowaychuk
60ee465bf7 add static-files example
point people here if they have
problems with serving static files
2012-09-03 08:52:53 -07:00
TJ Holowaychuk
51210d6b95 Release 3.0.0rc4 2012-08-30 22:13:38 -07:00
TJ Holowaychuk
a6caa267bd add res.jsonp(). Closes #1307
this also removes the jsonp
2012-08-30 09:29:43 -07:00
TJ Holowaychuk
cdb3e9dc0d fix etag quoting. Closes #1310 2012-08-30 08:49:48 -07:00
TJ Holowaychuk
f2f09767fb add etag util 2012-08-30 08:27:57 -07:00
TJ Holowaychuk
eec31d7126 fix acceptance tests 2012-08-29 10:47:29 -07:00
TJ Holowaychuk
3c5ad753b6 add "verbose errors" option to error-pages example 2012-08-29 10:46:52 -07:00
TJ Holowaychuk
dd338b5567 Merge pull request #1309 from nickjj/patch-1
fix error-pages 404 status
2012-08-29 10:42:42 -07:00
nickjj
1b15af713c fix error-pages 404 status 2012-08-29 14:31:13 -03:00
TJ Holowaychuk
89c5affc3b fix jsonp callback char restrictions 2012-08-28 18:11:49 -07:00
TJ Holowaychuk
2bba69f633 remove old OPTIONS default response
relatively useless since its so non-informative,
let me know if anyone has an objection to this,
i think its best to define these for your API
2012-08-28 17:24:52 -07:00
TJ Holowaychuk
4403f136b7 add another route example to express(1) so people are not so confused 2012-08-28 16:18:00 -07:00
TJ Holowaychuk
f7c1c638bb add some comments to error-pages example 2012-08-28 10:28:58 -07:00
TJ Holowaychuk
f2d7bbe0e9 change jsonp callback default to false. Closes #1304 2012-08-28 09:08:41 -07:00
TJ Holowaychuk
87468cffce update connect dep 2012-08-21 19:09:18 -07:00
TJ Holowaychuk
3f7e3a714e drop to assert() for less crazy error message 2012-08-16 10:14:05 -07:00
TJ Holowaychuk
38a9caf159 update send 2012-08-16 10:08:55 -07:00
TJ Holowaychuk
500c8ab4b3 docs 2012-08-14 04:14:37 -07:00
TJ Holowaychuk
c4ad97d555 add redis online user activity tracking example 2012-08-14 04:13:55 -07:00
TJ Holowaychuk
b96401ac83 Release 3.0.0rc3 2012-08-13 20:23:59 -07:00
TJ Holowaychuk
32a5c9cba5 Merge pull request #1286 from tnydwrds/fix-signed-cookie-prefix
Fix signed cookie prefix
2012-08-13 20:21:36 -07:00
Tony Edwards
0217e6ce96 Fix signed cookie prefix
Update res.cookie to prefix signed cookies with 's:'
2012-08-13 20:01:17 -07:00
TJ Holowaychuk
6bebe0837f refactor res.render() to prevent clobbering "locals" 2012-08-11 15:04:54 -07:00
TJ Holowaychuk
ff1c6f0cfa rename multipart app.js to index.js 2012-08-09 20:08:03 -07:00
TJ Holowaychuk
8ac3e80bf0 Merge branch 'master' of github.com:visionmedia/express 2012-08-09 10:15:27 -07:00
TJ Holowaychuk
3923ec89f7 remove route-loading example
using vm for this is lame
2012-08-09 10:15:12 -07:00
TJ Holowaychuk
fd8ca32ab2 Merge pull request #1273 from ritch/patch-1
Removed extraneous require('http')
2012-08-09 08:46:23 -07:00
Ritchie Martori
ba2f66d765 Removed extraneous require('http') 2012-08-08 17:51:04 -07:00
TJ Holowaychuk
056c840a06 update connect dep 2012-08-07 09:10:40 -07:00
TJ Holowaychuk
20e8f08cb2 Release 3.0.0rc2 2012-08-03 13:32:53 -07:00
TJ Holowaychuk
bac0c64633 escape res.redirect() link 2012-08-02 19:41:37 -07:00
TJ Holowaychuk
48923055eb docs 2012-08-01 13:18:30 -07:00
TJ Holowaychuk
0f20a5e06a add CORS example 2012-08-01 13:17:46 -07:00
TJ Holowaychuk
56bfb9249f Merge branch 'master' of github.com:visionmedia/express 2012-07-31 20:51:07 -07:00
TJ Holowaychuk
5ed1544cab remove generated docs 2012-07-31 20:50:52 -07:00
TJ Holowaychuk
e5c7be9364 Merge pull request #1250 from silvinci/1249-mvc-boot.js-fix-rc
Fixed double inclusion of methods in mvc example
2012-07-26 12:53:06 -07:00
Jan Buschtöns
73ce9d028c Line 40 removed. Fixed! 2012-07-26 21:46:22 +02:00
TJ Holowaychuk
75debbe5bc update connect dep 2012-07-25 09:26:20 -07:00
TJ Holowaychuk
5f33d89ea5 fix vhost example 2012-07-24 15:41:12 -07:00
TJ Holowaychuk
42fd29efe8 deprecate .createServer() & remove old stale examples 2012-07-24 15:40:05 -07:00
TJ Holowaychuk
2d91eac811 Release 3.0.0rc1 2012-07-24 13:32:49 -07:00
TJ Holowaychuk
a50f02e87d update cookie dep 2012-07-24 13:30:44 -07:00
TJ Holowaychuk
214f913b0c merge 2012-07-24 13:28:13 -07:00
TJ Holowaychuk
1021c86300 update connect dep 2012-07-24 13:25:50 -07:00
TJ Holowaychuk
386516815a Merge branch 'master' of github.com:visionmedia/express 2012-07-24 09:57:00 -07:00
TJ Holowaychuk
d5e5647bba fix express(1) -h flag, use -H for hogan. Closes #1245 2012-07-24 09:56:48 -07:00
TJ Holowaychuk
a861ea7eaf Merge pull request #1243 from saintedlama/master
Fixes path joining in app stub generator on win32 systems
2012-07-24 08:46:11 -07:00
TJ Holowaychuk
cb844132e6 Merge pull request #1244 from RubenVerborgh/master
EventEmitter memory leak with successful sendfile
2012-07-24 08:36:53 -07:00
Ruben Verborgh
8050308706 Remove socket error handler if file was sent successfully. 2012-07-24 11:57:02 +02:00
Christoph Walcher
e79f72bf88 Fixes path joining in win32 systems 2012-07-24 07:18:16 +02:00
TJ Holowaychuk
07b6c9f563 update connect dep 2012-07-23 11:35:54 -07:00
TJ Holowaychuk
8f4e61a474 fix res.render docs 2012-07-20 17:22:21 -07:00
TJ Holowaychuk
54d37c60f5 add more examples to view-locals 2012-07-18 10:41:07 -07:00
TJ Holowaychuk
a93d375acc add res.redirect("//foo.com") support 2012-07-18 09:29:11 -07:00
TJ Holowaychuk
d66f0e5eb9 change res.redirect() to use scheme-relative urls 2012-07-18 09:07:21 -07:00
TJ Holowaychuk
e84db12783 update send dep 2012-07-16 19:27:53 -07:00
TJ Holowaychuk
1ad2ecefe8 Release 3.0.0beta7 2012-07-16 09:25:03 -07:00
TJ Holowaychuk
08b68ec8cd udpate connect dep 2012-07-16 09:23:40 -07:00
TJ Holowaychuk
48be9233d8 add res.download() content-disposition on error removal test 2012-07-13 09:24:09 -07:00
TJ Holowaychuk
a512d9b47d Release 3.0.0beta6 2012-07-13 09:19:24 -07:00
TJ Holowaychuk
62234cc106 clean up package.json 2012-07-13 09:00:26 -07:00
TJ Holowaychuk
ab61837885 change res.sendfile() to use send() module 2012-07-13 08:58:40 -07:00
TJ Holowaychuk
7a5041bf9c keywords 2012-07-12 12:51:29 -07:00
TJ Holowaychuk
4d219135b2 update connect dep 2012-07-12 12:03:21 -07:00
TJ Holowaychuk
26eeb64640 add err.view property for view errors. Closes #1226 2012-07-11 08:45:37 -07:00
riadh
5426eb0b62 add "jsonp callback name" setting 2012-07-06 08:53:31 -07:00
TJ Holowaychuk
b9e32ec2c4 styling 2012-07-06 08:15:28 -07:00
TJ Holowaychuk
32b8613708 fix matchRequest tests 2012-07-06 08:14:53 -07:00
TJ Holowaychuk
90eddb3439 dont .toLowerCase() twice 2012-07-06 08:12:47 -07:00
TJ Holowaychuk
accd6180c1 rename matchReq -> matchRequest 2012-07-06 08:12:33 -07:00
TJ Holowaychuk
e770b674ff Merge pull request #1212 from riadhchtara/Issue944
issue#944 : Changing Router#match() signature
2012-07-06 08:08:11 -07:00
TJ Holowaychuk
4f7c4d1051 remove app.locals.use and res.locals.use
there are a few reasons for this:

  a) less API, simpler implementation ...
  b) difficult to inherit cleanly from subapps
  c) effectively the same as parallelized middleware (use connect-parallel for example)

lastly this api in a sense promotes some obscure uses since
they may be scattered throughout rather than explicitly
given to specific routes or used globally as middleware etc
2012-07-05 18:46:19 -07:00
TJ Holowaychuk
78845e7d23 fix app.locals.use() when mounting apps 2012-07-05 18:31:02 -07:00
TJ Holowaychuk
93f1cecc92 update connect dep 2012-07-05 09:43:24 -07:00
riadh
8ccd89f6ad Update Issue944 2012-07-05 13:32:33 +03:00
riadh
0a210cce7a fix to test 2012-07-04 18:51:48 +02:00
riadhchtara
f110248462 Issue 944:
Changing Router#match() signature from (req[, i]) to (method, url[, i]), and making it public
2012-07-04 13:19:40 +02:00
TJ Holowaychuk
96e4014a70 Replace res.send() with res.send = require("response-send") 2012-07-03 21:09:17 -07:00
TJ Holowaychuk
2173d00829 whitespace ocd 2012-07-03 13:57:40 -07:00
riadh
3827f5ef8b tests for issues 1115 fix for utils 2012-07-03 13:56:21 -07:00
riadh
73bed61afa * immediately after :param would become not greedy 2012-07-03 13:56:21 -07:00
TJ Holowaychuk
ee89ff5026 Release 3.0.0beta5 2012-07-03 10:20:19 -07:00
TJ Holowaychuk
8df9e745d5 Merge branch 'master' of github.com:visionmedia/express 2012-07-03 09:39:52 -07:00
TJ Holowaychuk
c76e954504 upgrade connect dep 2012-07-03 09:30:53 -07:00
TJ Holowaychuk
136f054614 Merge pull request #1202 from strk/make_check
Add "make check" support.
2012-07-02 11:41:09 -07:00
Sandro Santilli
e9a4b4f8ff Add "make check" support.
This is to follow GNU coding stadards. See:
www.gnu.org/prep/standards/html_node/Standard-Targets.html
2012-07-02 15:45:33 +02:00
TJ Holowaychuk
5415c98ff9 add route-map tests 2012-06-30 12:18:32 -07:00
TJ Holowaychuk
39efa452fc Added route-map example 2012-06-29 09:01:06 -07:00
TJ Holowaychuk
bddcdee3fe docs 2012-06-27 13:58:35 -07:00
TJ Holowaychuk
2e8d44b444 docs 2012-06-27 13:36:12 -07:00
TJ Holowaychuk
70d68419b0 Added res.json(obj, status) support back for BC 2012-06-27 13:34:53 -07:00
TJ Holowaychuk
26fab2a27d update auth example to utilize cores pbkdf2 2012-06-27 13:20:39 -07:00
TJ Holowaychuk
0f5560eebd updated tests to use "supertest" 2012-06-26 17:14:07 -07:00
TJ Holowaychuk
170dcc2907 Added "methods" dep 2012-06-26 11:38:55 -07:00
TJ Holowaychuk
18d6c78ef4 fixed a test race condition 2012-06-25 12:42:51 -07:00
157 changed files with 3212 additions and 3302 deletions

1
.gitignore vendored
View File

@@ -16,3 +16,4 @@ testing
.coverage_data
cover_html
test.js
.idea

View File

@@ -1,3 +1,4 @@
language: node_js
node_js:
- 0.6
- "0.8"
- "0.10"

View File

@@ -1,5 +1,204 @@
3.0.0beta4 / 2012-06-25
3.2.5 / 2013-05-21
==================
* update connect
* update node-cookie
* 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
==================
* fix `req.subdomains` when no Host is present
* fix `req.host` when no Host is present, return undefined
3.2.3 / 2013-05-07
==================
* update connect / qs
3.2.2 / 2013-05-03
==================
* update qs
3.2.1 / 2013-04-29
==================
* add app.VERB() paths array deprecation warning
* update connect
* update qs and remove all ~ semver crap
* fix: accept number as value of Signed Cookie
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
3.1.2 / 2013-04-12
==================
* add support for custom Accept parameters
* update cookie-signature
3.1.1 / 2013-04-01
==================
* add X-Forwarded-Host support to `req.host`
* fix relative redirects
* update mkdirp
* update buffer-crc32
* remove legacy app.configure() method from app template.
3.1.0 / 2013-01-25
==================
* add support for leading "." in "view engine" setting
* add array support to `res.set()`
* add node 0.8.x to travis.yml
* add "subdomain offset" setting for tweaking `req.subdomains`
* add `res.location(url)` implementing `res.redirect()`-like setting of Location
* use app.get() for x-powered-by setting for inheritance
* fix colons in passwords for `req.auth`
3.0.6 / 2013-01-04
==================
* add http verb methods to Router
* update connect
* fix mangling of the `res.cookie()` options object
* fix jsonp whitespace escape. Closes #1132
3.0.5 / 2012-12-19
==================
* add throwing when a non-function is passed to a route
* fix: explicitly remove Transfer-Encoding header from 204 and 304 responses
* revert "add 'etag' option"
3.0.4 / 2012-12-05
==================
* add 'etag' option to disable `res.send()` Etags
* add escaping of urls in text/plain in `res.redirect()`
for old browsers interpreting as html
* change crc32 module for a more liberal license
* update connect
3.0.3 / 2012-11-13
==================
* update connect
* update cookie module
* fix cookie max-age
3.0.2 / 2012-11-08
==================
* add OPTIONS to cors example. Closes #1398
* fix route chaining regression. Closes #1397
3.0.1 / 2012-11-01
==================
* update connect
3.0.0 / 2012-10-23
==================
* add `make clean`
* add "Basic" check to req.auth
* add `req.auth` test coverage
* add cb && cb(payload) to `res.jsonp()`. Closes #1374
* add backwards compat for `res.redirect()` status. Closes #1336
* add support for `res.json()` to retain previously defined Content-Types. Closes #1349
* update connect
* change `res.redirect()` to utilize a pathname-relative Location again. Closes #1382
* remove non-primitive string support for `res.send()`
* fix view-locals example. Closes #1370
* fix route-separation example
3.0.0rc5 / 2012-09-18
==================
* update connect
* add redis search example
* add static-files example
* add "x-powered-by" setting (`app.disable('x-powered-by')`)
* add "application/octet-stream" redirect Accept test case. Closes #1317
3.0.0rc4 / 2012-08-30
==================
* add `res.jsonp()`. Closes #1307
* add "verbose errors" option to error-pages example
* add another route example to express(1) so people are not so confused
* add redis online user activity tracking example
* update connect dep
* fix etag quoting. Closes #1310
* fix error-pages 404 status
* fix jsonp callback char restrictions
* remove old OPTIONS default response
3.0.0rc3 / 2012-08-13
==================
* update connect dep
* fix signed cookies to work with `connect.cookieParser()` ("s:" prefix was missing) [tnydwrds]
* fix `res.render()` clobbering of "locals"
3.0.0rc2 / 2012-08-03
==================
* add CORS example
* update connect dep
* deprecate `.createServer()` & remove old stale examples
* fix: escape `res.redirect()` link
* fix vhost example
3.0.0rc1 / 2012-07-24
==================
* add more examples to view-locals
* add scheme-relative redirects (`res.redirect("//foo.com")`) support
* update cookie dep
* update connect dep
* update send dep
* fix `express(1)` -h flag, use -H for hogan. Closes #1245
* fix `res.sendfile()` socket error handling regression
3.0.0beta7 / 2012-07-16
==================
* update connect dep for `send()` root normalization regression
3.0.0beta6 / 2012-07-13
==================
* add `err.view` property for view errors. Closes #1226
* add "jsonp callback name" setting
* add support for "/foo/:bar*" non-greedy matches
* change `res.sendfile()` to use `send()` module
* change `res.send` to use "response-send" module
* remove `app.locals.use` and `res.locals.use`, use regular middleware
3.0.0beta5 / 2012-07-03
==================
* add "make check" support
* add route-map example
* add `res.json(obj, status)` support back for BC
* add "methods" dep, remove internal methods module
* update connect dep
* update auth example to utilize cores pbkdf2
* updated tests to use "supertest"
3.0.0beta4 / 2012-06-25
==================
* Added `req.auth`
@@ -11,7 +210,7 @@
* Revert "Added + support to the router"
* Fixed `res.send()` freshness check, respect res.statusCode
3.0.0beta3 / 2012-06-15
3.0.0beta3 / 2012-06-15
==================
* Added hogan `--hjs` to express(1) [nullfirm]
@@ -20,7 +219,7 @@
* Changed: `res.send()` always checks freshness
* Fixed: expose connects mime module. Cloases #1165
3.0.0beta2 / 2012-06-06
3.0.0beta2 / 2012-06-06
==================
* Added `+` support to the router
@@ -28,13 +227,13 @@
* Changed `req.param()` to check route first
* Update connect dep
3.0.0beta1 / 2012-06-01
3.0.0beta1 / 2012-06-01
==================
* Added `res.format()` callback to override default 406 behaviour
* Fixed `res.redirect()` 406. Closes #1154
3.0.0alpha5 / 2012-05-30
3.0.0alpha5 / 2012-05-30
==================
* Added `req.ip`
@@ -43,14 +242,14 @@
* Changed: dont reverse `req.ips`
* Fixed "trust proxy" setting check for `req.ips`
3.0.0alpha4 / 2012-05-09
3.0.0alpha4 / 2012-05-09
==================
* Added: allow `[]` in jsonp callback. Closes #1128
* Added `PORT` env var support in generated template. Closes #1118 [benatkin]
* Updated: connect 2.2.2
3.0.0alpha3 / 2012-05-04
3.0.0alpha3 / 2012-05-04
==================
* Added public `app.routes`. Closes #887
@@ -65,7 +264,7 @@
* Changed: `make test` now runs unit / acceptance tests
* Fixed req/res proto inheritance
3.0.0alpha2 / 2012-04-26
3.0.0alpha2 / 2012-04-26
==================
* Added `make benchmark` back
@@ -81,7 +280,7 @@
* Fixed session example. Closes #1105
* Fixed generated express dep. Closes #1078
3.0.0alpha1 / 2012-04-15
3.0.0alpha1 / 2012-04-15
==================
* Added `app.locals.use(callback)`
@@ -136,54 +335,54 @@
* Fixed `res.sendfile()` with non-GET. Closes #723
* Fixed express(1) public dir for windows. Closes #866
2.5.9/ 2012-04-02
2.5.9/ 2012-04-02
==================
* Added support for PURGE request method [pbuyle]
* Fixed `express(1)` generated app `app.address()` before `listening` [mmalecki]
2.5.8 / 2012-02-08
2.5.8 / 2012-02-08
==================
* Update mkdirp dep. Closes #991
2.5.7 / 2012-02-06
2.5.7 / 2012-02-06
==================
* Fixed `app.all` duplicate DELETE requests [mscdex]
2.5.6 / 2012-01-13
2.5.6 / 2012-01-13
==================
* Updated hamljs dev dep. Closes #953
2.5.5 / 2012-01-08
2.5.5 / 2012-01-08
==================
* Fixed: set `filename` on cached templates [matthewleon]
2.5.4 / 2012-01-02
2.5.4 / 2012-01-02
==================
* Fixed `express(1)` eol on 0.4.x. Closes #947
2.5.3 / 2011-12-30
2.5.3 / 2011-12-30
==================
* Fixed `req.is()` when a charset is present
2.5.2 / 2011-12-10
2.5.2 / 2011-12-10
==================
* Fixed: express(1) LF -> CRLF for windows
2.5.1 / 2011-11-17
2.5.1 / 2011-11-17
==================
* Changed: updated connect to 1.8.x
* Removed sass.js support from express(1)
2.5.0 / 2011-10-24
2.5.0 / 2011-10-24
==================
* Added ./routes dir for generated app by default
@@ -192,7 +391,7 @@
* Removed `make test-cov` since it wont work with node 0.5.x
* Fixed express(1) public dir for windows. Closes #866
2.4.7 / 2011-10-05
2.4.7 / 2011-10-05
==================
* Added mkdirp to express(1). Closes #795
@@ -203,17 +402,17 @@
* Fixed `req.flash()`, only escape args
* Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie]
2.4.6 / 2011-08-22
2.4.6 / 2011-08-22
==================
* Fixed multiple param callback regression. Closes #824 [reported by TroyGoode]
2.4.5 / 2011-08-19
2.4.5 / 2011-08-19
==================
* Added support for routes to handle errors. Closes #809
* Added `app.routes.all()`. Closes #803
* Added "basepath" setting to work in conjunction with reverse proxies etc.
* Added "basepath" setting to work in conjunction with reverse proxies etc.
* Refactored `Route` to use a single array of callbacks
* Added support for multiple callbacks for `app.param()`. Closes #801
Closes #805
@@ -221,25 +420,25 @@ Closes #805
* Dependency: `qs >= 0.3.1`
* Fixed `res.redirect()` on windows due to `join()` usage. Closes #808
2.4.4 / 2011-08-05
2.4.4 / 2011-08-05
==================
* Fixed `res.header()` intention of a set, even when `undefined`
* Fixed `*`, value no longer required
* Fixed `res.send(204)` support. Closes #771
2.4.3 / 2011-07-14
2.4.3 / 2011-07-14
==================
* Added docs for `status` option special-case. Closes #739
* Fixed `options.filename`, exposing the view path to template engines
2.4.2. / 2011-07-06
2.4.2. / 2011-07-06
==================
* Revert "removed jsonp stripping" for XSS
2.4.1 / 2011-07-06
2.4.1 / 2011-07-06
==================
* Added `res.json()` JSONP support. Closes #737
@@ -251,14 +450,14 @@ Closes #805
* Changed; default cookie path to "home" setting. Closes #731
* Removed _pids/logs_ creation from express(1)
2.4.0 / 2011-06-28
2.4.0 / 2011-06-28
==================
* Added chainable `res.status(code)`
* Added `res.json()`, an explicit version of `res.send(obj)`
* Added simple web-service example
2.3.12 / 2011-06-22
2.3.12 / 2011-06-22
==================
* \#express is now on freenode! come join!
@@ -270,7 +469,7 @@ Closes #805
* Fixed view layout bug. Closes #720
* Fixed; ignore body on 304. Closes #701
2.3.11 / 2011-06-04
2.3.11 / 2011-06-04
==================
* Added `npm test`
@@ -278,14 +477,14 @@ Closes #805
* Fixed; `express(1)` adds express as a dep
* Fixed; prune on `prepublish`
2.3.10 / 2011-05-27
2.3.10 / 2011-05-27
==================
* Added `req.route`, exposing the current route
* Added _package.json_ generation support to `express(1)`
* Fixed call to `app.param()` function for optional params. Closes #682
2.3.9 / 2011-05-25
2.3.9 / 2011-05-25
==================
* Fixed bug-ish with `../' in `res.partial()` calls
@@ -304,7 +503,7 @@ Closes #805
* Removed module.parent check from express(1) generated app. Closes #670
* Refactored router. Closes #639
2.3.6 / 2011-05-20
2.3.6 / 2011-05-20
==================
* Changed; using devDependencies instead of git submodules
@@ -312,30 +511,30 @@ Closes #805
* Fixed markdown example
* Fixed view caching, should not be enabled in development
2.3.5 / 2011-05-20
2.3.5 / 2011-05-20
==================
* Added export `.view` as alias for `.View`
2.3.4 / 2011-05-08
2.3.4 / 2011-05-08
==================
* Added `./examples/say`
* Fixed `res.sendfile()` bug preventing the transfer of files with spaces
2.3.3 / 2011-05-03
2.3.3 / 2011-05-03
==================
* Added "case sensitive routes" option.
* Changed; split methods supported per rfc [slaskis]
* Fixed route-specific middleware when using the same callback function several times
2.3.2 / 2011-04-27
2.3.2 / 2011-04-27
==================
* Fixed view hints
2.3.1 / 2011-04-26
2.3.1 / 2011-04-26
==================
* Added `app.match()` as `app.match.all()`
@@ -345,7 +544,7 @@ Closes #805
* Fixed template caching collision issue. Closes #644
* Moved router over from connect and started refactor
2.3.0 / 2011-04-25
2.3.0 / 2011-04-25
==================
* Added options support to `res.clearCookie()`
@@ -354,18 +553,18 @@ Closes #805
* Changed; auto set Content-Type in res.attachement [Aaron Heckmann]
* Renamed "cache views" to "view cache". Closes #628
* Fixed caching of views when using several apps. Closes #637
* Fixed gotcha invoking `app.param()` callbacks once per route middleware.
* Fixed gotcha invoking `app.param()` callbacks once per route middleware.
Closes #638
* Fixed partial lookup precedence. Closes #631
Shaw]
2.2.2 / 2011-04-12
2.2.2 / 2011-04-12
==================
* Added second callback support for `res.download()` connection errors
* Fixed `filename` option passing to template engine
2.2.1 / 2011-04-04
2.2.1 / 2011-04-04
==================
* Added `layout(path)` helper to change the layout within a view. Closes #610
@@ -379,7 +578,7 @@ Shaw]
* Removed `request` and `response` locals
* Changed; errorHandler page title is now `Express` instead of `Connect`
2.2.0 / 2011-03-30
2.2.0 / 2011-03-30
==================
* Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606
@@ -387,14 +586,14 @@ Shaw]
* Added `app.VERB(path)` as alias of `app.lookup.VERB()`.
* Dependency `connect >= 1.2.0`
2.1.1 / 2011-03-29
2.1.1 / 2011-03-29
==================
* Added; expose `err.view` object when failing to locate a view
* Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann]
* Fixed; `res.send(undefined)` responds with 204 [aheckmann]
2.1.0 / 2011-03-24
2.1.0 / 2011-03-24
==================
* Added `<root>/_?<name>` partial lookup support. Closes #447
@@ -405,20 +604,20 @@ Shaw]
* Fixed stylus example for latest version
* Fixed; wrap try/catch around `res.render()`
2.0.0 / 2011-03-17
2.0.0 / 2011-03-17
==================
* Fixed up index view path alternative.
* Changed; `res.locals()` without object returns the locals
2.0.0rc3 / 2011-03-17
2.0.0rc3 / 2011-03-17
==================
* Added `res.locals(obj)` to compliment `res.local(key, val)`
* Added `res.partial()` callback support
* Fixed recursive error reporting issue in `res.render()`
2.0.0rc2 / 2011-03-17
2.0.0rc2 / 2011-03-17
==================
* Changed; `partial()` "locals" are now optional
@@ -427,14 +626,14 @@ Shaw]
* Fixed blog example
* Fixed `{req,res}.app` reference when mounting [Ben Weaver]
2.0.0rc / 2011-03-14
2.0.0rc / 2011-03-14
==================
* Fixed; expose `HTTPSServer` constructor
* Fixed express(1) default test charset. Closes #579 [reported by secoif]
* Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]
2.0.0beta3 / 2011-03-09
2.0.0beta3 / 2011-03-09
==================
* Added support for `res.contentType()` literal
@@ -452,13 +651,13 @@ Shaw]
* Fixed; default `res.send()` string charset to utf8
* Removed `Partial` constructor (not currently used)
2.0.0beta2 / 2011-03-07
2.0.0beta2 / 2011-03-07
==================
* Added res.render() `.locals` support back to aid in migration process
* Fixed flash example
2.0.0beta / 2011-03-03
2.0.0beta / 2011-03-03
==================
* Added HTTPS support
@@ -491,46 +690,46 @@ Shaw]
* Fixed; strip unsafe chars from jsonp callbacks
* Removed "stream threshold" setting
1.0.8 / 2011-03-01
1.0.8 / 2011-03-01
==================
* Allow `req.query` to be pre-defined (via middleware or other parent app)
* "connect": ">= 0.5.0 < 1.0.0". Closes #547
* Removed the long deprecated __EXPRESS_ENV__ support
1.0.7 / 2011-02-07
1.0.7 / 2011-02-07
==================
* Fixed `render()` setting inheritance.
Mounted apps would not inherit "view engine"
1.0.6 / 2011-02-07
1.0.6 / 2011-02-07
==================
* Fixed `view engine` setting bug when period is in dirname
1.0.5 / 2011-02-05
1.0.5 / 2011-02-05
==================
* Added secret to generated app `session()` call
1.0.4 / 2011-02-05
1.0.4 / 2011-02-05
==================
* Added `qs` dependency to _package.json_
* Fixed namespaced `require()`s for latest connect support
1.0.3 / 2011-01-13
1.0.3 / 2011-01-13
==================
* Remove unsafe characters from JSONP callback names [Ryan Grove]
1.0.2 / 2011-01-10
1.0.2 / 2011-01-10
==================
* Removed nested require, using `connect.router`
1.0.1 / 2010-12-29
1.0.1 / 2010-12-29
==================
* Fixed for middleware stacked via `createServer()`
@@ -538,7 +737,7 @@ Shaw]
would not have access to Express methods such as `res.send()`
or props like `req.query` etc.
1.0.0 / 2010-11-16
1.0.0 / 2010-11-16
==================
* Added; deduce partial object names from the last segment.
@@ -552,7 +751,7 @@ Shaw]
* Added _-s, --session[s]_ flag to express(1) to add session related middleware
* Added _--template_ flag to express(1) to specify the
template engine to use.
* Added _--css_ flag to express(1) to specify the
* Added _--css_ flag to express(1) to specify the
stylesheet engine to use (or just plain css by default).
* Added `app.all()` support [thanks aheckmann]
* Added partial direct object support.
@@ -565,7 +764,7 @@ Shaw]
* Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454
* Fixed jsonp support; _text/javascript_ as per mailinglist discussion
1.0.0rc4 / 2010-10-14
1.0.0rc4 / 2010-10-14
==================
* Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0
@@ -584,7 +783,7 @@ Shaw]
* Fixed; exposing _./support_ libs to examples so they can run without installs
* Fixed mvc example
1.0.0rc3 / 2010-09-20
1.0.0rc3 / 2010-09-20
==================
* Added confirmation for `express(1)` app generation. Closes #391
@@ -607,7 +806,7 @@ Shaw]
* Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo]
1.0.0rc2 / 2010-08-17
1.0.0rc2 / 2010-08-17
==================
* Added `app.register()` for template engine mapping. Closes #390
@@ -620,7 +819,7 @@ Shaw]
* Fixed `res.sendfile()` error handling, defer via `next()`
* Fixed `res.render()` callback when a layout is used [thanks guillermo]
* Fixed; `make install` creating ~/.node_libraries when not present
* Fixed issue preventing error handlers from being defined anywhere. Closes #387
* Fixed issue preventing error handlers from being defined anywhere. Closes #387
1.0.0rc / 2010-07-28
==================
@@ -638,7 +837,7 @@ Shaw]
* Fixed "home" setting
* Fixed middleware/router precedence issue. Closes #366
* Fixed; _configure()_ callbacks called immediately. Closes #368
1.0.0beta2 / 2010-07-23
==================
@@ -773,7 +972,7 @@ Shaw]
* Updated dependencies
* Removed set("session cookie") in favour of use(Session, { cookie: { ... }})
* Removed utils.mixin(); use Object#mergeDeep()
0.8.0 / 2010-03-19
==================
@@ -840,16 +1039,16 @@ Shaw]
* Added seed.yml for kiwi package management support
* Added HTTP client query string support when method is GET. Closes #205
* Added support for arbitrary view engines.
For example "foo.engine.html" will now require('engine'),
the exports from this module are cached after the first require().
* Added async plugin support
* Removed usage of RESTful route funcs as http client
get() etc, use http.get() and friends
* Removed custom exceptions
0.5.0 / 2010-03-10

View File

@@ -1,24 +1,20 @@
MOCHA_OPTS= --check-leaks
REPORTER = dot
docs: docs/express.md
docs/express.md: docs/application.md docs/request.md docs/response.md
cat $^ > $@
docs/%.md: lib/%.js
@mkdir -p docs
dox --raw < $< | ./support/docs > $@
check: test
test: test-unit test-acceptance
test-unit:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER)
--reporter $(REPORTER) \
$(MOCHA_OPTS)
test-acceptance:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--bail \
test/acceptance/*.js
test-cov: lib-cov
@@ -27,10 +23,11 @@ test-cov: lib-cov
lib-cov:
@jscoverage lib lib-cov
docclean:
rm -fr docs
benchmark:
@./support/bench
.PHONY: docs docclean test test-unit test-acceptance benchmark
clean:
rm -f coverage.html
rm -fr lib-cov
.PHONY: test test-unit test-acceptance benchmark clean

View File

@@ -1,7 +1,6 @@
![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)
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)
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)
```js
var express = require('express');
@@ -18,10 +17,6 @@ app.listen(3000);
$ npm install -g express
To install the 3.0 alpha:
$ npm install -g express@3.0
## Quick Start
The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:
@@ -56,7 +51,7 @@ app.listen(3000);
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
@@ -70,12 +65,14 @@ app.listen(3000);
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) 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://express-js.ru/)
* [Русскоязычная документация](http://jsman.ru/express/)
* Run express examples [online](https://runnable.com/express)
## Viewing Examples
First 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 deps:
$ git clone git://github.com/visionmedia/express.git --depth 1
$ cd express
$ npm install
@@ -100,14 +97,14 @@ project: express
commits: 3559
active : 468 days
files : 237
authors:
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%
21 Robert Sköld 0.6%
6 Guillermo Rauch 0.2%
3 Dav Glass 0.1%
3 Nick Poulden 0.1%
@@ -153,11 +150,11 @@ authors:
1 Jonathan Zacsh 0.0%
1 Justin Lilly 0.0%
1 Ken Sato 0.0%
1 Maciej Małecki 0.0%
1 Maciej Małecki 0.0%
1 Masahiro Hayashi 0.0%
```
## License
## License
(The MIT License)

View File

@@ -19,7 +19,7 @@ program
.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('-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);
@@ -30,7 +30,7 @@ var path = program.args.shift() || '.';
// end-of-line code
var eol = 'win32' == os.platform() ? '\r\n' : '\n'
var eol = os.EOL
// Template engine
@@ -54,6 +54,21 @@ var index = [
, '};'
].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.
*/
@@ -195,30 +210,33 @@ var app = [
, ''
, 'var express = require(\'express\')'
, ' , routes = require(\'./routes\')'
, ' , http = require(\'http\');'
, ' , user = require(\'./routes/user\')'
, ' , http = require(\'http\')'
, ' , path = require(\'path\');'
, ''
, 'var app = express();'
, ''
, 'app.configure(function(){'
, ' 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(__dirname + \'/public\'));'
, '});'
, '// 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\')));'
, ''
, 'app.configure(\'development\', function(){'
, '// 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\'));'
, ' console.log(\'Express server listening on port \' + app.get(\'port\'));'
, '});'
, ''
].join(eol);
@@ -279,6 +297,7 @@ function createApplicationAt(path) {
mkdir(path + '/routes', function(){
write(path + '/routes/index.js', index);
write(path + '/routes/user.js', users);
});
mkdir(path + '/views', function(){
@@ -315,7 +334,7 @@ function createApplicationAt(path) {
// Session support
app = app.replace('{sess}', program.sessions
? eol + ' app.use(express.cookieParser(\'your secret here\'));' + eol + ' app.use(express.session());'
? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());'
: '');
// Template support
@@ -326,7 +345,7 @@ function createApplicationAt(path) {
name: 'application-name'
, version: '0.0.1'
, private: true
, scripts: { start: 'node app' }
, scripts: { start: 'node app.js' }
, dependencies: {
express: version
}

View File

@@ -1,25 +0,0 @@
var http = require('http');
var times = 50;
while (times--) {
var req = http.request({
port: 3000
, method: 'POST'
, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
req.on('response', function(res){
console.log(res.statusCode);
});
var n = 500000;
while (n--) {
req.write('foo=bar&bar=baz&');
}
req.write('foo=bar&bar=baz');
req.end();
}

View File

@@ -1,181 +0,0 @@
# app
Application prototype.
# app.use()
Proxy `connect#use()` to apply settings to
mounted applications.
# app.engine()
Register the given template engine callback `fn`
as `ext`.
By default will `require()` the engine based on the
file extension. For example if you try to render
a "foo.jade" file Express will invoke the following internally:
app.engine('jade', require('jade').__express);
For engines that do not provide `.__express` out of the box,
or if you wish to "map" a different extension to the template engine
you may use this method. For example mapping the EJS template engine to
".html" files
app.engine('html', require('ejs').renderFile);
In this case EJS provides a `.renderFile()` method with
the same signature that Express expects: `(path, options, callback)`,
though note that it aliases this method as `ejs.__express` internally
so if you're using ".ejs" extensions you dont need to do anything.
Some template engines do not follow this convention, the
[Consolidate.js](https://github.com/visionmedia/consolidate.js)
library was created to map all of node's popular template
engines to follow this convention, thus allowing them to
work seemlessly within Express.
# app.param()
Map the given param placeholder `name`(s) to the given callback(s).
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'));
}
});
});
# app.set()
Assign `setting` to `val`, or return `setting`'s value.
app.set('foo', 'bar');
app.get('foo');
// => "bar"
Mounted servers inherit their parent server's settings.
# app.enabled()
Check if `setting` is enabled (truthy).
app.enabled('foo')
// => false
app.enable('foo')
app.enabled('foo')
// => true
# app.disabled()
Check if `setting` is disabled.
app.disabled('foo')
// => true
app.enable('foo')
app.disabled('foo')
// => false
# app.enable()
Enable `setting`.
# app.disable()
Disable `setting`.
# app.configure()
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;
}
# app.all()
Special-cased "all" method, applying the given route `path`,
middleware, and callback to _every_ HTTP method.
# app.render()
Render the given view `name` name with `options`
and a callback accepting an error and the
rendered template string.
## Example
app.render('email', { name: 'Tobi' }, function(err, html){
// ...
})
# app.listen()
Listen for connections.
A node `http.Server` is returned, with this
application (which is a `Function`) as its
callback. If you wish to create both an HTTP
and HTTPS server you may do so with the "http"
and "https" modules as shown here.
var http = require('http')
, https = require('https')
, express = require('express')
, app = express();
http.createServer(app).listen(80);
http.createServer({ ... }, app).listen(443);

View File

@@ -1,506 +0,0 @@
# app
Application prototype.
# app.use()
Proxy `connect#use()` to apply settings to
mounted applications.
# app.engine()
Register the given template engine callback `fn`
as `ext`.
By default will `require()` the engine based on the
file extension. For example if you try to render
a "foo.jade" file Express will invoke the following internally:
app.engine('jade', require('jade').__express);
For engines that do not provide `.__express` out of the box,
or if you wish to "map" a different extension to the template engine
you may use this method. For example mapping the EJS template engine to
".html" files
app.engine('html', require('ejs').renderFile);
In this case EJS provides a `.renderFile()` method with
the same signature that Express expects: `(path, options, callback)`,
though note that it aliases this method as `ejs.__express` internally
so if you're using ".ejs" extensions you dont need to do anything.
Some template engines do not follow this convention, the
[Consolidate.js](https://github.com/visionmedia/consolidate.js)
library was created to map all of node's popular template
engines to follow this convention, thus allowing them to
work seemlessly within Express.
# app.param()
Map the given param placeholder `name`(s) to the given callback(s).
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'));
}
});
});
# app.set()
Assign `setting` to `val`, or return `setting`'s value.
app.set('foo', 'bar');
app.get('foo');
// => "bar"
Mounted servers inherit their parent server's settings.
# app.enabled()
Check if `setting` is enabled (truthy).
app.enabled('foo')
// => false
app.enable('foo')
app.enabled('foo')
// => true
# app.disabled()
Check if `setting` is disabled.
app.disabled('foo')
// => true
app.enable('foo')
app.disabled('foo')
// => false
# app.enable()
Enable `setting`.
# app.disable()
Disable `setting`.
# app.configure()
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;
}
# app.all()
Special-cased "all" method, applying the given route `path`,
middleware, and callback to _every_ HTTP method.
# app.render()
Render the given view `name` name with `options`
and a callback accepting an error and the
rendered template string.
## Example
app.render('email', { name: 'Tobi' }, function(err, html){
// ...
})
# app.listen()
Listen for connections.
A node `http.Server` is returned, with this
application (which is a `Function`) as its
callback. If you wish to create both an HTTP
and HTTPS server you may do so with the "http"
and "https" modules as shown here.
var http = require('http')
, https = require('https')
, express = require('express')
, app = express();
http.createServer(app).listen(80);
http.createServer({ ... }, app).listen(443);
# req
Request prototype.
# req.get
Return request header.
The `Referrer` header field is special-cased,
both `Referrer` and `Referer` are interchangeable.
## Examples
req.get('Content-Type');
// => "text/plain"
req.get('content-type');
// => "text/plain"
req.get('Something');
// => undefined
Aliased as `req.header()`.
# req.accepts()
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".
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",
or an array `["json", "html", "text/plain"]`. When a list
or array is given the _best_ match, if any is returned.
## Examples
// Accept: text/html
req.accepts('html');
// => "html"
// Accept: text/*, application/json
req.accepts('html');
// => "html"
req.accepts('text/html');
// => "text/html"
req.accepts('json, text');
// => "json"
req.accepts('application/json');
// => "application/json"
// Accept: text/*, application/json
req.accepts('image/png');
req.accepts('png');
// => undefined
// Accept: text/*;q=.5, application/json
req.accepts(['html', 'json']);
req.accepts('html, json');
// => "json"
# req.acceptsCharset()
Check if the given `charset` is acceptable,
otherwise you should respond with 406 "Not Acceptable".
# req.acceptsLanguage()
Check if the given `lang` is acceptable,
otherwise you should respond with 406 "Not Acceptable".
# req.param()
Return the value of param `name` when present or `defaultValue`.
- Checks route placeholders, ex: _/user/:id_
- Checks body params, ex: id=12, {"id":12}
- Checks query string params, ex: ?id=12
To utilize request bodies, `req.body`
should be an object. This can be done by using
the `connect.bodyParser()` middleware.
# req.is()
Check if the incoming request contains the "Content-Type"
header field, and it contains the give mime `type`.
## Examples
// With Content-Type: text/html; charset=utf-8
req.is('html');
req.is('text/html');
req.is('text/*');
// => true
// When Content-Type is application/json
req.is('json');
req.is('application/json');
req.is('application/*');
// => true
req.is('html');
// => false
# res
Response prototype.
# res.status()
Set status `code`.
# res.send()
Send a response.
## Examples
res.send(new Buffer('wahoo'));
res.send({ some: 'json' });
res.send('<p>some html</p>');
res.send(404, 'Sorry, cant find that');
res.send(404);
# res.json()
Send JSON response.
## Examples
res.json(null);
res.json({ user: 'tj' });
res.json(500, 'oh noes!');
res.json(404, 'I dont have that');
# res.sendfile()
Transfer the file at the given `path`.
Automatically sets the _Content-Type_ response header field.
The callback `fn(err)` is invoked when the transfer is complete
or when an error occurs. Be sure to check `res.sentHeader`
if you wish to attempt responding, as the header and some data
may have already been transferred.
## Options
- `maxAge` defaulting to 0
- `root` root directory for relative filenames
## Examples
The following example illustrates how `res.sendfile()` may
be used as an alternative for the `static()` middleware for
dynamic situations. The code backing `res.sendfile()` is actually
the same code, so HTTP cache support etc is identical.
app.get('/user/:uid/photos/:file', function(req, res){
var uid = req.params.uid
, file = req.params.file;
req.user.mayViewFilesFrom(uid, function(yes){
if (yes) {
res.sendfile('/uploads/' + uid + '/' + file);
} else {
res.send(403, 'Sorry! you cant see that.');
}
});
});
# res.download()
Transfer the file at the given `path` as an attachment.
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.
This method uses `res.sendfile()`.
# res.format()
Respond to the Acceptable formats using an `obj`
of mime-type callbacks.
This method uses `req.accepted`, an array of
acceptable types ordered by their quality values.
When "Accept" is not present the _first_ callback
is invoked, otherwise the first match is used. When
no match is performed the server responds with
406 "Not Acceptable".
Content-Type is set for you, however if you choose
you may alter this within the callback using `res.type()`
or `res.set('Content-Type', ...)`.
res.format({
'text/plain': function(){
res.send('hey');
},
'text/html': function(){
res.send('<p>hey</p>');
},
'appliation/json': function(){
res.send({ message: 'hey' });
}
});
In addition to canonicalized MIME types you may
also use extnames mapped to these types:
res.format({
text: function(){
res.send('hey');
},
html: function(){
res.send('<p>hey</p>');
},
json: function(){
res.send({ message: 'hey' });
}
});
By default Express passes an `Error`
with a `.status` of 406 to `next(err)`
if a match is not made, however you may
provide an optional callback `fn` to
be invoked instead.
# res.attachment()
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
# res.set
Set header `field` to `val`, or pass
an object of header fields.
## Examples
res.set('Accept', 'application/json');
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
Aliased as `res.header()`.
# res.get()
Get value for header `field`.
# res.clearCookie()
Clear cookie `name`.
# res.cookie()
Set cookie `name` to `val`, with the given `options`.
## Options
- `maxAge` max-age in milliseconds, converted to `expires`
- `signed` sign the cookie
- `path` defaults to "/"
## Examples
// "Remember Me" for 15 minutes
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
// save as above
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
# res.redirect()
Redirect to the given `url` with optional response `status`
defaulting to 302.
The given `url` can also be the name of a mapped url, for
example by default express supports "back" which redirects
to the _Referrer_ or _Referer_ headers or "/".
## Examples
res.redirect('/foo/bar');
res.redirect('http://example.com');
res.redirect(301, 'http://example.com');
res.redirect('../login'); // /blog/post/1 -> /blog/login
## Mounting
When an application is mounted, and `res.redirect()`
is given a path that does _not_ lead with "/". For
example suppose a "blog" app is mounted at "/blog",
the following redirect would result in "/blog/login":
res.redirect('login');
While the leading slash would result in a redirect to "/login":
res.redirect('/login');
# res.render()
Render `view` with the given `options` and optional callback `fn`.
When a callback function is given a response will _not_ be made
automatically, otherwise a response of _200_ and _text/html_ is given.
## Options
- `status` Response status code (`res.statusCode`)
- `charset` Set the charset (`res.charset`)
## Reserved locals
- `cache` boolean hinting to the engine it should cache
- `filename` filename of the view being rendered

View File

@@ -1,107 +0,0 @@
# req
Request prototype.
# req.get
Return request header.
The `Referrer` header field is special-cased,
both `Referrer` and `Referer` are interchangeable.
## Examples
req.get('Content-Type');
// => "text/plain"
req.get('content-type');
// => "text/plain"
req.get('Something');
// => undefined
Aliased as `req.header()`.
# req.accepts()
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".
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",
or an array `["json", "html", "text/plain"]`. When a list
or array is given the _best_ match, if any is returned.
## Examples
// Accept: text/html
req.accepts('html');
// => "html"
// Accept: text/*, application/json
req.accepts('html');
// => "html"
req.accepts('text/html');
// => "text/html"
req.accepts('json, text');
// => "json"
req.accepts('application/json');
// => "application/json"
// Accept: text/*, application/json
req.accepts('image/png');
req.accepts('png');
// => undefined
// Accept: text/*;q=.5, application/json
req.accepts(['html', 'json']);
req.accepts('html, json');
// => "json"
# req.acceptsCharset()
Check if the given `charset` is acceptable,
otherwise you should respond with 406 "Not Acceptable".
# req.acceptsLanguage()
Check if the given `lang` is acceptable,
otherwise you should respond with 406 "Not Acceptable".
# req.param()
Return the value of param `name` when present or `defaultValue`.
- Checks route placeholders, ex: _/user/:id_
- Checks body params, ex: id=12, {"id":12}
- Checks query string params, ex: ?id=12
To utilize request bodies, `req.body`
should be an object. This can be done by using
the `connect.bodyParser()` middleware.
# req.is()
Check if the incoming request contains the "Content-Type"
header field, and it contains the give mime `type`.
## Examples
// With Content-Type: text/html; charset=utf-8
req.is('html');
req.is('text/html');
req.is('text/*');
// => true
// When Content-Type is application/json
req.is('json');
req.is('application/json');
req.is('application/*');
// => true
req.is('html');
// => false

View File

@@ -1,218 +0,0 @@
# res
Response prototype.
# res.status()
Set status `code`.
# res.send()
Send a response.
## Examples
res.send(new Buffer('wahoo'));
res.send({ some: 'json' });
res.send('<p>some html</p>');
res.send(404, 'Sorry, cant find that');
res.send(404);
# res.json()
Send JSON response.
## Examples
res.json(null);
res.json({ user: 'tj' });
res.json(500, 'oh noes!');
res.json(404, 'I dont have that');
# res.sendfile()
Transfer the file at the given `path`.
Automatically sets the _Content-Type_ response header field.
The callback `fn(err)` is invoked when the transfer is complete
or when an error occurs. Be sure to check `res.sentHeader`
if you wish to attempt responding, as the header and some data
may have already been transferred.
## Options
- `maxAge` defaulting to 0
- `root` root directory for relative filenames
## Examples
The following example illustrates how `res.sendfile()` may
be used as an alternative for the `static()` middleware for
dynamic situations. The code backing `res.sendfile()` is actually
the same code, so HTTP cache support etc is identical.
app.get('/user/:uid/photos/:file', function(req, res){
var uid = req.params.uid
, file = req.params.file;
req.user.mayViewFilesFrom(uid, function(yes){
if (yes) {
res.sendfile('/uploads/' + uid + '/' + file);
} else {
res.send(403, 'Sorry! you cant see that.');
}
});
});
# res.download()
Transfer the file at the given `path` as an attachment.
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.
This method uses `res.sendfile()`.
# res.format()
Respond to the Acceptable formats using an `obj`
of mime-type callbacks.
This method uses `req.accepted`, an array of
acceptable types ordered by their quality values.
When "Accept" is not present the _first_ callback
is invoked, otherwise the first match is used. When
no match is performed the server responds with
406 "Not Acceptable".
Content-Type is set for you, however if you choose
you may alter this within the callback using `res.type()`
or `res.set('Content-Type', ...)`.
res.format({
'text/plain': function(){
res.send('hey');
},
'text/html': function(){
res.send('<p>hey</p>');
},
'appliation/json': function(){
res.send({ message: 'hey' });
}
});
In addition to canonicalized MIME types you may
also use extnames mapped to these types:
res.format({
text: function(){
res.send('hey');
},
html: function(){
res.send('<p>hey</p>');
},
json: function(){
res.send({ message: 'hey' });
}
});
By default Express passes an `Error`
with a `.status` of 406 to `next(err)`
if a match is not made, however you may
provide an optional callback `fn` to
be invoked instead.
# res.attachment()
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
# res.set
Set header `field` to `val`, or pass
an object of header fields.
## Examples
res.set('Accept', 'application/json');
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
Aliased as `res.header()`.
# res.get()
Get value for header `field`.
# res.clearCookie()
Clear cookie `name`.
# res.cookie()
Set cookie `name` to `val`, with the given `options`.
## Options
- `maxAge` max-age in milliseconds, converted to `expires`
- `signed` sign the cookie
- `path` defaults to "/"
## Examples
// "Remember Me" for 15 minutes
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
// save as above
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
# res.redirect()
Redirect to the given `url` with optional response `status`
defaulting to 302.
The given `url` can also be the name of a mapped url, for
example by default express supports "back" which redirects
to the _Referrer_ or _Referer_ headers or "/".
## Examples
res.redirect('/foo/bar');
res.redirect('http://example.com');
res.redirect(301, 'http://example.com');
res.redirect('../login'); // /blog/post/1 -> /blog/login
## Mounting
When an application is mounted, and `res.redirect()`
is given a path that does _not_ lead with "/". For
example suppose a "blog" app is mounted at "/blog",
the following redirect would result in "/blog/login":
res.redirect('login');
While the leading slash would result in a redirect to "/login":
res.redirect('/login');
# res.render()
Render `view` with the given `options` and optional callback `fn`.
When a callback function is given a response will _not_ be made
automatically, otherwise a response of _200_ and _text/html_ is given.
## Options
- `status` Response status code (`res.statusCode`)
- `charset` Set the charset (`res.charset`)
## Reserved locals
- `cache` boolean hinting to the engine it should cache
- `filename` filename of the view being rendered

View File

@@ -1,23 +1,26 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express')
, crypto = require('crypto');
var express = require('../..')
, hash = require('./pass').hash;
var app = module.exports = express();
// config
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
// middleware
app.use(express.bodyParser());
app.use(express.cookieParser('shhhh, very secret'));
app.use(express.session());
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
// Session-persisted message middleware
app.locals.use(function(req,res){
app.use(function(req, res, next){
var err = req.session.error
, msg = req.session.success;
delete req.session.error;
@@ -25,28 +28,28 @@ app.locals.use(function(req,res){
res.locals.message = '';
if (err) res.locals.message = '<p class="msg error">' + err + '</p>';
if (msg) res.locals.message = '<p class="msg success">' + msg + '</p>';
})
next();
});
// dummy database
// Generate a salt for the user to prevent rainbow table attacks
// for better security take a look at the bcrypt c++ addon:
// https://github.com/ncb000gt/node.bcrypt.js
var users = {
tj: {
name: 'tj'
, salt: 'randomly-generated-salt'
, pass: hash('foobar', 'randomly-generated-salt')
}
tj: { name: 'tj' }
};
// Used to generate a hash of the plain-text password + salt
function hash(msg, key) {
return crypto
.createHmac('sha256', key)
.update(msg)
.digest('hex');
}
// when you create a user, generate a salt
// and hash the password ('foobar' is the pass here)
hash('foobar', function(err, salt, hash){
if (err) throw err;
// store the salt & hash in the "db"
users.tj.salt = salt;
users.tj.hash = hash;
});
// Authenticate using our plain-object database of doom!
function authenticate(name, pass, fn) {
if (!module.parent) console.log('authenticating %s:%s', name, pass);
var user = users[name];
@@ -55,9 +58,11 @@ function authenticate(name, pass, fn) {
// apply the same algorithm to the POSTed password, applying
// the hash against the pass / salt, if there is a match we
// found the user
if (user.pass == hash(pass, user.salt)) return fn(null, user);
// Otherwise password is invalid
fn(new Error('invalid password'));
hash(pass, user.salt, function(err, hash){
if (err) return fn(err);
if (hash == user.hash) return fn(null, user);
fn(new Error('invalid password'));
})
}
function restrict(req, res, next) {
@@ -74,7 +79,7 @@ app.get('/', function(req, res){
});
app.get('/restricted', restrict, function(req, res){
res.send('Wahoo! restricted area');
res.send('Wahoo! restricted area, click to <a href="/logout">logout</a>');
});
app.get('/logout', function(req, res){
@@ -86,11 +91,6 @@ app.get('/logout', function(req, res){
});
app.get('/login', function(req, res){
if (req.session.user) {
req.session.success = 'Authenticated as ' + req.session.user.name
+ ' click to <a href="/logout">logout</a>. '
+ ' You may now access <a href="/restricted">/restricted</a>.';
}
res.render('login');
});
@@ -104,6 +104,9 @@ app.post('/login', function(req, res){
// in the session store to be retrieved,
// or in this case the entire user object
req.session.user = user;
req.session.success = 'Authenticated as ' + user.name
+ ' click to <a href="/logout">logout</a>. '
+ ' You may now access <a href="/restricted">/restricted</a>.';
res.redirect('back');
});
} else {
@@ -118,4 +121,4 @@ app.post('/login', function(req, res){
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
}

48
examples/auth/pass.js Normal file
View File

@@ -0,0 +1,48 @@
// check out https://github.com/visionmedia/node-pwd
/**
* Module dependencies.
*/
var crypto = require('crypto');
/**
* Bytesize.
*/
var len = 128;
/**
* Iterations. ~300ms
*/
var iterations = 12000;
/**
* Hashes a password with optional `salt`, otherwise
* generate a salt for `pass` and invoke `fn(err, salt, hash)`.
*
* @param {String} password to hash
* @param {String} optional salt
* @param {Function} callback
* @api public
*/
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'));
});
} else {
fn = salt;
crypto.randomBytes(len, function(err, salt){
if (err) return fn(err);
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'));
});
});
}
};

View File

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

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Authentication Example</title>
<title><%= title %></title>
<style>
body {
padding: 50px;
@@ -16,6 +16,3 @@
</style>
</head>
<body>
<%- body %>
</body>
</html>

View File

@@ -1,3 +1,7 @@
<% var title = 'Authentication Example' %>
<% include head %>
<h1>Login</h1>
<%- message %>
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
@@ -13,4 +17,6 @@ Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj"
<p>
<input type="submit" value="Login">
</p>
</form>
</form>
<% include foot %>

46
examples/cors/index.js Normal file
View File

@@ -0,0 +1,46 @@
/**
* Module dependencies.
*/
var express = require('../..')
, app = express()
, api = express();
// app middleware
app.use(express.static(__dirname + '/public'));
// api middleware
api.use(express.logger('dev'));
api.use(express.bodyParser());
/**
* CORS support.
*/
api.all('*', function(req, res, next){
if (!req.get('Origin')) return next();
// use "*" here to accept any origin
res.set('Access-Control-Allow-Origin', 'http://localhost:3000');
res.set('Access-Control-Allow-Methods', 'GET, POST');
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
// res.set('Access-Control-Allow-Max-Age', 3600);
if ('OPTIONS' == req.method) return res.send(200);
next();
});
/**
* POST a user.
*/
api.post('/user', function(req, res){
console.log(req.body);
res.send(201);
});
app.listen(3000);
api.listen(3001);
console.log('app listening on 3000');
console.log('api listening on 3001');

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<script>
var req = new XMLHttpRequest;
req.open('POST', 'http://localhost:3001/user', false);
req.setRequestHeader('Content-Type', 'application/json');
req.send('{"name":"tobi","species":"ferret"}');
console.log(req.responseText);
</script>
</body>
</html>

View File

@@ -31,16 +31,20 @@ app.set('view engine', 'html');
// Dummy users
var users = [
{ name: 'tobi', email: 'tobi@learnboost.com' }
, { name: 'loki', email: 'loki@learnboost.com' }
, { name: 'jane', email: 'jane@learnboost.com' }
{ name: 'tobi', email: 'tobi@learnboost.com' },
{ name: 'loki', email: 'loki@learnboost.com' },
{ name: 'jane', email: 'jane@learnboost.com' }
];
app.get('/', function(req, res){
res.render('users', { users: users });
res.render('users', {
users: users,
title: "EJS example",
header: "Some users"
});
});
if (!module.parent) {
app.listen(3000);
console.log('Express app started on port 3000');
}
}

View File

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

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title> <%= title %> </title>
<style type="text/css">
body {
padding: 50px;
font: 13px Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>

View File

@@ -1,6 +1,10 @@
<% include header.html %>
<h1>Users</h1>
<ul id="users">
<% users.forEach(function(user){ %>
<li><%= user.name %> <%= user.email %></li>
<li><%= user.name %> &lt;<%= user.email %>&gt;</li>
<% }) %>
</ul>
</ul>
<% include footer.html %>

View File

@@ -1,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -7,9 +6,21 @@ var express = require('../../')
, app = module.exports = express()
, silent = 'test' == process.env.NODE_ENV;
// general config
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
// our custom "verbose errors" setting
// which we can use in the templates
// via settings['verbose errors']
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');
}
app.use(express.favicon());
silent || app.use(express.logger('dev'));
@@ -32,9 +43,10 @@ app.use(app.router);
// $ curl http://localhost:3000/notfound -H "Accept: text/plain"
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.status(404);
res.render('404', { url: req.url });
return;
}
@@ -76,16 +88,21 @@ app.get('/', function(req, res){
});
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!'));
});

View File

@@ -7,4 +7,7 @@ extends error
block content
h1 Error: #{error.message}
pre= error.stack
if settings['verbose errors']
pre= error.stack
else
p An error ocurred!

View File

@@ -8,8 +8,8 @@ block content
| visit
a(href="/500") 500
li
| visit
| visit
a(href="/404") 404
li
| visit
| visit
a(href='/403') 403

View File

@@ -45,14 +45,6 @@ app.use(function(req, res, next){
next();
});
// if you wanted to _always_ expose
// the user you might do something like this:
/*
app.locals.use(function(req, res){
if (req.user) res.locals.expose.user = req.user;
})
*/
app.get('/', function(req, res){
res.redirect('/user');
});

View File

@@ -9,10 +9,9 @@ var express = require('../../lib/express');
var pub = __dirname + '/public';
// Auto-compile sass to css with "compiler"
// and then serve with connect's staticProvider
// setup middleware
var app = express.createServer();
var app = express();
app.use(app.router);
app.use(express.static(pub));
app.use(express.errorHandler());

View File

@@ -1,3 +1,4 @@
body {
padding: 50px 80px;
font: 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif;}
font: 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif;
}

View File

@@ -3,9 +3,9 @@
* Module dependencies.
*/
var express = require('../../')
var express = require('../..')
, fs = require('fs')
, md = require('github-flavored-markdown').parse;
, md = require('marked').parse;
var app = module.exports = express();
@@ -42,4 +42,4 @@ app.get('/fail', function(req, res){
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
}

View File

@@ -3,7 +3,7 @@
* Module dependencies.
*/
var express = require('../../')
var express = require('../..')
, format = require('util').format;
var app = module.exports = express()

View File

@@ -1,4 +1,3 @@
var express = require('../..');
var app = module.exports = express();
@@ -25,28 +24,6 @@ app.response.message = function(msg){
return this;
};
// expose the "messages" local variable when views are rendered
app.locals.use(function(req, res){
var msgs = req.session.messages || [];
// expose "messages" local variable
res.locals.messages = msgs;
// expose "hasMessages"
res.locals.hasMessages = !! msgs.length;
/* This is equivalent:
res.locals({
messages: msgs,
hasMessages: !! msgs.length
});
*/
// empty or "flush" the messages so they
// don't build up
req.session.messages = [];
});
// log
if (!module.parent) app.use(express.logger('dev'));
@@ -63,6 +40,29 @@ app.use(express.bodyParser());
// support _method (PUT in forms etc)
app.use(express.methodOverride());
// expose the "messages" local variable when views are rendered
app.use(function(req, res, next){
var msgs = req.session.messages || [];
// expose "messages" local variable
res.locals.messages = msgs;
// expose "hasMessages"
res.locals.hasMessages = !! msgs.length;
/* This is equivalent:
res.locals({
messages: msgs,
hasMessages: !! msgs.length
});
*/
next();
// empty or "flush" the messages so they
// don't build up
req.session.messages = [];
});
// load controllers
require('./lib/boot')(app, { verbose: !module.parent });
@@ -89,4 +89,4 @@ app.use(function(req, res, next){
if (!module.parent) {
app.listen(3000);
console.log('\n listening on port 3000\n');
}
}

View File

@@ -37,7 +37,6 @@ module.exports = function(parent, options){
case 'show':
method = 'get';
path = '/' + name + '/:' + name + '_id';
app[method](path, obj[key]);
break;
case 'list':
method = 'get';

54
examples/online/index.js Normal file
View File

@@ -0,0 +1,54 @@
// first:
// $ npm install redis online
// $ redis-server
/**
* Module dependencies.
*/
var express = require('../..')
, online = require('online')
, redis = require('redis')
, db = redis.createClient();
// online
online = online(db);
// app
var app = express();
// activity tracking, in this case using
// the UA string, you would use req.user.id etc
app.use(function(req, res, next){
// fire-and-forget
online.add(req.headers['user-agent']);
next();
});
/**
* List helper.
*/
function list(ids) {
return '<ul>' + ids.map(function(id){
return '<li>' + id + '</li>';
}).join('') + '</ul>';
}
/**
* GET users online.
*/
app.get('/', function(req, res, next){
online.last(5, function(err, ids){
if (err) return next(err);
res.send('<p>Users online: ' + ids.length + '</p>' + list(ids));
});
});
app.listen(3000);
console.log('listening on port 3000');

View File

@@ -1,32 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var app = express.createServer();
// Optional since express defaults to CWD/views
app.set('views', __dirname + '/views');
// Set our default template engine to "jade"
// which prevents the need for extensions
// (although you can still mix and match)
app.set('view engine', 'jade');
// Dummy record
var ninja = {
name: 'leonardo',
summary: { email: 'hunter.loftis+github@gmail.com', master: 'splinter', description: 'peaceful leader' },
weapons: ['katana', 'fists', 'shell'],
victims: ['shredder', 'brain', 'beebop', 'rocksteady']
};
app.get('/', function(req, res){
res.render('ninja', { ninja: ninja });
});
app.listen(3000);
console.log('Express app started on port 3000');

View File

@@ -1,5 +0,0 @@
!!!
html
head
title Partials Example
body!= body

View File

@@ -1 +0,0 @@
li= value

View File

@@ -1 +0,0 @@
li.weapon= weapon

View File

@@ -1,22 +0,0 @@
h1= ninja.name
// file, partial name, and partial object all match ('summary')
// the partial filename prefix '_' is completely optional.
// In this case we need to specify ninja.summary as the object
// option, since it is a "plain" object Express cannot otherwise
// tell if it is intended to be locals, or THE summary object
#summary!= partial('summary', { object: ninja.summary })
// file, partial name = '_weapon', resolves to 'weapon' object within partial
#weapons
h2 Weapons
// the weapon partial is rendered once per item in
// the weapons array or "collection"
ul!= partial('weapon', ninja.weapons)
// partial name 'victim' resolves to 'victim.jade'
// or 'victim/index.jade', providing the "victim" local
#victims
h2 Victims
ul!= partial('victim', ninja.victims)

View File

@@ -1,4 +0,0 @@
h2 Summary
p= summary.email
p= summary.description
p taught by master #{summary.master}

View File

@@ -1,5 +0,0 @@
// this is insane overkill, I do not recommend
// doing tiny partials like this as it gets expensive
// with collections, however this illustrates the new
// partial lookup mechanism
!= partial('../../li', { object: victim, as: 'value' })

View File

@@ -1,26 +0,0 @@
/**
* Module dependencies.
*/
var vm = require('vm')
, fs = require('fs');
module.exports = function(app, db){
var dir = __dirname + '/routes';
// grab a list of our route files
fs.readdirSync(dir).forEach(function(file){
var str = fs.readFileSync(dir + '/' + file, 'utf8');
// inject some pseudo globals by evaluating
// the file with vm.runInNewContext()
// instead of loading it with require(). require's
// internals use similar, so dont be afraid of "boot time".
var context = { app: app, db: db };
// we have to merge the globals for console, process etc
for (var key in global) context[key] = global[key];
// note that this is essentially no different than ... just using
// global variables, though it's only YOUR code that could influence
// them, which is a bonus.
vm.runInNewContext(str, context, file);
});
};

View File

@@ -1,20 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express')
, app = express()
, db = { users: [] };
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
// pretend db is a database, could be
// whatever you like
require('./boot')(app, db);
app.listen(3000);
console.log('Express app started on port 3000');

View File

@@ -1,4 +0,0 @@
app.get('/', function(req, res){
res.render('index');
});

View File

@@ -1,18 +0,0 @@
app.get('/users', function(req, res){
res.render('user/list', { users: db.users });
});
app.get('/user/add', function(req, res){
res.render('user/add');
});
app.post('/user', function(req, res){
var user = req.body.user;
db.users.push(user);
res.redirect('/users');
});
app.get('/user/:id', function(req, res){
res.render('user');
});

View File

@@ -1,8 +0,0 @@
extends layout
block content
h2 Route sharing example
ul
li: a(href='/user/add') Add user
li: a(href='/users') User list

View File

@@ -1,11 +0,0 @@
doctype html
html
head
title Route loading example
style
body {
padding: 50px;
font: 14px/1.5 solid helvetica, arial, sans-serif;
}
body
block content

View File

@@ -1,9 +0,0 @@
extends ../layout
block content
h2 Add a user
form(action='/user', method='post')
p: input(type='text', name='user[name]', placeholder='Username')
p: input(type='text', name='user[email]', placeholder='Email')
p: input(type='submit', value='Add')

View File

@@ -1,10 +0,0 @@
extends ../layout
block content
h1= user.name
table
tbody
tr
td Email
td= user.email

View File

@@ -1,10 +0,0 @@
extends ../layout
block content
h1 Users
if users.length
for user in users
include index
else
p No users, head over to <a href='/user/add'>/user/add</a> to create one.

View File

@@ -0,0 +1,63 @@
var express = require('../../lib/express')
, verbose = process.env.NODE_ENV != 'test'
, app = module.exports = express();
app.map = function(a, route){
route = route || '';
for (var key in a) {
switch (typeof a[key]) {
// { '/path': { ... }}
case 'object':
app.map(a[key], route + key);
break;
// get: function(){ ... }
case 'function':
if (verbose) console.log('%s %s', key, route);
app[key](route, a[key]);
break;
}
}
};
var users = {
list: function(req, res){
res.send('user list');
},
get: function(req, res){
res.send('user ' + req.params.uid);
},
del: function(req, res){
res.send('delete users');
}
};
var pets = {
list: function(req, res){
res.send('user ' + req.params.uid + '\'s pets');
},
del: function(req, res){
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
}
};
app.map({
'/users': {
get: users.list,
del: users.del,
'/:uid': {
get: users.get,
'/pets': {
get: pets.list,
'/:pid': {
del: pets.del
}
}
}
}
});
app.listen(3000);

View File

@@ -4,7 +4,7 @@
var express = require('../../lib/express');
var app = express.createServer();
var app = express();
// Example requests:
// curl http://localhost:3000/user/0

View File

@@ -3,17 +3,19 @@
* Module dependencies.
*/
var express = require('../../lib/express')
, app = express.createServer()
var express = require('../..')
, app = express()
, site = require('./site')
, post = require('./post')
, user = require('./user');
// Config
app.set('view engine', 'ejs');
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(express.static(__dirname + '/public'));
@@ -35,4 +37,4 @@ app.put('/user/:id/edit', user.update);
app.get('/posts', post.list);
app.listen(3000);
console.log('Express app started on port 3000');
console.log('Express app started on port 3000');

View File

@@ -2,9 +2,9 @@
// Fake posts database
var posts = [
{ title: 'Foo', body: 'some foo bar' }
, { title: 'Foo bar', body: 'more foo bar' }
, { title: 'Foo bar baz', body: 'more foo bar baz' }
{ title: 'Foo', body: 'some foo bar' },
{ title: 'Foo bar', body: 'more foo bar' },
{ title: 'Foo bar baz', body: 'more foo bar baz' }
];
exports.list = function(req, res){

View File

@@ -11,10 +11,10 @@ a.edit {
opacity: .3;
}
a.edit::before {
content: '[ ';
content: ' [';
}
a.edit::after {
content: ' ]';
content: ']';
}
dt {
font-weight: bold;

View File

@@ -2,8 +2,8 @@
// Fake user database
var users = [
{ name: 'TJ', email: 'tj@vision-media.ca' }
, { name: 'Tobi', email: 'tobi@vision-media.ca' }
{ name: 'TJ', email: 'tj@vision-media.ca' },
{ name: 'Tobi', email: 'tobi@vision-media.ca' }
];
exports.list = function(req, res){
@@ -22,15 +22,15 @@ exports.load = function(req, res, next){
exports.view = function(req, res){
res.render('users/view', {
title: 'Viewing user ' + req.user.name
, user: req.user
title: 'Viewing user ' + req.user.name,
user: req.user
});
};
exports.edit = function(req, res){
res.render('users/edit', {
title: 'Editing user ' + req.user.name
, user: req.user
title: 'Editing user ' + req.user.name,
user: req.user
});
};

View File

@@ -1,4 +0,0 @@
<ul>
<li>Visit the <a href="/users">users</a> page</li>
<li>Visit the <a href="/posts">posts</a> page</li>
</ul>

View File

@@ -0,0 +1,6 @@
extends layout
block content
ul
li Visit the <a href="/users">users</a> page
li Visit the <a href="/posts">posts</a> page

View File

@@ -1,9 +0,0 @@
<html>
<head>
<title><%= title %></title>
<link href="/style.css" rel="stylesheet" />
</head>
<body>
<%- body %>
</body>
</html>

View File

@@ -0,0 +1,6 @@
html
head
title= title
link(href="/style.css", rel="stylesheet")
body
block content

View File

@@ -1,7 +0,0 @@
<h1>Posts</h1>
<dl id="posts">
<% posts.forEach(function(post){ %>
<dt><%= post.title %></dt>
<dd><%= post.body %></dd>
<% }) %>
</dl>

View File

@@ -0,0 +1,8 @@
extends ../layout
block content
h1 Posts
dl#posts
for post in posts
dt= post.title
dd= post.body

View File

@@ -1,9 +0,0 @@
<h1>Editing <%= user.name %></h1>
<div id="user">
<form method="post">
<input type="hidden" value="put" name="_method" />
<p>Name: <input type="text" value="<%= user.name %>" name="user[name]"/></p>
<p>Email: <input type="text" value="<%= user.email %>" name="user[email]"/></p>
<p><input type="submit" value="Save" /></p>
</form>
</div>

View File

@@ -0,0 +1,13 @@
extends ../layout
block content
h1 Editing #{user.name}
#user
form(method="post")
input(type="hidden", value="put", name="_method")
p Name:
input(type="text", value= user.name, name="user[name]")
p Email:
input(type="text", value= user.email, name="user[email]")
p
input(type="submit", value="Save")

View File

@@ -1,9 +0,0 @@
<h1>Users</h1>
<ul id="users">
<% users.forEach(function(user, id){ %>
<li>
<a href="/user/<%= id %>"><%= user.name %></a>
<a class="edit" href="/user/<%= id %>/edit">edit</a>
</li>
<% }) %>
</ul>

View File

@@ -0,0 +1,9 @@
extends ../layout
block content
h1 Users
#users
for user, i in users
li
a(href="/user/#{i}")= user.name
a.edit(href="/user/#{i}/edit") edit

View File

@@ -1,4 +0,0 @@
<h1><%= user.name %></h1>
<div id="user">
<p>Email: <%= user.email %></p>
</div>

View File

@@ -0,0 +1,6 @@
extends ../layout
block content
h1= user.name
#user
p Email: #{user.email}

View File

@@ -1,44 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../')
, path = require('path')
, exec = require('child_process').exec
, fs = require('fs');
/**
* Error handler.
*/
function errorHandler(voice) {
return function(err, req, res, next) {
var parts = err.stack.split('\n')[1].split(/[()]/)[1].split(':')
, filename = parts.shift()
, basename = path.basename(filename)
, lineno = parts.shift()
, col = parts.shift()
, lines = fs.readFileSync(filename, 'utf8').split('\n')
, line = lines[lineno - 1].replace(/\./, ' ');
exec('say -v "' + voice + '" '
+ err.message
+ ' on line ' + lineno
+ ' of ' + basename + '.'
+ ' The contents of this line is '
+ ' "' + line + '".');
res.send(500);
}
}
var app = express.createServer();
app.get('/', function(request, response){
if (request.is(foo)) response.end('bar');
});
app.use(errorHandler('Vicki'));
app.listen(3000);

14
examples/search/client.js Normal file
View File

@@ -0,0 +1,14 @@
var search = document.querySelector('[type=search]');
var code = document.querySelector('pre');
search.addEventListener('keyup', function(){
var xhr = new XMLHttpRequest;
xhr.open('GET', '/search/' + search.value, true);
xhr.onreadystatechange = function(){
if (4 == xhr.readyState) {
code.textContent = xhr.responseText;
}
};
xhr.send();
}, false);

61
examples/search/index.js Normal file
View File

@@ -0,0 +1,61 @@
// first:
// $ npm install redis
// $ redis-server
/**
* Module dependencies.
*/
var express = require('../..')
, redis = require('redis')
, db = redis.createClient();
// npm install redis
var app = express();
app.set('view engine', 'jade');
app.set('views', __dirname);
// populate search
db.sadd('ferret', 'tobi');
db.sadd('ferret', 'loki');
db.sadd('ferret', 'jane');
db.sadd('cat', 'manny');
db.sadd('cat', 'luna');
/**
* GET the search page.
*/
app.get('/', function(req, res){
res.render('search');
});
/**
* GET search for :query.
*/
app.get('/search/:query?', function(req, res){
var query = req.params.query;
db.smembers(query, function(err, vals){
if (err) return res.send(500);
res.send(vals);
});
});
/**
* GET client javascript. Here we use sendfile()
* because serving __dirname with the static() middleware
* would also mean serving our server "index.js" and the "search.jade"
* template.
*/
app.get('/client.js', function(req, res){
res.sendfile(__dirname + '/client.js');
});
app.listen(3000);
console.log('app listening on port 3000');

View File

@@ -0,0 +1,15 @@
!!! 5
html
head
title Search example
style
body {
font: 14px "Helvetica Neue", Helvetica;
padding: 50px;
}
body
h2 Search
p Try searching for "ferret" or "cat".
input(type='search')
pre
script(src='client.js')

View File

@@ -1,4 +1,8 @@
// first:
// $ npm install redis
// $ redis-server
var express = require('../..');
var app = express();
@@ -25,4 +29,4 @@ app.get('/', function(req, res){
});
app.listen(3000);
console.log('Express app started on port 3000');
console.log('Express app started on port 3000');

View File

@@ -0,0 +1,44 @@
var express = require('../..');
var app = express();
// log requests
app.use(express.logger('dev'));
// express on its own has no notion
// of a "file". The express.static()
// middleware checks for a file matching
// the `req.path` within the directory
// that you pass it. In this case "GET /js/app.js"
// will look for "./public/js/app.js".
app.use(express.static(__dirname + '/public'));
// if you wanted to "prefix" you may use
// the mounting feature of Connect, for example
// "GET /static/js/app.js" instead of "GET /js/app.js".
// The mount-path "/static" is simply removed before
// passing control to the express.static() middleware,
// thus it serves the file correctly by ignoring "/static"
app.use('/static', express.static(__dirname + '/public'));
// if for some reason you want to serve files from
// several directories, you can use express.static()
// multiple times! Here we're passing "./public/css",
// 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');

View File

@@ -0,0 +1,3 @@
body {
}

View File

@@ -0,0 +1 @@
hey

View File

@@ -0,0 +1 @@
foo

View File

@@ -1,51 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express')
, stylus = require('stylus');
var app = express.createServer();
// $ npm install stylus
// completely optional, however
// the compile function allows you to
// define additional functions exposed to Stylus,
// alter settings, etc
function compile(str, path) {
return stylus(str)
.set('filename', path)
.set('compress', true);
};
// add the stylus middleware, which re-compiles when
// a stylesheet has changed, compiling FROM src,
// TO dest. dest is optional, defaulting to src
app.use(stylus.middleware({
src: __dirname + '/views'
, dest: __dirname + '/public'
, compile: compile
}));
// minimal setup both reading and writting to ./public
// would look like:
// app.use(stylus.middleware({ src: __dirname + '/public' }));
// the middleware itself does not serve the static
// css files, so we need to expose them with staticProvider
app.use(express.static(__dirname + '/public'));
app.set('views', __dirname + '/views');
app.get('/', function(req, res){
res.render('index.jade');
});
app.listen(3000);
console.log('server listening on port 3000');

View File

@@ -1 +0,0 @@
*.css

View File

@@ -1,2 +0,0 @@
h1 Stylus
p Just an example of using Stylus with Express.

View File

@@ -1,6 +0,0 @@
html
head
title Stylus Example
link(rel='stylesheet', href='/reset.css')
link(rel='stylesheet', href='/main.css')
body!= body

View File

@@ -1,8 +0,0 @@
body
font 14px helvetica, arial, sans-serif
padding 50px
h1
font-size 50px
p
margin 15px 0

View File

@@ -1,50 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express');
// Edit /etc/vhosts
// First app
var one = express.createServer();
one.use(express.logger());
one.get('/', function(req, res){
res.send('Hello from app one!')
});
one.get('/:sub', function(req, res){
res.send('requsted ' + req.params.sub);
});
// App two
var two = express.createServer();
two.get('/', function(req, res){
res.send('Hello from app two!')
});
// Redirect app
var redirect = express.createServer();
redirect.all('*', function(req, res){
console.log(req.subdomains);
res.redirect('http://localhost:3000/' + req.subdomains[0]);
});
// Main app
var app = express.createServer();
app.use(express.vhost('*.localhost', redirect))
app.use(express.vhost('localhost', one));
app.use(express.vhost('dev', two));
app.listen(3000);
console.log('Express app started on port 3000');

46
examples/vhost/index.js Normal file
View File

@@ -0,0 +1,46 @@
/**
* Module dependencies.
*/
var express = require('../..');
/*
edit /etc/hosts:
127.0.0.1 foo.example.com
127.0.0.1 bar.example.com
127.0.0.1 example.com
*/
// Main app
var main = express();
main.use(express.logger('dev'));
main.get('/', function(req, res){
res.send('Hello from main app!')
});
main.get('/:sub', function(req, res){
res.send('requsted ' + 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]);
});
// Main app
var app = express();
app.use(express.vhost('*.example.com', redirect))
app.use(express.vhost('example.com', main));
app.listen(3000);
console.log('Express app started on port 3000');

View File

@@ -0,0 +1,52 @@
/**
* Module dependencies.
*/
var http = require('http')
, path = require('path')
, extname = path.extname
/**
* Expose `GithubView`.
*/
module.exports = GithubView;
/**
* Custom view that fetches and renders
* remove github templates. You could
* render templates from a database etc.
*/
function GithubView(name, options){
this.name = name;
options = options || {};
this.engine = options.engines[extname(name)];
// "root" is the app.set('views') setting, however
// in your own implementation you could ignore this
this.path = '/' + options.root + '/master/' + name;
}
/**
* Render the view.
*/
GithubView.prototype.render = function(options, fn){
var self = this;
var opts = {
host: 'rawgithub.com',
port: 80,
path: this.path,
method: 'GET'
};
http.request(opts, function(res) {
var buf = '';
res.setEncoding('utf8');
res.on('data', function(str){ buf += str });
res.on('end', function(){
self.engine(buf, options, fn);
});
}).end();
};

View File

@@ -0,0 +1,47 @@
/**
* Module dependencies.
*/
var express = require('../../')
, http = require('http')
, GithubView = require('./github-view')
, md = require('marked').parse;
var app = module.exports = express();
// register .md as an engine in express view system
app.engine('md', function(str, options, fn){
try {
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');
// register a new view constructor
app.set('view', GithubView);
app.get('/', function(req, res){
// rendering a view relative to the repo.
// 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');
})
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -88,7 +88,6 @@ function count2(req, res, next) {
function users2(req, res, next) {
User.all(function(err, users){
if (err) return next(err);
// this would not be ideal for *this*
res.locals.users = users.filter(ferrets);
next();
})
@@ -102,32 +101,46 @@ app.get('/middleware-locals', count2, users2, function(req, res, next){
res.render('user', { title: 'Users' });
});
// keep in mind that middleware may be placed anywhere
// and in various combinations, so if you have locals
// that you wish to make available to all subsequent
// middleware/routes you can do something like this:
/*
// let's assume we wanted to load the users
// and count for every res.render() call, we
// could use app.locals.use() for this. These
// are callbacks which run in parallel ONLY
// when res.render() is invoked. If no views
// are rendered, there is no overhead.
// This may be ideal if you want to load auxiliary
// user information, but only for templates. Note
// that (req, res) are available to you, so you may
// access req.session.user etc.
// Keep in mind these execute in *parallel*, so these
// callbacks should not depend on each other, this
// also makes them slightly more efficient than
// using middleware which execute sequentially
app.locals.use(count2);
app.locals.use(users2);
app.get('/locals', function(req, res){
res.render('user', { title: 'Users' });
app.use(function(req, res, next){
res.locals.user = req.user;
res.locals.sess = req.session;
next();
});
*/
// or suppose you have some /admin
// "global" local variables:
/*
app.use('/api', function(req, res, next){
res.locals.user = req.user;
res.locals.sess = req.session;
next();
});
*/
// the following is effectively the same,
// but uses a route instead:
/*
app.all('/api/*', function(req, res, next){
res.locals.user = req.user;
res.locals.sess = req.session;
next();
});
*/
app.listen(3000);
console.log('Application listening on port 3000');

View File

@@ -4,17 +4,15 @@
var connect = require('connect')
, Router = require('./router')
, methods = Router.methods
, methods = require('methods')
, middleware = require('./middleware')
, debug = require('debug')('express:application')
, locals = require('./utils').locals
, View = require('./view')
, url = require('url')
, utils = connect.utils
, path = require('path')
, http = require('http')
, join = path.join
, fs = require('fs');
, join = path.join;
/**
* Application prototype.
@@ -33,11 +31,9 @@ var app = exports = module.exports = {};
*/
app.init = function(){
var self = this;
this.cache = {};
this.settings = {};
this.engines = {};
this.viewCallbacks = [];
this.defaultConfiguration();
};
@@ -48,22 +44,22 @@ app.init = function(){
*/
app.defaultConfiguration = function(){
var self = this;
// default settings
this.enable('x-powered-by');
this.set('env', process.env.NODE_ENV || 'development');
this.set('subdomain offset', 2);
debug('booting in %s mode', this.get('env'));
// implicit middleware
this.use(connect.query());
this.use(middleware.init(this));
// inherit view callbacks
// inherit protos
this.on('mount', function(parent){
this.request.__proto__ = parent.request;
this.response.__proto__ = parent.response;
this.engines.__proto__ = parent.engines;
this.viewCallbacks = parent.viewCallbacks.slice(0);
this.settings.__proto__ = parent.settings;
});
// router
@@ -83,7 +79,9 @@ app.defaultConfiguration = function(){
this.locals.settings = this.settings;
// default configuration
this.enable('jsonp callback');
this.set('view', View);
this.set('views', process.cwd() + '/views');
this.set('jsonp callback name', 'callback');
this.configure('development', function(){
this.set('json spaces', 2);
@@ -105,7 +103,7 @@ app.defaultConfiguration = function(){
*/
app.use = function(route, fn){
var app, home, handle;
var app;
// default route to '/'
if ('string' != typeof route) fn = route, route = '/';
@@ -164,7 +162,7 @@ app.use = function(route, fn){
* [Consolidate.js](https://github.com/visionmedia/consolidate.js)
* library was created to map all of node's popular template
* engines to follow this convention, thus allowing them to
* work seemlessly within Express.
* work seamlessly within Express.
*
* @param {String} ext
* @param {Function} fn
@@ -253,11 +251,7 @@ app.param = function(name, fn){
app.set = function(setting, val){
if (1 == arguments.length) {
if (this.settings.hasOwnProperty(setting)) {
return this.settings[setting];
} else if (this.parent) {
return this.parent.set(setting);
}
return this.settings[setting];
} else {
this.settings[setting] = val;
return this;
@@ -401,16 +395,25 @@ app.configure = function(env, fn){
};
/**
* Delegate `.VERB(...)` calls to `.route(VERB, ...)`.
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
*/
methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
var args = [method].concat([].slice.call(arguments));
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');
}
// if no router attached yet, attach the router
if (!this._usedRouter) this.use(this.router);
return this._router.route.apply(this._router, args);
}
// setup route
this._router[method].apply(this._router, arguments);
return this;
};
});
/**
@@ -453,8 +456,7 @@ app.del = app.delete;
*/
app.render = function(name, options, fn){
var self = this
, opts = {}
var opts = {}
, cache = this.cache
, engines = this.engines
, view;
@@ -467,8 +469,8 @@ app.render = function(name, options, fn){
// merge app.locals
utils.merge(opts, this.locals);
// merge options.locals
if (options.locals) utils.merge(opts, options.locals);
// merge options._locals
if (options._locals) utils.merge(opts, options._locals);
// merge options
utils.merge(opts, options);
@@ -483,14 +485,16 @@ app.render = function(name, options, fn){
// view
if (!view) {
view = new View(name, {
defaultEngine: this.get('view engine')
, root: this.get('views') || process.cwd() + '/views'
, engines: engines
view = new (this.get('view'))(name, {
defaultEngine: this.get('view engine'),
root: this.get('views'),
engines: engines
});
if (!view.path) {
return fn(new Error('Failed to lookup view "' + name + '"'));
var err = new Error('Failed to lookup view "' + name + '"');
err.view = view;
return fn(err);
}
// prime the cache
@@ -520,7 +524,7 @@ app.render = function(name, options, fn){
* , app = express();
*
* http.createServer(app).listen(80);
* http.createServer({ ... }, app).listen(443);
* https.createServer({ ... }, app).listen(443);
*
* @return {http.Server}
* @api public

View File

@@ -1,10 +1,8 @@
/**
* Module dependencies.
*/
var http = require('http')
, connect = require('connect')
var connect = require('connect')
, proto = require('./application')
, Route = require('./router/route')
, Router = require('./router')
@@ -22,7 +20,7 @@ exports = module.exports = createApplication;
* Framework version.
*/
exports.version = '3.0.0beta4';
exports.version = '3.2.5';
/**
* Expose mime.
@@ -48,7 +46,7 @@ function createApplication() {
/**
* Expose connect.middleware as express.*
* for example `express.logger` etc.
* for example `express.logger` etc.
*/
for (var key in connect.middleware) {
@@ -59,10 +57,19 @@ for (var key in connect.middleware) {
}
/**
* Backwards compat.
* Error on createServer().
*/
exports.createServer = createApplication;
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.
@@ -79,12 +86,6 @@ exports.response = res;
exports.Route = Route;
exports.Router = Router;
/**
* Expose HTTP methods.
*/
exports.methods = require('./router/methods');
// Error handler title
exports.errorHandler.title = 'Express';

View File

@@ -18,7 +18,7 @@ var utils = require('./utils');
exports.init = function(app){
return function expressInit(req, res, next){
req.app = res.app = app;
res.setHeader('X-Powered-By', 'Express');
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
req.res = res;
res.req = req;
req.next = next;
@@ -26,7 +26,7 @@ exports.init = function(app){
req.__proto__ = app.request;
res.__proto__ = app.response;
res.locals = utils.locals(res);
res.locals = res.locals || utils.locals(res);
next();
}

View File

@@ -29,21 +29,21 @@ var req = exports = module.exports = {
*
* req.get('Content-Type');
* // => "text/plain"
*
*
* req.get('content-type');
* // => "text/plain"
*
*
* req.get('Something');
* // => undefined
*
* Aliased as `req.header()`.
*
* @param {String} name
* @return {String}
* @return {String}
* @api public
*/
req.get =
req.get =
req.header = function(name){
switch (name = name.toLowerCase()) {
case 'referer':
@@ -67,7 +67,7 @@ req.header = function(name){
* or array is given the _best_ match, if any is returned.
*
* Examples:
*
*
* // Accept: text/html
* req.accepts('html');
* // => "html"
@@ -101,6 +101,18 @@ req.accepts = function(type){
return utils.accepts(type, this.get('Accept'));
};
/**
* Check if the given `encoding` is accepted.
*
* @param {String} encoding
* @return {Boolean}
* @api public
*/
req.acceptsEncoding = function(encoding){
return ~this.acceptedEncodings.indexOf(encoding);
};
/**
* Check if the given `charset` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
@@ -159,6 +171,24 @@ 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.
@@ -202,7 +232,7 @@ req.__defineGetter__('acceptedLanguages', function(){
var accept = this.get('Accept-Language');
return accept
? utils
.parseQuality(accept)
.parseParams(accept)
.map(function(obj){
return obj.value;
})
@@ -226,7 +256,7 @@ req.__defineGetter__('acceptedCharsets', function(){
var accept = this.get('Accept-Charset');
return accept
? utils
.parseQuality(accept)
.parseParams(accept)
.map(function(obj){
return obj.value;
})
@@ -245,7 +275,7 @@ req.__defineGetter__('acceptedCharsets', function(){
* the `connect.bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} defaultValue
* @param {Mixed} [defaultValue]
* @return {String}
* @api public
*/
@@ -261,7 +291,7 @@ req.param = function(name, defaultValue){
};
/**
* Check if the incoming request contains the "Content-Type"
* Check if the incoming request contains the "Content-Type"
* header field, and it contains the give mime `type`.
*
* Examples:
@@ -271,16 +301,16 @@ req.param = function(name, defaultValue){
* req.is('text/html');
* req.is('text/*');
* // => true
*
*
* // When Content-Type is application/json
* req.is('json');
* req.is('application/json');
* req.is('application/*');
* // => true
*
*
* req.is('html');
* // => false
*
*
* @param {String} type
* @return {Boolean}
* @api public
@@ -303,7 +333,7 @@ req.is = function(type){
/**
* Return the protocol string "http" or "https"
* when requested with TLS. When the "trust proxy"
* 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
@@ -377,39 +407,48 @@ req.__defineGetter__('ips', function(){
* req.auth
* // => { username: 'tobi', password: 'hello' }
*
* @return {Object}
* @return {Object} or undefined
* @api public
*/
req.__defineGetter__('auth', function(){
// missing
var auth = this.get('Authorization');
if (!auth) return {};
if (!auth) return;
// malformed
auth = auth.split(' ')[1];
if (!auth) return {};
var parts = auth.split(' ');
if ('basic' != parts[0].toLowerCase()) return;
if (!parts[1]) return;
auth = parts[1];
// credentials
auth = new Buffer(auth, 'base64').toString().split(':');
return { username: auth[0], password: auth[1] };
auth = new Buffer(auth, 'base64').toString().match(/^([^:]*):(.*)$/);
if (!auth) return;
return { username: auth[1], password: auth[2] };
});
/**
* Return subdomains as an array.
*
* For example "tobi.ferrets.example.com"
* would provide `["ferrets", "tobi"]`.
* Subdomains are the dot-separated parts of the host before the main domain of
* the app. By default, the domain of the app is assumed to be the last two
* parts of the host. This can be changed by setting "subdomain offset".
*
* For example, if the domain is "tobi.ferrets.example.com":
* If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
* If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
*
* @return {Array}
* @api public
*/
req.__defineGetter__('subdomains', function(){
return this.get('Host')
var offset = this.app.get('subdomain offset');
return (this.host || '')
.split('.')
.slice(0, -2)
.reverse();
.reverse()
.slice(offset);
});
/**
@@ -431,7 +470,11 @@ req.__defineGetter__('path', function(){
*/
req.__defineGetter__('host', function(){
return this.get('Host').split(':')[0];
var trustProxy = this.app.get('trust proxy');
var host = trustProxy && this.get('X-Forwarded-Host');
host = host || this.get('Host');
if (!host) return;
return host.split(':')[0];
});
/**

View File

@@ -1,19 +1,18 @@
/**
* Module dependencies.
*/
var fs = require('fs')
, http = require('http')
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
, send = connect.static.send
, cookie = require('cookie')
, crc = require('crc')
, send = require('send')
, mime = connect.mime
, basename = path.basename
, extname = path.extname
@@ -79,9 +78,9 @@ res.links = function(links){
*/
res.send = function(body){
var req = this.req
, head = 'HEAD' == req.method
, len;
var req = this.req;
var head = 'HEAD' == req.method;
var len;
// allow status / body
if (2 == arguments.length) {
@@ -94,10 +93,6 @@ res.send = function(body){
}
}
// convert string objects to primitives
if (body instanceof String) body = body.toString();
switch (typeof body) {
// response status
case 'number':
@@ -133,10 +128,10 @@ res.send = function(body){
// ETag support
// TODO: W/ support
if (len > 1024) {
if (!this.get('ETag')) this.set('ETag', Buffer.isBuffer(body)
? crc.buffer.crc32(body)
: crc.crc32(body));
if (len > 1024 && 'GET' == req.method) {
if (!this.get('ETag')) {
this.set('ETag', etag(body));
}
}
// freshness
@@ -146,6 +141,7 @@ res.send = function(body){
if (204 == this.statusCode || 304 == this.statusCode) {
this.removeHeader('Content-Type');
this.removeHeader('Content-Length');
this.removeHeader('Transfer-Encoding');
body = '';
}
@@ -173,23 +169,74 @@ res.send = function(body){
res.json = function(obj){
// allow status / body
if (2 == arguments.length) {
this.statusCode = obj;
obj = arguments[1];
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
} else {
this.statusCode = obj;
obj = arguments[1];
}
}
var app = this.app
, jsonp = app.get('jsonp callback')
, replacer = app.get('json replacer')
, spaces = app.get('json spaces')
, body = JSON.stringify(obj, replacer, spaces)
, callback = this.req.query.callback;
// settings
var app = this.app;
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
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);
};
/**
* Send JSON response with JSONP callback support.
*
* Examples:
*
* res.jsonp(null);
* res.jsonp({ user: 'tj' });
* res.jsonp(500, 'oh noes!');
* res.jsonp(404, 'I dont have that');
*
* @param {Mixed} obj or status
* @param {Mixed} obj
* @return {ServerResponse}
* @api public
*/
res.jsonp = function(obj){
// allow status / body
if (2 == arguments.length) {
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
} else {
this.statusCode = obj;
obj = arguments[1];
}
}
// settings
var app = this.app;
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = JSON.stringify(obj, replacer, spaces)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
var callback = this.req.query[app.get('jsonp callback name')];
// content-type
this.charset = this.charset || 'utf-8';
this.set('Content-Type', 'application/json');
if (callback && jsonp) {
// jsonp
if (callback) {
this.set('Content-Type', 'text/javascript');
body = callback.replace(/[^[]\w$.]/g, '') + '(' + body + ');';
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = cb + ' && ' + cb + '(' + body + ');';
}
return this.send(body);
@@ -197,7 +244,7 @@ res.json = function(obj){
/**
* Transfer the file at the given `path`.
*
*
* Automatically sets the _Content-Type_ response header field.
* The callback `fn(err)` is invoked when the transfer is complete
* or when an error occurs. Be sure to check `res.sentHeader`
@@ -219,7 +266,7 @@ res.json = function(obj){
* app.get('/user/:uid/photos/:file', function(req, res){
* var uid = req.params.uid
* , file = req.params.file;
*
*
* req.user.mayViewFilesFrom(uid, function(yes){
* if (yes) {
* res.sendfile('/uploads/' + uid + '/' + file);
@@ -239,7 +286,8 @@ res.sendfile = function(path, options, fn){
var self = this
, req = self.req
, next = this.req.next
, options = options || {};
, options = options || {}
, done;
// support function as second arg
if ('function' == typeof options) {
@@ -247,30 +295,49 @@ res.sendfile = function(path, options, fn){
options = {};
}
// callback
options.callback = function(err){
if (err) {
// cast ENOENT
if ('ENOENT' == err.code) err = utils.error(404);
// socket errors
req.socket.on('error', error);
// ditch content-disposition to prevent funky responses
if (!self.headerSent) self.removeHeader('Content-Disposition');
// errors
function error(err) {
if (done) return;
done = true;
// woot! callback available
if (fn) return fn(err);
// clean up
cleanup();
if (!self.headerSent) self.removeHeader('Content-Disposition');
// lost in limbo if there's no callback
if (self.headerSent) return;
// callback available
if (fn) return fn(err);
return req.next(err);
}
// list in limbo if there's no callback
if (self.headerSent) return;
fn && fn();
};
// delegate
next(err);
}
// streaming
function stream() {
if (done) return;
cleanup();
if (fn) self.on('finish', fn);
}
// cleanup
function cleanup() {
req.socket.removeListener('error', error);
}
// transfer
options.path = encodeURIComponent(path);
send(this.req, this, next, options);
var file = send(req, path);
if (options.root) file.root(options.root);
file.maxage(options.maxAge || 0);
file.on('error', error);
file.on('directory', next);
file.on('stream', stream);
file.pipe(this);
this.on('finish', cleanup);
};
/**
@@ -344,11 +411,11 @@ res.type = function(type){
* 'text/plain': function(){
* res.send('hey');
* },
*
*
* 'text/html': function(){
* res.send('<p>hey</p>');
* },
*
*
* 'appliation/json': function(){
* res.send({ message: 'hey' });
* }
@@ -361,11 +428,11 @@ res.type = function(type){
* text: function(){
* res.send('hey');
* },
*
*
* html: function(){
* res.send('<p>hey</p>');
* },
*
*
* json: function(){
* res.send({ message: 'hey' });
* }
@@ -383,26 +450,26 @@ res.type = function(type){
*/
res.format = function(obj){
var keys = Object.keys(obj)
, req = this.req
var req = this.req
, next = req.next;
var fn = obj.default;
if (fn) delete obj.default;
var keys = Object.keys(obj);
var key = req.accepts(keys);
this.set('Vary', 'Accept');
if (key) {
this.set('Content-Type', normalizeType(key));
this.set('Content-Type', normalizeType(key).value);
obj[key](req, this, next);
} else if (fn) {
fn();
} else {
var err = new Error('Not Acceptable');
err.status = 406;
err.types = normalizeTypes(keys);
err.types = normalizeTypes(keys).map(function(o){ return o.value });
next(err);
}
@@ -431,24 +498,27 @@ res.attachment = function(filename){
*
* Examples:
*
* res.set('Foo', ['bar', 'baz']);
* res.set('Accept', 'application/json');
* res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
*
* Aliased as `res.header()`.
* Aliased as `res.header()`.
*
* @param {String|Object} field
* @param {String|Object|Array} field
* @param {String} val
* @return {ServerResponse} for chaining
* @api public
*/
res.set =
res.set =
res.header = function(field, val){
if (2 == arguments.length) {
this.setHeader(field, '' + val);
if (Array.isArray(val)) val = val.map(String);
else val = String(val);
this.setHeader(field, val);
} else {
for (var key in field) {
this.setHeader(key, '' + field[key]);
this.set(key, field[key]);
}
}
return this;
@@ -506,21 +576,25 @@ res.clearCookie = function(name, options){
*/
res.cookie = function(name, val, options){
options = options || {};
options = utils.merge({}, options);
var secret = this.req.secret;
var signed = options.signed;
if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
if ('number' == typeof val) val = val.toString();
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
if (signed) val = utils.sign(val, secret);
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
if (signed) val = 's:' + sign(val, secret);
if ('maxAge' in options) {
options.expires = new Date(Date.now() + options.maxAge);
options.maxAge /= 1000;
}
if (null == options.path) options.path = '/';
this.set('Set-Cookie', cookie.serialize(name, String(val), options));
return this;
};
/**
* Redirect to the given `url` with optional response `status`
* defaulting to 302.
* 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
@@ -528,41 +602,30 @@ res.cookie = function(name, val, options){
*
* Examples:
*
* res.redirect('/foo/bar');
* res.redirect('http://example.com');
* res.redirect(301, 'http://example.com');
* res.redirect('../login'); // /blog/post/1 -> /blog/login
* 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.redirect()`
* is given a path that does _not_ lead with "/". For
* example suppose a "blog" app is mounted at "/blog",
* the following redirect would result in "/blog/login":
* 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.redirect('login');
* res.location('login');
*
* While the leading slash would result in a redirect to "/login":
* While the leading slash would result in a location of "/login":
*
* res.redirect('/login');
* res.location('/login');
*
* @param {String} url
* @param {Number} code
* @api public
*/
res.redirect = function(url){
res.location = function(url){
var app = this.app
, req = this.req
, head = 'HEAD' == req.method
, status = 302
, body;
// allow status / url
if (2 == arguments.length) {
status = url;
url = arguments[1];
}
, req = this.req;
// setup redirect map
var map = { back: req.get('Referrer') || '/' };
@@ -571,30 +634,75 @@ res.redirect = function(url){
url = map[url] || url;
// relative
if (!~url.indexOf('://')) {
var path = app.path();
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
var path
// relative to path
if ('.' == url[0]) {
url = req.path + '/' + url;
// relative to mount-point
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;
}
// Absolute
var host = req.get('Host');
url = req.protocol + '://' + host + url;
}
// Respond
this.set('Location', url);
return this;
};
/**
* Redirect to the given `url` with optional response `status`
* defaulting to 302.
*
* The resulting `url` is determined by `res.location()`, so
* it will play nicely with mounted apps, relative paths,
* `"back"` etc.
*
* Examples:
*
* res.redirect('/foo/bar');
* res.redirect('http://example.com');
* res.redirect(301, 'http://example.com');
* res.redirect('http://example.com', 301);
* res.redirect('../login'); // /blog/post/1 -> /blog/login
*
* @param {String} url
* @param {Number} code
* @api public
*/
res.redirect = function(url){
var app = this.app
, head = 'HEAD' == this.req.method
, status = 302
, body;
// allow status / url
if (2 == arguments.length) {
if ('number' == typeof url) {
status = url;
url = arguments[1];
} else {
status = arguments[1];
}
}
// Set location header
this.location(url);
url = this.get('Location');
// Support text/{plain,html} by default
this.format({
text: function(){
body = statusCodes[status] + '. Redirecting to ' + url;
body = statusCodes[status] + '. Redirecting to ' + encodeURI(url);
},
html: function(){
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
var u = utils.escape(url);
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
},
default: function(){
@@ -604,7 +712,6 @@ res.redirect = function(url){
// Respond
this.statusCode = status;
this.set('Location', url);
this.set('Content-Length', Buffer.byteLength(body));
this.end(head ? null : body);
};
@@ -615,11 +722,6 @@ res.redirect = function(url){
* automatically, otherwise a response of _200_ and _text/html_ is given.
*
* Options:
*
* - `status` Response status code (`res.statusCode`)
* - `charset` Set the charset (`res.charset`)
*
* Reserved locals:
*
* - `cache` boolean hinting to the engine it should cache
* - `filename` filename of the view being rendered
@@ -641,41 +743,15 @@ res.render = function(view, options, fn){
fn = options, options = {};
}
function render() {
// merge res.locals
options.locals = self.locals;
// merge res.locals
options._locals = self.locals;
// default callback to respond
fn = fn || function(err, str){
if (err) return req.next(err);
self.send(str);
};
// default callback to respond
fn = fn || function(err, str){
if (err) return req.next(err);
self.send(str);
};
// render
app.render(view, options, fn);
}
// invoke view callbacks
var callbacks = app.viewCallbacks.concat(self.viewCallbacks)
, pending = callbacks.length
, len = pending
, done;
if (len) {
for (var i = 0; i < len; ++i) {
callbacks[i](req, self, function(err){
if (done) return;
if (err) {
req.next(err);
done = true;
return;
}
--pending || render();
});
}
} else {
render();
}
};
// render
app.render(view, options, fn);
};

View File

@@ -1,10 +1,10 @@
/**
* Module dependencies.
*/
var Route = require('./route')
, utils = require('../utils')
, methods = require('methods')
, debug = require('debug')('express:router')
, parse = require('connect').utils.parseUrl;
@@ -14,15 +14,9 @@ var Route = require('./route')
exports = module.exports = Router;
/**
* Expose HTTP methods.
*/
var methods = exports.methods = require('./methods');
/**
* Initialize a new `Router` with the given `options`.
*
*
* @param {Object} options
* @api private
*/
@@ -99,8 +93,7 @@ Router.prototype._dispatch = function(req, res, next){
, paramVal
, route
, keys
, key
, ret;
, key;
// match next route
function nextRoute(err) {
@@ -108,10 +101,7 @@ Router.prototype._dispatch = function(req, res, next){
}
// match route
req.route = route = self.match(req, i);
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
req.route = route = self.matchRequest(req, i);
// no route
if (!route) return next(err);
@@ -150,7 +140,7 @@ Router.prototype._dispatch = function(req, res, next){
};
param(err);
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
@@ -168,7 +158,8 @@ Router.prototype._dispatch = function(req, res, next){
if (fn.length < 4) return callbacks(err);
fn(err, req, res, callbacks);
} else if (fn) {
fn(req, res, callbacks);
if (fn.length < 4) return fn(req, res, callbacks);
callbacks();
} else {
nextRoute(err);
}
@@ -179,41 +170,6 @@ Router.prototype._dispatch = function(req, res, next){
})(0);
};
/**
* Respond to __OPTIONS__ method.
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @api private
*/
Router.prototype._options = function(req, res){
var path = parse(req).pathname
, body = this._optionsFor(path).join(',');
res.set('Allow', body).send(body);
};
/**
* Return an array of HTTP verbs or "options" for `path`.
*
* @param {String} path
* @return {Array}
* @api private
*/
Router.prototype._optionsFor = function(path){
var self = this;
return methods.filter(function(method){
var routes = self.map[method];
if (!routes || 'options' == method) return;
for (var i = 0, len = routes.length; i < len; ++i) {
if (routes[i].match(path)) return true;
}
}).map(function(method){
return method.toUpperCase();
});
};
/**
* Attempt to match a route for `req`
* with optional starting index of `i`
@@ -225,7 +181,7 @@ Router.prototype._optionsFor = function(path){
* @api private
*/
Router.prototype.match = function(req, i, head){
Router.prototype.matchRequest = function(req, i, head){
var method = req.method.toLowerCase()
, url = parse(req)
, path = url.pathname
@@ -235,7 +191,7 @@ Router.prototype.match = function(req, i, head){
// HEAD support
if (!head && 'head' == method) {
route = this.match(req, i, true);
route = this.matchRequest(req, i, true);
if (route) return route;
method = 'get';
}
@@ -254,6 +210,23 @@ Router.prototype.match = function(req, i, head){
}
};
/**
* Attempt to match a route for `method`
* and `url` with optional starting
* index of `i` defaulting to 0.
*
* @param {String} method
* @param {String} url
* @param {Number} i
* @return {Route}
* @api private
*/
Router.prototype.match = function(method, url, i, head){
var req = { method: method, url: url };
return this.matchRequest(req, i, head);
};
/**
* Route `method`, `path`, and one or more callbacks.
*
@@ -271,14 +244,30 @@ Router.prototype.route = function(method, path, callbacks){
// 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;
var type = {}.toString.call(fn);
var msg = '.' + method + '() 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, {
sensitive: this.caseSensitive
, strict: this.strict
sensitive: this.caseSensitive,
strict: this.strict
});
// add it
(this.map[method] = this.map[method] || []).push(route);
return this;
};
methods.forEach(function(method){
Router.prototype[method] = function(path){
var args = [method].concat([].slice.call(arguments));
this.route.apply(this, args);
return this;
};
});

View File

@@ -1,30 +0,0 @@
/**
* HTTP methods supported by node.
*/
module.exports = [
'get'
, 'post'
, 'put'
, 'head'
, 'delete'
, 'options'
, 'trace'
, 'copy'
, 'lock'
, 'mkcol'
, 'move'
, 'propfind'
, 'proppatch'
, 'unlock'
, 'report'
, 'mkactivity'
, 'checkout'
, 'merge'
, 'm-search'
, 'notify'
, 'subscribe'
, 'unsubscribe'
, 'patch'
];

View File

@@ -3,12 +3,31 @@
* Module dependencies.
*/
var mime = require('connect').mime;
var mime = require('connect').mime
, crc32 = require('buffer-crc32');
/**
* toString ref.
*/
var toString = {}.toString;
/**
* Return ETag for `body`.
*
* @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`.
*
* This is used for `app.locals` and `res.locals`.
*
* @param {Object} obj
* @return {Function}
@@ -16,25 +35,11 @@ var mime = require('connect').mime;
*/
exports.locals = function(obj){
obj.viewCallbacks = obj.viewCallbacks || [];
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
};
locals.use = function(fn){
if (3 == fn.length) {
obj.viewCallbacks.push(fn);
} else {
obj.viewCallbacks.push(function(req, res, done){
fn(req, res);
done();
});
}
return obj;
};
return locals;
};
@@ -76,12 +81,14 @@ exports.flatten = function(arr, ret){
* Normalize the given `type`, for example "html" becomes "text/html".
*
* @param {String} type
* @return {String}
* @return {Object}
* @api private
*/
exports.normalizeType = function(type){
return ~type.indexOf('/') ? type : mime.lookup(type);
return ~type.indexOf('/')
? acceptParams(type)
: { value: mime.lookup(type), params: {} };
};
/**
@@ -96,9 +103,7 @@ exports.normalizeTypes = function(types){
var ret = [];
for (var i = 0; i < types.length; ++i) {
ret.push(~types[i].indexOf('/')
? types[i]
: mime.lookup(types[i]));
ret.push(exports.normalizeType(types[i]));
}
return ret;
@@ -124,7 +129,7 @@ exports.acceptsArray = function(types, str){
for (var i = 0; i < len; ++i) {
for (var j = 0, jlen = types.length; j < jlen; ++j) {
if (exports.accept(normalized[j].split('/'), accepted[i])) {
if (exports.accept(normalized[j], accepted[i])) {
return types[j];
}
}
@@ -149,17 +154,34 @@ exports.accepts = function(type, str){
/**
* Check if `type` array is acceptable for `other`.
*
* @param {Array} type
* @param {Object} type
* @param {Object} other
* @return {Boolean}
* @api private
*/
exports.accept = function(type, other){
return (type[0] == other.type || '*' == other.type)
&& (type[1] == other.subtype || '*' == other.subtype);
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
@@ -174,7 +196,7 @@ exports.accept = function(type, other){
exports.parseAccept = function(str){
return exports
.parseQuality(str)
.parseParams(str)
.map(function(obj){
var parts = obj.value.split('/');
obj.type = parts[0];
@@ -185,44 +207,54 @@ exports.parseAccept = function(str){
/**
* Parse quality `str`, returning an
* array of objects with `.value` and
* `.quality`.
* array of objects with `.value`,
* `.quality` and optional `.params`
*
* @param {Type} name
* @return {Type}
* @param {String} str
* @return {Array}
* @api private
*/
exports.parseQuality = function(str){
exports.parseParams = function(str){
return str
.split(/ *, */)
.map(quality)
.map(acceptParams)
.filter(function(obj){
return obj.quality;
})
.sort(function(a, b){
return b.quality - a.quality;
if (a.quality === b.quality) {
return a.originalIndex - b.originalIndex;
} else {
return b.quality - a.quality;
}
});
};
/**
* Parse quality `str` returning an
* object with `.value` and `.quality`.
* Parse accept params `str` returning an
* object with `.value`, `.quality` and `.params`.
* also includes `.originalIndex` for stable sorting
*
* @param {String} str
* @return {Object}
* @api private
*/
function quality(str) {
var parts = str.split(/ *; */)
, val = parts[0];
function acceptParams(str, index) {
var parts = str.split(/ *; */);
var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
var q = parts[1]
? parseFloat(parts[1].split(/ *= */)[1])
: 1;
for (var i = 1; i < parts.length; ++i) {
var pms = parts[i].split(/ *= */);
if ('q' == pms[0]) {
ret.quality = parseFloat(pms[1]);
} else {
ret.params[pms[0]] = pms[1];
}
}
return { value: val, quality: q };
return ret;
}
/**
@@ -259,12 +291,12 @@ exports.escape = function(html) {
*/
exports.pathRegexp = function(path, keys, sensitive, strict) {
if (path instanceof RegExp) return path;
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){
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){
keys.push({ name: key, optional: !! optional });
slash = slash || '';
return ''
@@ -272,9 +304,10 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ (optional || '');
+ (optional || '')
+ (star ? '(/*)?' : '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
}

View File

@@ -8,7 +8,7 @@ var path = require('path')
, dirname = path.dirname
, basename = path.basename
, extname = path.extname
, exists = fs.existsSync || path.existsSync
, exists = fs.existsSync || path.existsSync
, join = path.join;
/**
@@ -22,9 +22,9 @@ module.exports = View;
*
* Options:
*
* - `defaultEngine` the default template engine name
* - `engines` template engine require() cache
* - `root` root path for view lookup
* - `defaultEngine` the default template engine name
* - `engines` template engine require() cache
* - `root` root path for view lookup
*
* @param {String} name
* @param {Object} options
@@ -38,7 +38,8 @@ function View(name, options) {
var engines = options.engines;
this.defaultEngine = options.defaultEngine;
var ext = this.ext = extname(name);
if (!ext) name += (ext = this.ext = '.' + this.defaultEngine);
if (!ext && !this.defaultEngine) throw new Error('No default engine was specified and no extension was provided.');
if (!ext) name += (ext = this.ext = ('.' != this.defaultEngine[0] ? '.' : '') + this.defaultEngine);
this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express);
this.path = this.lookup(name);
}

View File

@@ -1,22 +1,37 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.0.0beta4",
"version": "3.2.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" }
"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"
}
],
"dependencies": {
"connect": "2.3.4",
"connect": "2.7.10",
"commander": "0.6.1",
"range-parser": "0.0.4",
"mkdirp": "0.3.3",
"cookie": "0.0.3",
"crc": "0.2.0",
"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": {
@@ -27,17 +42,30 @@
"stylus": "*",
"should": "*",
"connect-redis": "*",
"github-flavored-markdown": "*"
"marked": "*",
"supertest": "0.6.0"
},
"publishConfig": { "tag": "3.0" },
"keywords": ["express", "framework", "sinatra", "web", "rest", "restful", "router"],
"keywords": [
"express",
"framework",
"sinatra",
"web",
"rest",
"restful",
"router",
"app",
"api"
],
"repository": "git://github.com/visionmedia/express",
"main": "index",
"bin": { "express": "./bin/express" },
"bin": {
"express": "./bin/express"
},
"scripts": {
"prepublish" : "npm prune",
"prepublish": "npm prune",
"test": "make test"
},
"engines": { "node": "*" }
"engines": {
"node": "*"
}
}

View File

@@ -16,8 +16,6 @@ app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.locals.self = true;
var repo = require('../package.json');
app.get('/render', function(req, res){
res.render('hello');
});
@@ -65,4 +63,4 @@ app.get('/match', function(req, res){
res.send('Hello World\n');
});
app.listen(8000);
app.listen(8000);

View File

@@ -12,27 +12,53 @@ describe('Router', function(){
app = express();
})
describe('.match(req, i)', 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(){});
var method = 'GET';
var url = '/foo?bar=baz';
var route = router.match(method, url, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
var route = router.match(method, url, 1);
route.path.should.equal('/foob?');
var route = router.match(method, url, 2);
assert(!route);
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' };
var route = router.match(req, 0);
var route = router.matchRequest(req, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
var route = router.match(req, 1);
var route = router.matchRequest(req, 1);
req._route_index.should.equal(1);
route.path.should.equal('/foob?');
var route = router.match(req, 2);
var route = router.matchRequest(req, 2);
assert(!route);
req.url = '/bar';
var route = router.match(req);
var route = router.matchRequest(req);
route.path.should.equal('/bar');
})
})
@@ -50,4 +76,28 @@ describe('Router', function(){
.expect('foo', done);
})
})
})
describe('.multiple callbacks', function(){
it('should throw if a callback is null', function(){
assert.throws(function () {
router.route('get', '/foo', null, function(){});
})
})
it('should throw if a callback is undefined', function(){
assert.throws(function () {
router.route('get', '/foo', undefined, function(){});
})
})
it('should throw if a callback is not a function', function(){
assert.throws(function () {
router.route('get', '/foo', 'not a function', function(){});
})
})
it('should not throw if all callbacks are functions', function(){
router.route('get', '/foo', function(){}, function(){});
})
})
})

View File

@@ -1,8 +1,8 @@
var app = require('../../examples/auth/app')
, request = require('../support/http');
function redirects(to,fn){
return function(res){
function redirects(to, fn){
return function(err, res){
res.statusCode.should.equal(302)
res.headers.should.have.property('location').match(to);
fn()
@@ -14,80 +14,29 @@ function getCookie(res) {
}
describe('auth', function(){
var cookie;
describe('GET /',function(){
it('should redirect to /login', function(done){
request(app)
.get('/')
.end(redirects(/\/login$/,done))
.get('/')
.end(redirects(/\/login$/, done))
})
})
describe('GET /restricted (w/o cookie)',function(){
it('should redirect to /login', function(done){
request(app)
.get('/restricted')
.end(redirects(/\/login$/,done))
.get('/restricted')
.end(redirects(/\/login$/,done))
})
})
describe('POST /login', function(){
it('should fail without proper credentials', function(done){
request(app)
.post('/login')
.set('content-type','application/x-www-form-urlencoded')
.write('&username=not-tj&password=foobar')
.end(redirects(/\/login$/,done))
})
it('should authenticate', function(done){
request(app)
.post('/login')
.set('content-type', 'application/x-www-form-urlencoded')
.write('username=tj&password=foobar')
.end(function(res){
res.statusCode.should.equal(302);
cookie = getCookie(res);
request(app)
.get('/login')
.set('Cookie', cookie)
.end(function(res){
res.body.should.include('Authenticated as tj');
done();
})
})
})
})
describe('GET /restricted (w. cookie)',function(){
it('should respond with 200', function(done){
request(app)
.get('/restricted')
.set('Cookie', cookie)
.expect(200, done);
})
})
describe('GET /logout',function(){
it('should respond with 302 and clear cookie',function(done){
request(app)
.get('/logout')
.set('Cookie', cookie)
.end(function(res){
res.statusCode.should.equal(302);
res.headers.should.not.have.property('set-cookie')
done();
})
})
})
describe('GET /restricted (w. expired cookie)',function(){
it('should respond with 302',function(done){
request(app)
.get('/restricted')
.set('Cookie', cookie)
.expect(302, done)
.post('/login')
.type('urlencoded')
.send('username=not-tj&password=foobar')
.end(redirects(/\/login$/, done))
})
})
})

View File

@@ -7,20 +7,16 @@ describe('content-negotiation', function(){
it('should default to text/html', function(done){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>');
done();
})
.expect('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>')
.end(done);
})
it('should accept to text/plain', function(done){
request(app)
.get('/')
.set('Accept', 'text/plain')
.end(function(res){
res.body.should.equal(' - Tobi\n - Loki\n - Jane\n');
done();
})
.expect(' - Tobi\n - Loki\n - Jane\n')
.end(done);
})
})
})

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