Compare commits

...

122 Commits
3.0.1 ... 3.1.2

Author SHA1 Message Date
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
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
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
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
35 changed files with 984 additions and 515 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,10 +1,73 @@
3.0.1 / 2012-11-01
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
3.0.0 / 2012-10-23
==================
* add `make clean`
@@ -19,7 +82,7 @@
* fix view-locals example. Closes #1370
* fix route-separation example
3.0.0rc5 / 2012-09-18
3.0.0rc5 / 2012-09-18
==================
* update connect
@@ -28,7 +91,7 @@
* 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
3.0.0rc4 / 2012-08-30
==================
* add `res.jsonp()`. Closes #1307
@@ -41,14 +104,14 @@
* fix jsonp callback char restrictions
* remove old OPTIONS default response
3.0.0rc3 / 2012-08-13
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
3.0.0rc2 / 2012-08-03
==================
* add CORS example
@@ -57,7 +120,7 @@
* fix: escape `res.redirect()` link
* fix vhost example
3.0.0rc1 / 2012-07-24
3.0.0rc1 / 2012-07-24
==================
* add more examples to view-locals
@@ -68,12 +131,12 @@
* fix `express(1)` -h flag, use -H for hogan. Closes #1245
* fix `res.sendfile()` socket error handling regression
3.0.0beta7 / 2012-07-16
3.0.0beta7 / 2012-07-16
==================
* update connect dep for `send()` root normalization regression
3.0.0beta6 / 2012-07-13
3.0.0beta6 / 2012-07-13
==================
* add `err.view` property for view errors. Closes #1226
@@ -83,7 +146,7 @@
* 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
3.0.0beta5 / 2012-07-03
==================
* add "make check" support
@@ -94,7 +157,7 @@
* update auth example to utilize cores pbkdf2
* updated tests to use "supertest"
3.0.0beta4 / 2012-06-25
3.0.0beta4 / 2012-06-25
==================
* Added `req.auth`
@@ -106,7 +169,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]
@@ -115,7 +178,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
@@ -123,13 +186,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`
@@ -138,14 +201,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
@@ -160,7 +223,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
@@ -176,7 +239,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)`
@@ -231,54 +294,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
@@ -287,7 +350,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
@@ -298,17 +361,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
@@ -316,25 +379,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
@@ -346,14 +409,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!
@@ -365,7 +428,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`
@@ -373,14 +436,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
@@ -399,7 +462,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
@@ -407,30 +470,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()`
@@ -440,7 +503,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()`
@@ -449,18 +512,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
@@ -474,7 +537,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
@@ -482,14 +545,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
@@ -500,20 +563,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
@@ -522,14 +585,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
@@ -547,13 +610,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
@@ -586,46 +649,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()`
@@ -633,7 +696,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.
@@ -647,7 +710,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.
@@ -660,7 +723,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
@@ -679,7 +742,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
@@ -702,7 +765,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
@@ -715,7 +778,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
==================
@@ -733,7 +796,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
==================
@@ -868,7 +931,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
==================
@@ -935,16 +998,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,5 +1,5 @@
MOCHA_OPTS=
MOCHA_OPTS= --check-leaks
REPORTER = dot
check: test

View File

@@ -1,6 +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');
@@ -17,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:
@@ -55,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
@@ -69,7 +65,8 @@ 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
@@ -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

@@ -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
@@ -216,27 +216,27 @@ var app = [
, ''
, '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(path.join(__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);
@@ -345,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,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -21,11 +20,13 @@ api.use(express.bodyParser());
*/
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();
});

View File

@@ -5,46 +5,43 @@
var express = require('../..');
// Edit /etc/vhosts
/*
edit /etc/vhosts:
// First app
127.0.0.1 foo.example.com
127.0.0.1 bar.example.com
127.0.0.1 example.com
*/
var one = express();
// Main app
one.use(express.logger());
var main = express();
one.get('/', function(req, res){
res.send('Hello from app one!')
main.use(express.logger('dev'));
main.get('/', function(req, res){
res.send('Hello from main app!')
});
one.get('/:sub', function(req, res){
main.get('/:sub', function(req, res){
res.send('requsted ' + req.params.sub);
});
// App two
var two = express();
two.get('/', function(req, res){
res.send('Hello from app two!')
});
// Redirect app
var redirect = express();
redirect.all('*', function(req, res){
console.log(req.subdomains);
res.redirect('http://localhost:3000/' + req.subdomains[0]);
res.redirect('http://example.com:3000/' + req.subdomains[0]);
});
// Main app
var app = express();
app.use(express.vhost('*.localhost', redirect))
app.use(express.vhost('localhost', one));
app.use(express.vhost('dev', two));
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');
console.log('Express app started on port 3000');

View File

@@ -9,12 +9,10 @@ var connect = require('connect')
, 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,11 +44,10 @@ 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
@@ -64,6 +59,7 @@ app.defaultConfiguration = function(){
this.request.__proto__ = parent.request;
this.response.__proto__ = parent.response;
this.engines.__proto__ = parent.engines;
this.settings.__proto__ = parent.settings;
});
// router
@@ -106,7 +102,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 = '/';
@@ -165,7 +161,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
@@ -254,11 +250,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;
@@ -402,16 +394,20 @@ 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);
// 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;
};
});
/**
@@ -454,8 +450,7 @@ app.del = app.delete;
*/
app.render = function(name, options, fn){
var self = this
, opts = {}
var opts = {}
, cache = this.cache
, engines = this.engines
, view;

View File

@@ -20,7 +20,7 @@ exports = module.exports = createApplication;
* Framework version.
*/
exports.version = '3.0.0';
exports.version = '3.1.2';
/**
* Expose mime.
@@ -46,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) {

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;
if (app.settings['x-powered-by']) 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;

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"
@@ -202,7 +202,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 +226,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 +245,7 @@ req.__defineGetter__('acceptedCharsets', function(){
* the `connect.bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} defaultValue
* @param {Mixed} [defaultValue]
* @return {String}
* @api public
*/
@@ -261,7 +261,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 +271,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 +303,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
@@ -393,25 +393,32 @@ req.__defineGetter__('auth', function(){
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(){
var offset = this.app.get('subdomain offset');
return this.get('Host')
.split('.')
.slice(0, -2)
.reverse();
.reverse()
.slice(offset);
});
/**
@@ -433,7 +440,10 @@ 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');
return host.split(':')[0];
});
/**

View File

@@ -2,8 +2,7 @@
* Module dependencies.
*/
var fs = require('fs')
, http = require('http')
var http = require('http')
, path = require('path')
, connect = require('connect')
, utils = connect.utils
@@ -12,10 +11,8 @@ var fs = require('fs')
, normalizeTypes = require('./utils').normalizeTypes
, etag = require('./utils').etag
, statusCodes = http.STATUS_CODES
, send = connect.static.send
, cookie = require('cookie')
, send = require('send')
, crc = require('crc')
, mime = connect.mime
, basename = path.basename
, extname = path.extname
@@ -144,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 = '';
}
@@ -189,7 +187,7 @@ res.json = function(obj){
// content-type
this.charset = this.charset || 'utf-8';
this.get('Content-Type') || this.set('Content-Type', 'application/json');
return this.send(body);
};
@@ -225,13 +223,15 @@ res.jsonp = function(obj){
var app = this.app;
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = JSON.stringify(obj, replacer, spaces);
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');
// jsonp
if (callback) {
this.set('Content-Type', 'text/javascript');
@@ -244,7 +244,7 @@ res.jsonp = 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`
@@ -266,7 +266,7 @@ res.jsonp = 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);
@@ -411,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' });
* }
@@ -428,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' });
* }
@@ -462,14 +462,14 @@ res.format = function(obj){
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);
}
@@ -498,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;
@@ -573,25 +576,89 @@ 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 ('object' == typeof val) val = 'j:' + JSON.stringify(val);
if (signed) val = 's:' + sign(val, secret);
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
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;
};
/**
* 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
* to the _Referrer_ or _Referer_ headers or "/".
*
* Examples:
*
* res.location('/foo/bar').;
* res.location('http://example.com');
* res.location('../login'); // /blog/post/1 -> /blog/login
*
* Mounting:
*
* When an application is mounted and `res.location()`
* is given a path that does _not_ lead with "/" it becomes
* relative to the mount-point. For example if the application
* is mounted at "/blog", the following would become "/blog/login".
*
* res.location('login');
*
* While the leading slash would result in a location of "/login":
*
* res.location('/login');
*
* @param {String} url
* @api public
*/
res.location = function(url){
var app = this.app
, req = this.req;
// setup redirect map
var map = { back: req.get('Referrer') || '/' };
// perform redirect
url = map[url] || url;
// relative
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
var path
// relative to path
if ('.' == url[0]) {
path = req.originalUrl.split('?')[0]
url = path + ('/' == path[path.length - 1] ? '' : '/') + url;
// relative to mount-point
} else if ('/' != url[0]) {
path = app.path();
url = path + '/' + url;
}
}
// Respond
this.set('Location', url);
return this;
};
/**
* 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 "/".
* The resulting `url` is determined by `res.location()`, so
* it will play nicely with mounted apps, relative paths,
* `"back"` etc.
*
* Examples:
*
@@ -601,19 +668,6 @@ res.cookie = function(name, val, options){
* res.redirect('http://example.com', 301);
* 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');
*
* @param {String} url
* @param {Number} code
* @api public
@@ -621,8 +675,7 @@ res.cookie = function(name, val, options){
res.redirect = function(url){
var app = this.app
, req = this.req
, head = 'HEAD' == req.method
, head = 'HEAD' == this.req.method
, status = 302
, body;
@@ -636,29 +689,14 @@ res.redirect = function(url){
}
}
// setup redirect map
var map = { back: req.get('Referrer') || '/' };
// perform redirect
url = map[url] || url;
// relative
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
var path = app.path();
// relative to path
if ('.' == url[0]) {
url = req.path + '/' + url;
// relative to mount-point
} else if ('/' != url[0]) {
url = path + '/' + url;
}
}
// 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(){
@@ -673,7 +711,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);
};
@@ -716,4 +753,4 @@ res.render = function(view, options, fn){
// render
app.render(view, options, fn);
};
};

View File

@@ -4,9 +4,9 @@
var Route = require('./route')
, utils = require('../utils')
, methods = require('methods')
, debug = require('debug')('express:router')
, parse = require('connect').utils.parseUrl
, methods = require('methods');
, parse = require('connect').utils.parseUrl;
/**
* Expose `Router` constructor.
@@ -16,7 +16,7 @@ exports = module.exports = Router;
/**
* Initialize a new `Router` with the given `options`.
*
*
* @param {Object} options
* @api private
*/
@@ -93,8 +93,7 @@ Router.prototype._dispatch = function(req, res, next){
, paramVal
, route
, keys
, key
, ret;
, key;
// match next route
function nextRoute(err) {
@@ -141,7 +140,7 @@ Router.prototype._dispatch = function(req, res, next){
};
param(err);
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
@@ -245,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

@@ -4,7 +4,7 @@
*/
var mime = require('connect').mime
, crc = require('crc');
, crc32 = require('buffer-crc32');
/**
* Return ETag for `body`.
@@ -15,15 +15,13 @@ var mime = require('connect').mime
*/
exports.etag = function(body){
return '"' + (Buffer.isBuffer(body)
? crc.buffer.crc32(body)
: crc.crc32(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}
@@ -31,8 +29,6 @@ exports.etag = function(body){
*/
exports.locals = function(obj){
obj.viewCallbacks = obj.viewCallbacks || [];
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
@@ -79,12 +75,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: {} };
};
/**
@@ -99,9 +97,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;
@@ -127,7 +123,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];
}
}
@@ -152,17 +148,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
@@ -177,7 +190,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];
@@ -188,18 +201,18 @@ 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;
})
@@ -209,23 +222,28 @@ exports.parseQuality = function(str){
};
/**
* Parse quality `str` returning an
* object with `.value` and `.quality`.
* Parse accept params `str` returning an
* object with `.value`, `.quality` and `.params`.
*
* @param {String} str
* @return {Object}
* @api private
*/
function quality(str) {
var parts = str.split(/ *; */)
, val = parts[0];
function acceptParams(str) {
var parts = str.split(/ *; */);
var ret = { value: parts[0], quality: 1, params: {} };
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;
}
/**
@@ -281,4 +299,4 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
.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,7 @@ 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) 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,25 +1,37 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.0.1",
"version": "3.1.2",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
{ "name": "Aaron Heckmann", "email": "aaron.heckmann+github@gmail.com" },
{ "name": "Ciaran Jessup", "email": "ciaranj@gmail.com" },
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
"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.6.2",
"connect": "2.7.5",
"commander": "0.6.1",
"range-parser": "0.0.4",
"mkdirp": "0.3.3",
"cookie": "0.0.4",
"crc": "0.2.0",
"mkdirp": "~0.3.4",
"cookie": "0.0.5",
"buffer-crc32": "~0.2.1",
"fresh": "0.1.0",
"methods": "0.0.1",
"send": "0.1.0",
"cookie-signature": "0.0.1",
"cookie-signature": "1.0.0",
"debug": "*"
},
"devDependencies": {
@@ -46,11 +58,14 @@
],
"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

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

@@ -61,5 +61,20 @@ describe('app', function(){
done();
})
})
it('should work "view engine" with leading "."', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.engine('.html', render);
app.set('view engine', '.html');
app.locals.user = { name: 'tobi' };
app.render('user', function(err, str){
if (err) return done(err);
str.should.equal('<p>tobi</p>');
done();
})
})
})
})

View File

@@ -596,4 +596,9 @@ describe('app.router', function(){
.get('/account/edit')
.expect('editing user 12', done);
})
it('should be chainable', function(){
var app = express();
app.get('/', function(){}).should.equal(app);
})
})

View File

@@ -15,7 +15,7 @@ describe('req', function(){
.get('/')
.expect('yes', done);
})
it('should return true when present', function(done){
var app = express();
@@ -28,7 +28,7 @@ describe('req', function(){
.set('Accept', 'application/json')
.expect('yes', done);
})
it('should return false otherwise', function(done){
var app = express();

View File

@@ -17,7 +17,7 @@ describe('req', function(){
.expect('yes', done);
})
})
describe('when Accept-Charset is not present', function(){
it('should return true when present', function(done){
var app = express();
@@ -31,7 +31,7 @@ describe('req', function(){
.set('Accept-Charset', 'foo, bar, utf-8')
.expect('yes', done);
})
it('should return false otherwise', function(done){
var app = express();

View File

@@ -48,6 +48,36 @@ describe('req', function(){
})
})
describe('when encoded string is malformed', function(){
it('should return undefined', function(done){
var app = express();
app.get('/', function(req, res){
res.send(req.auth || 'none');
});
request(app)
.get('/')
.set('Authorization', 'Basic Z21ldGh2aW4=')
.expect('none', done)
})
})
describe('when password contains a colon', function(){
it('should return .username and .password', function(done){
var app = express();
app.get('/', function(req, res){
res.send(req.auth || 'none');
});
request(app)
.get('/')
.set('Authorization', 'Basic dG9iaTpmZXJyZXQ6ZmVycmV0')
.expect('{"username":"tobi","password":"ferret:ferret"}', done)
})
})
it('should return .username and .password', function(done){
var app = express();

View File

@@ -1,18 +0,0 @@
var express = require('../');
function req(ret) {
return {
get: function(){ return ret }
, __proto__: express.request
};
}
describe('req', function(){
describe('.host', function(){
it('should return hostname', function(){
req('example.com:3000').host.should.equal('example.com');
req('example.com').host.should.equal('example.com');
})
})
})

View File

@@ -9,6 +9,10 @@ describe('req', function(){
, cookieHeader
, val;
/* So we use the same serialization for expected results. */
var replacer = app.get('json replacer')
, spaces = app.get('json spaces');
app.use(express.cookieParser('secret'));
app.use(function(req, res){
@@ -19,7 +23,7 @@ describe('req', function(){
app.response.cookie('obj', { foo: 'bar' }, { signed: true });
cookieHeader = app.response.get('set-cookie');
val = JSON.stringify({ obj: { foo: 'bar' } });
val = JSON.stringify({ obj: { foo: 'bar' } }, replacer, spaces);
request(app)
.get('/')
.set('Cookie', cookieHeader)
@@ -31,6 +35,10 @@ describe('req', function(){
, cookieHeader
, val;
/* So we use the same serialization for expected results. */
var replacer = app.get('json replacer')
, spaces = app.get('json spaces');
app.use(express.cookieParser('secret'));
app.use(function(req, res){
@@ -41,7 +49,7 @@ describe('req', function(){
app.response.cookie('foo', 'bar', { signed: true });
cookieHeader = app.response.get('set-cookie');
val = JSON.stringify({ foo: 'bar' });
val = JSON.stringify({ foo: 'bar' }, replacer, spaces);
request(app)
.get('/')
.set('Cookie', cookieHeader)

View File

@@ -33,5 +33,55 @@ describe('req', function(){
.expect('[]', done);
})
})
describe('when subdomain offset is set', function(){
describe('when subdomain offset is zero', function(){
it('should return an array with the whole domain', function(done){
var app = express();
app.set('subdomain offset', 0);
app.use(function(req, res){
res.send(req.subdomains);
});
request(app)
.get('/')
.set('Host', 'tobi.ferrets.sub.example.com')
.expect('["com","example","sub","ferrets","tobi"]', done);
})
})
describe('when present', function(){
it('should return an array', function(done){
var app = express();
app.set('subdomain offset', 3);
app.use(function(req, res){
res.send(req.subdomains);
});
request(app)
.get('/')
.set('Host', 'tobi.ferrets.sub.example.com')
.expect('["ferrets","tobi"]', done);
})
})
describe('otherwise', function(){
it('should return an empty array', function(done){
var app = express();
app.set('subdomain offset', 3);
app.use(function(req, res){
res.send(req.subdomains);
});
request(app)
.get('/')
.set('Host', 'sub.example.com')
.expect('[]', done);
})
})
})
})
})

View File

@@ -1,6 +1,7 @@
var express = require('../')
, request = require('./support/http')
, utils = require('connect').utils
, cookie = require('cookie');
describe('res', function(){
@@ -38,7 +39,7 @@ describe('res', function(){
done();
})
})
it('should allow multiple calls', function(done){
var app = express();
@@ -57,7 +58,7 @@ describe('res', function(){
})
})
})
describe('.cookie(name, string, options)', function(){
it('should set params', function(done){
var app = express();
@@ -75,7 +76,7 @@ describe('res', function(){
done();
})
})
describe('maxAge', function(){
it('should set relative expires', function(done){
var app = express();
@@ -92,6 +93,41 @@ describe('res', function(){
done();
})
})
it('should set max-age', function(done){
var app = express();
app.use(function(req, res){
res.cookie('name', 'tobi', { maxAge: 1000 });
res.end();
});
request(app)
.get('/')
.end(function(err, res){
res.headers['set-cookie'][0].should.include('Max-Age=1');
done();
})
})
it('should not mutate the options object', function(done){
var app = express();
var options = { maxAge: 1000 };
var optionsCopy = utils.merge({}, options);
app.use(function(req, res){
res.cookie('name', 'tobi', options)
res.end();
});
request(app)
.get('/')
.end(function(err, res){
options.should.eql(optionsCopy);
done();
})
})
})
describe('signed', function(){
@@ -128,7 +164,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
var val = ['name=s%3Atobi.xJjV2iZ6EI7C8E5kzwbfA9PVLl1ZR07UTnuTgQQ4EnQ; Path=/'];
var val = ['name=s%3Atobi.z6Nfs5haoP4UAqCcr497ZljiIbrNqn8EjjM10baIYvvG32lReGseC3v2T79fecY4G%2FLdEm9fpTihvlEMVuNjGw; Path=/'];
res.headers['set-cookie'].should.eql(val);
done();
})

View File

@@ -71,6 +71,22 @@ describe('res', function(){
})
})
it('should escape utf whitespace', function(done){
var app = express();
app.use(function(req, res){
res.jsonp({ str: '\u2028 \u2029 woot' });
});
request(app)
.get('/?callback=foo')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('foo && foo({"str":"\\u2028 \\u2029 woot"});');
done();
});
});
describe('when given primitives', function(){
it('should respond with json', function(done){
var app = express();

171
test/res.location.js Normal file
View File

@@ -0,0 +1,171 @@
var express = require('../')
, request = require('./support/http');
describe('res', function(){
describe('.location(url)', function(){
it('should set the header', function(done){
var app = express();
app.use(function(req, res){
res.location('http://google.com').end();
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('location', 'http://google.com');
done();
})
})
describe('with leading //', function(){
it('should pass through scheme-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('//cuteoverload.com').end();
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('location', '//cuteoverload.com');
done();
})
})
})
describe('with leading /', function(){
it('should construct scheme-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('/login').end();
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('location', '/login');
done();
})
})
})
describe('with leading ./', function(){
it('should construct path-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('./edit').end();
});
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/./edit');
done();
})
})
})
describe('with leading ../', function(){
it('should construct path-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('../new').end();
});
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/../new');
done();
})
})
})
describe('without leading /', function(){
it('should construct mount-point relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('login').end();
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('location', '/login');
done();
})
})
})
describe('when mounted', function(){
describe('deeply', function(){
it('should respect the mount-point', function(done){
var app = express()
, blog = express()
, admin = express();
admin.use(function(req, res){
res.location('login').end();
});
app.use('/blog', blog);
blog.use('/admin', admin);
request(app)
.get('/blog/admin')
.end(function(err, res){
res.headers.should.have.property('location', '/blog/admin/login');
done();
})
})
})
describe('omitting leading /', function(){
it('should respect the mount-point', function(done){
var app = express()
, admin = express();
admin.use(function(req, res){
res.location('admin/login').end();
});
app.use('/blog', admin);
request(app)
.get('/blog')
.end(function(err, res){
res.headers.should.have.property('location', '/blog/admin/login');
done();
})
})
})
describe('providing leading /', function(){
it('should ignore mount-point', function(done){
var app = express()
, admin = express();
admin.use(function(req, res){
res.location('/admin/login').end();
});
app.use('/blog', admin);
request(app)
.get('/blog')
.end(function(err, res){
res.headers.should.have.property('location', '/admin/login');
done();
})
})
})
})
})
})

View File

@@ -19,164 +19,6 @@ describe('res', function(){
done();
})
})
describe('with leading //', function(){
it('should pass through scheme-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.redirect('//cuteoverload.com');
});
request(app)
.get('/')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '//cuteoverload.com');
done();
})
})
})
describe('with leading /', function(){
it('should construct scheme-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.redirect('/login');
});
request(app)
.get('/')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '/login');
done();
})
})
})
describe('with leading ./', function(){
it('should construct path-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.redirect('./edit');
});
request(app)
.get('/post/1')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/./edit');
done();
})
})
})
describe('with leading ../', function(){
it('should construct path-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.redirect('../new');
});
request(app)
.get('/post/1')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/../new');
done();
})
})
})
describe('without leading /', function(){
it('should construct mount-point relative urls', function(done){
var app = express();
app.use(function(req, res){
res.redirect('login');
});
request(app)
.get('/')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '/login');
done();
})
})
})
describe('when mounted', function(){
describe('deeply', function(){
it('should respect the mount-point', function(done){
var app = express()
, blog = express()
, admin = express();
admin.use(function(req, res){
res.redirect('login');
});
app.use('/blog', blog);
blog.use('/admin', admin);
request(app)
.get('/blog/admin')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '/blog/admin/login');
done();
})
})
})
describe('omitting leading /', function(){
it('should respect the mount-point', function(done){
var app = express()
, admin = express();
admin.use(function(req, res){
res.redirect('admin/login');
});
app.use('/blog', admin);
request(app)
.get('/blog')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '/blog/admin/login');
done();
})
})
})
describe('providing leading /', function(){
it('should ignore mount-point', function(done){
var app = express()
, admin = express();
admin.use(function(req, res){
res.redirect('/admin/login');
});
app.use('/blog', admin);
request(app)
.get('/blog')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '/admin/login');
done();
})
})
})
})
})
describe('.redirect(status, url)', function(){
@@ -232,7 +74,7 @@ describe('res', function(){
})
})
})
describe('when accepting html', function(){
it('should respond with html', function(done){
var app = express();
@@ -268,7 +110,7 @@ describe('res', function(){
})
})
})
describe('when accepting text', function(){
it('should respond with text', function(done){
var app = express();
@@ -287,6 +129,23 @@ describe('res', function(){
done();
})
})
it('should encode the url', function(done){
var app = express();
app.use(function(req, res){
res.redirect('http://example.com/?param=<script>alert("hax");</script>');
});
request(app)
.get('/')
.set('Host', 'http://example.com')
.set('Accept', 'text/plain, */*')
.end(function(err, res){
res.text.should.equal('Moved Temporarily. Redirecting to http://example.com/?param=%3Cscript%3Ealert(%22hax%22);%3C/script%3E');
done();
})
})
})
describe('when accepting neither text or html', function(){
@@ -310,4 +169,76 @@ describe('res', function(){
})
})
})
describe('responses redirected to relative paths', function(){
function create(depth, parent) {
var app = express();
if (parent) {
parent.use('/depth' + depth, app);
}
app.get('/', function(req, res){
res.redirect('./index');
});
app.get('/index', function(req, res){
res.json({ depth: depth, content: 'index' });
});
return app;
}
var root = create(0);
var depth1 = create(1, root);
var depth2 = create(2, depth1);
var depth3 = create(3, depth2);
root.use('/depth2', depth2);
root.use('/depth3', depth3);
it('should not contain redundant leading slashes in the location header', function(done){
request(root)
.get('/')
.end(function(err, res){
res.headers.location.search(/^\/{2}/).should.equal(-1);
done();
})
})
it('should preserve context when redirecting nested applications at any depth', function(done){
request(root)
.get('/depth1')
.end(function(err, res){
res.headers.should.have.property('location', '/depth1/./index');
request(root)
.get('/depth1/depth2')
.end(function(err, res){
res.headers.should.have.property('location', '/depth1/depth2/./index');
request(root)
.get('/depth1/depth2/depth3')
.end(function(err, res){
res.headers.should.have.property('location', '/depth1/depth2/depth3/./index');
done();
})
})
});
})
it('should redirect correctly for nested applications that have been remounted', function(done){
request(root)
.get('/depth2')
.end(function(err, res){
res.headers.should.have.property('location', '/depth2/./index');
request(root)
.get('/depth3')
.end(function(err, res){
res.headers.should.have.property('location', '/depth3/./index');
done();
})
})
})
})
})

View File

@@ -206,11 +206,11 @@ describe('res', function(){
})
describe('when .statusCode is 204', function(){
it('should strip Content-* fields & body', function(done){
it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){
var app = express();
app.use(function(req, res){
res.status(204).send('foo');
res.status(204).set('Transfer-Encoding', 'chunked').send('foo');
});
request(app)
@@ -218,6 +218,7 @@ describe('res', function(){
.end(function(err, res){
res.headers.should.not.have.property('content-type');
res.headers.should.not.have.property('content-length');
res.headers.should.not.have.property('transfer-encoding');
res.text.should.equal('');
done();
})
@@ -225,11 +226,11 @@ describe('res', function(){
})
describe('when .statusCode is 304', function(){
it('should strip Content-* fields & body', function(done){
it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){
var app = express();
app.use(function(req, res){
res.status(304).send('foo');
res.status(304).set('Transfer-Encoding', 'chunked').send('foo');
});
request(app)
@@ -237,6 +238,7 @@ describe('res', function(){
.end(function(err, res){
res.headers.should.not.have.property('content-type');
res.headers.should.not.have.property('content-length');
res.headers.should.not.have.property('transfer-encoding');
res.text.should.equal('');
done();
})

View File

@@ -127,7 +127,7 @@ describe('res', function(){
});
})
})
describe('with a relative path', function(){
it('should transfer the file', function(done){
var app = express();
@@ -144,7 +144,7 @@ describe('res', function(){
done();
});
})
it('should serve relative to "root"', function(done){
var app = express();
@@ -160,7 +160,7 @@ describe('res', function(){
done();
});
})
it('should consider ../ malicious when "root" is not set', function(done){
var app = express();
@@ -172,7 +172,7 @@ describe('res', function(){
.get('/')
.expect(403, done);
})
it('should allow ../ when "root" is set', function(done){
var app = express();
@@ -184,7 +184,7 @@ describe('res', function(){
.get('/')
.expect(200, done);
})
it('should disallow requesting out of "root"', function(done){
var app = express();
@@ -196,7 +196,7 @@ describe('res', function(){
.get('/')
.expect(403, done);
})
it('should next(404) when not found', function(done){
var app = express()
, calls = 0;
@@ -208,7 +208,7 @@ describe('res', function(){
app.use(function(req, res){
assert(0, 'this should not be called');
});
app.use(function(err, req, res, next){
++calls;
next(err);

View File

@@ -24,6 +24,27 @@ describe('res', function(){
res.get('ETag').should.equal('123');
})
})
describe('.set(field, values)', function(){
it('should set multiple response header fields', function(done){
var app = express();
app.use(function(req, res){
res.set('Set-Cookie', ["type=ninja", "language=javascript"]);
res.send(res.get('Set-Cookie'));
});
request(app)
.get('/')
.expect('["type=ninja","language=javascript"]', done);
})
it('should coerce to an array of strings', function(){
res.headers = {};
res.set('ETag', [123, 456]);
JSON.stringify(res.get('ETag')).should.equal('["123","456"]');
})
})
describe('.set(object)', function(){
it('should set multiple fields', function(done){

View File

@@ -2,6 +2,26 @@
var utils = require('../lib/utils')
, assert = require('assert');
describe('utils.etag(body)', function(){
var str = 'Hello CRC';
var strUTF8 = '<!DOCTYPE html>\n<html>\n<head>\n</head>\n<body><p>自動販売</p></body></html>';
it('should support strings', function(){
utils.etag(str).should.eql('"-2034458343"');
})
it('should support utf8 strings', function(){
utils.etag(strUTF8).should.eql('"1395090196"');
})
it('should support buffer', function(){
utils.etag(new Buffer(strUTF8)).should.eql('"1395090196"');
utils.etag(new Buffer(str)).should.eql('"-2034458343"');
})
})
describe('utils.isAbsolute()', function(){
it('should support windows', function(){
assert(utils.isAbsolute('c:\\'));
@@ -29,28 +49,38 @@ describe('utils.escape(html)', function(){
})
})
describe('utils.parseQuality(str)', function(){
describe('utils.parseParams(str)', function(){
it('should default quality to 1', function(){
utils.parseQuality('text/html')
.should.eql([{ value: 'text/html', quality: 1 }]);
utils.parseParams('text/html')
.should.eql([{ value: 'text/html', quality: 1, params: {}}]);
})
it('should parse qvalues', function(){
utils.parseQuality('text/html; q=0.5')
.should.eql([{ value: 'text/html', quality: 0.5 }]);
utils.parseParams('text/html; q=0.5')
.should.eql([{ value: 'text/html', quality: 0.5, params: {}}]);
utils.parseQuality('text/html; q=.2')
.should.eql([{ value: 'text/html', quality: 0.2 }]);
utils.parseParams('text/html; q=.2')
.should.eql([{ value: 'text/html', quality: 0.2, params: {}}]);
})
it('should parse accept parameters', function(){
utils.parseParams('application/json; ver=2.0')
.should.eql([{ value: 'application/json', quality: 1, params: {ver: "2.0"}}]);
utils.parseParams('text/html; q=0.5; level=2')
.should.eql([{ value: 'text/html', quality: 0.5, params: {level: "2"}}]);
utils.parseParams('text/html;q=.2;ver=beta')
.should.eql([{ value: 'text/html', quality: 0.2, params: {ver: "beta"}}]);
})
it('should work with messed up whitespace', function(){
utils.parseQuality('text/html ; q = .2')
.should.eql([{ value: 'text/html', quality: 0.2 }]);
utils.parseParams('text/html ; q = .2')
.should.eql([{ value: 'text/html', quality: 0.2, params: {}}]);
})
it('should work with multiples', function(){
var str = 'da, en;q=.5, en-gb;q=.8';
var arr = utils.parseQuality(str);
var arr = utils.parseParams(str);
arr[0].value.should.equal('da');
arr[1].value.should.equal('en-gb');
arr[2].value.should.equal('en');
@@ -58,7 +88,7 @@ describe('utils.parseQuality(str)', function(){
it('should sort by quality', function(){
var str = 'text/plain;q=.2, application/json, text/html;q=0.5';
var arr = utils.parseQuality(str);
var arr = utils.parseParams(str);
arr[0].value.should.equal('application/json');
arr[1].value.should.equal('text/html');
arr[2].value.should.equal('text/plain');
@@ -66,7 +96,7 @@ describe('utils.parseQuality(str)', function(){
it('should exclude those with a quality of 0', function(){
var str = 'text/plain;q=.2, application/json, text/html;q=0';
var arr = utils.parseQuality(str);
var arr = utils.parseParams(str);
arr.should.have.length(2);
})
})