Compare commits

...

272 Commits
3.1.0 ... 3.4.8

Author SHA1 Message Date
Jonathan Ong
ff23423d34 3.4.8 2014-01-13 20:50:51 -08:00
Doug Patti
1d97599f8b prevent incorrect automatic OPTIONS responses
The router has automatic handling of OPTIONS based on the registered
routes, but if you make an OPTIONS request for an endpoint that does
not exist, then it will still return a 200 with nothing allowed.
Instead, we can let the request move on down the middleware chain. This
has two benefits: first, if the route was not defined and no other
middleware handles it, it will return with a 404. Secondly, if multiple
routers are used and a later one has the route or a custom OPTIONS
defined, the first router will not respond incorrectly.
2014-01-13 20:46:07 -08:00
Matheus Azzi
e465624fd0 Update layout.jade 2014-01-13 20:45:50 -08:00
TJ Holowaychuk
dc5932d177 Merge pull request #1877 from reqshark/master
update express jade layout generator
2013-12-23 10:34:47 -08:00
Bent Cardan
cfd93b7529 update express jade layout generator
update doctype
2013-12-23 13:03:21 -05:00
Jonathan Ong
8b2208f394 Merge pull request #1876 from yosssi/dev
Updated the example file to use `doctype html` on  because  `doctype 5` was deprecated on Jade version 1.0.0.
2013-12-23 08:10:26 -08:00
yosssi
00a3b01f39 Changed doctype 5 to doctype html on the example file because the former was deprecated on Jade version 1.0.0. 2013-12-23 22:13:05 +09:00
TJ Holowaychuk
3baca251f0 use 8 threads for benchmarks 2013-12-22 08:57:04 -08:00
TJ Holowaychuk
72daae1d92 Merge pull request #1869 from yamatt/master
Error message now describes where the view was not able to be found.
2013-12-21 11:13:04 -08:00
Matt Copperwaite
fcbe53ddb5 Added appropriate test for more descriptive render error 2013-12-21 17:34:59 +00:00
Jonathan Ong
e9851672eb bench: remove --harmony-generators flag 2013-12-20 21:15:17 -08:00
TJ Holowaychuk
9a45f7bd3d add new benchmarks (to match koa) 2013-12-20 19:34:59 -08:00
Matt Copperwaite
85834fd146 Error message now describes where the view was not able to be found. Useful for debugging. 2013-12-20 11:39:31 +00:00
Roman Shtylman
a0c1ac7b45 add license field to package.json
close #1862
2013-12-18 10:16:56 -05:00
Alex Kocharin
7b0dca0f9c throw 400 in case of malformed paths 2013-12-11 17:14:44 -08:00
Jonathan Ong
34c83d7d29 3.4.7 2013-12-10 23:57:39 -08:00
Jonathan Ong
7c6882234e bump connect, mocha, and should 2013-12-10 23:54:07 -08:00
Jonathan Ong
2e68ddbae9 expose connect.middleware using Object.getOwnPropertyDescriptor()
closes #1853. no tests, but it should be fine.
2013-12-10 23:52:48 -08:00
Jonathan Ong
7724fc6af7 3.4.6 2013-12-01 12:21:08 -08:00
Roman Shtylman
2939075f03 Merge pull request #1836 from fluxusfrequency/patch-1
Grammar and punctuation fixes [ci skip]
2013-11-28 08:18:42 -08:00
Ben Lewis
606f68de02 Grammar and punctuation fixes [ci skip] 2013-11-28 06:36:21 -07:00
Jonathan Ong
863160ae49 3.4.5 2013-11-27 15:54:41 -08:00
TJ Holowaychuk
edd39fb194 fix weird variable name in example 2013-11-26 23:39:35 -08:00
TJ Holowaychuk
a71d264d45 fix weird variable name in example 2013-11-26 23:39:08 -08:00
TJ Holowaychuk
8a7a695836 ocd 2013-11-26 11:12:56 -08:00
TJ Holowaychuk
de54af4061 Merge pull request #1829 from michaelficarra/patch-1
fixes #1826: res.redirect('toString') fails with 500
2013-11-26 11:12:13 -08:00
Michael Ficarra
2f2a652bc9 fixes #1826: res.redirect('toString') fails with 500
Removed the unused map and corrected the doc comment.
2013-11-26 13:11:15 -06:00
TJ Holowaychuk
1e638663de Merge pull request #1822 from yakubori/auth-buffer-call-removal
Removed Buffer call with 'binary' encoding option in auth example.
2013-11-21 12:32:39 -08:00
Rick Yakubowski
1684a8792a Removed Buffer call with 'binary' encoding option in auth example.
According to the Node.js documentation for Buffer objects regarding the
'binary' encoding option:

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

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

0
.gitmodules vendored
View File

View File

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

View File

@@ -1,3 +1,186 @@
4.0.0 /
==================
* remove:
- express(1) - moved to [express-generator](https://github.com/expressjs/generator)
- `req.accepted*` - use `req.accepts*()` instead
- `app.configure` - use logic in your own app code
* change:
- `req.accepts*` -> `req.accepts*s` - i.e. `req.acceptsEncoding` -> `req.acceptsEncodings`
- `req.params` is now an object instead of an array
- `json spaces` no longer enabled by default in development
* refactor:
- `req.accepts*` with [accepts](https://github.com/expressjs/accepts)
- `req.is` with [type-is](https://github.com/expressjs/type-is)
3.4.8 / 2014-01-13
==================
* prevent incorrect automatic OPTIONS responses #1868 @dpatti
* update binary and examples for jade 1.0 #1876 @yossi, #1877 @reqshark, #1892 @matheusazzi
* throw 400 in case of malformed paths @rlidwka
3.4.7 / 2013-12-10
==================
* update connect
3.4.6 / 2013-12-01
==================
* update connect (raw-body)
3.4.5 / 2013-11-27
==================
* update connect
* res.location: remove leading ./ #1802 @kapouer
* res.redirect: fix `res.redirect('toString') #1829 @michaelficarra
* res.send: always send ETag when content-length > 0
* router: add Router.all() method
3.4.4 / 2013-10-29
==================
* update connect
* update supertest
* update methods
* express(1): replace bodyParser() with urlencoded() and json() #1795 @chirag04
3.4.3 / 2013-10-23
==================
* update connect
3.4.2 / 2013-10-18
==================
* update connect
* downgrade commander
3.4.1 / 2013-10-15
==================
* update connect
* update commander
* jsonp: check if callback is a function
* router: wrap encodeURIComponent in a try/catch #1735 (@lxe)
* res.format: now includes chraset @1747 (@sorribas)
* res.links: allow multiple calls @1746 (@sorribas)
3.4.0 / 2013-09-07
==================
* add res.vary(). Closes #1682
* update connect
3.3.8 / 2013-09-02
==================
* update connect
3.3.7 / 2013-08-28
==================
* update connect
3.3.6 / 2013-08-27
==================
* Revert "remove charset from json responses. Closes #1631" (causes issues in some clients)
* add: req.accepts take an argument list
3.3.4 / 2013-07-08
==================
* update send and connect
3.3.3 / 2013-07-04
==================
* update connect
3.3.2 / 2013-07-03
==================
* update connect
* update send
* remove .version export
3.3.1 / 2013-06-27
==================
* update connect
3.3.0 / 2013-06-26
==================
* update connect
* add support for multiple X-Forwarded-Proto values. Closes #1646
* change: remove charset from json responses. Closes #1631
* change: return actual booleans from req.accept* functions
* fix jsonp callback array throw
3.2.6 / 2013-06-02
==================
* update connect
3.2.5 / 2013-05-21
==================
* update connect
* 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
==================

View File

@@ -1,6 +1,6 @@
(The MIT License)
Copyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2009-2013 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,5 +1,5 @@
MOCHA_OPTS=
MOCHA_OPTS= --check-leaks
REPORTER = dot
check: test
@@ -9,6 +9,7 @@ test: test-unit test-acceptance
test-unit:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--globals setImmediate,clearImmediate \
$(MOCHA_OPTS)
test-acceptance:
@@ -23,11 +24,11 @@ test-cov: lib-cov
lib-cov:
@jscoverage lib lib-cov
benchmark:
@./support/bench
bench:
@$(MAKE) -C benchmarks
clean:
rm -f coverage.html
rm -fr lib-cov
.PHONY: test test-unit test-acceptance benchmark clean
.PHONY: test test-unit test-acceptance bench clean

View File

@@ -1,6 +1,8 @@
![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)
[![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)](http://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). [![Build Status](https://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) [![Gittip](http://img.shields.io/gittip/visionmedia.png)](https://www.gittip.com/visionmedia/)
```js
var express = require('express');
@@ -48,112 +50,57 @@ app.listen(3000);
## Philosophy
The Express philosophy is to provide small, robust tooling for HTTP servers. Making
The Express philosophy is to provide small, robust tooling for HTTP servers, making
it a great solution for single page applications, web sites, hybrids, or public
HTTP APIs.
Built on Connect you can use _only_ what you need, and nothing more, applications
Built on Connect, you can use _only_ what you need, and nothing more. Applications
can be as big or as small as you like, even a single file. Express does
not force you to use any specific ORM or template engine. With support for over
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js)
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js),
you can quickly craft your perfect framework.
## More Information
* [Website and Documentation](http://expressjs.com/) stored at [visionmedia/expressjs.com](https://github.com/visionmedia/expressjs.com)
* Join #express on freenode
* [Google Group](http://groups.google.com/group/express-js) for discussion
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
* 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
Clone the Express repo, then install the dev dependencies to install all the example / test suite deps:
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
$ git clone git://github.com/visionmedia/express.git --depth 1
$ cd express
$ npm install
then run whichever tests you want:
Then run whichever tests you want:
$ node examples/content-negotiation
You can also view live examples here:
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
## Running Tests
To run the test suite first invoke the following command within the repo, installing the development dependencies:
To run the test suite, first invoke the following command within the repo, installing the development dependencies:
$ npm install
then run the tests:
Then run the tests:
$ make test
## Contributors
```
project: express
commits: 3559
active : 468 days
files : 237
authors:
1891 Tj Holowaychuk 53.1%
1285 visionmedia 36.1%
182 TJ Holowaychuk 5.1%
54 Aaron Heckmann 1.5%
34 csausdev 1.0%
26 ciaranj 0.7%
21 Robert Sköld 0.6%
6 Guillermo Rauch 0.2%
3 Dav Glass 0.1%
3 Nick Poulden 0.1%
2 Randy Merrill 0.1%
2 Benny Wong 0.1%
2 Hunter Loftis 0.1%
2 Jake Gordon 0.1%
2 Brian McKinney 0.1%
2 Roman Shtylman 0.1%
2 Ben Weaver 0.1%
2 Dave Hoover 0.1%
2 Eivind Fjeldstad 0.1%
2 Daniel Shaw 0.1%
1 Matt Colyer 0.0%
1 Pau Ramon 0.0%
1 Pero Pejovic 0.0%
1 Peter Rekdal Sunde 0.0%
1 Raynos 0.0%
1 Teng Siong Ong 0.0%
1 Viktor Kelemen 0.0%
1 ctide 0.0%
1 8bitDesigner 0.0%
1 isaacs 0.0%
1 mgutz 0.0%
1 pikeas 0.0%
1 shuwatto 0.0%
1 tstrimple 0.0%
1 ewoudj 0.0%
1 Adam Sanderson 0.0%
1 Andrii Kostenko 0.0%
1 Andy Hiew 0.0%
1 Arpad Borsos 0.0%
1 Ashwin Purohit 0.0%
1 Benjen 0.0%
1 Darren Torpey 0.0%
1 Greg Ritter 0.0%
1 Gregory Ritter 0.0%
1 James Herdman 0.0%
1 Jim Snodgrass 0.0%
1 Joe McCann 0.0%
1 Jonathan Dumaine 0.0%
1 Jonathan Palardy 0.0%
1 Jonathan Zacsh 0.0%
1 Justin Lilly 0.0%
1 Ken Sato 0.0%
1 Maciej Małecki 0.0%
1 Masahiro Hayashi 0.0%
```
https://github.com/visionmedia/express/graphs/contributors
## License
## License
(The MIT License)

13
benchmarks/Makefile Normal file
View File

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

23
benchmarks/middleware.js Normal file
View File

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

16
benchmarks/run Executable file
View File

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

View File

@@ -4,8 +4,7 @@
* Module dependencies.
*/
var exec = require('child_process').exec
, program = require('commander')
var program = require('commander')
, mkdirp = require('mkdirp')
, pkg = require('../package.json')
, version = pkg.version
@@ -16,6 +15,7 @@ var exec = require('child_process').exec
program
.version(version)
.usage('[options] [dir]')
.option('-s, --sessions', 'add session support')
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
@@ -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
@@ -74,7 +74,7 @@ var users = [
*/
var jadeLayout = [
'doctype 5'
'doctype html'
, 'html'
, ' head'
, ' title= title'
@@ -208,35 +208,36 @@ var app = [
, ' * Module dependencies.'
, ' */'
, ''
, 'var express = require(\'express\')'
, ' , routes = require(\'./routes\')'
, ' , user = require(\'./routes/user\')'
, ' , http = require(\'http\')'
, ' , path = require(\'path\');'
, 'var express = require(\'express\');'
, 'var routes = require(\'./routes\');'
, 'var user = require(\'./routes/user\');'
, 'var http = require(\'http\');'
, 'var 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(path.join(__dirname, \'public\')));'
, '});'
, '// all environments'
, 'app.set(\'port\', process.env.PORT || 3000);'
, 'app.set(\'views\', path.join(__dirname, \'views\'));'
, 'app.set(\'view engine\', \':TEMPLATE\');'
, 'app.use(express.favicon());'
, 'app.use(express.logger(\'dev\'));'
, 'app.use(express.json());'
, 'app.use(express.urlencoded());'
, 'app.use(express.methodOverride());{sess}'
, 'app.use(app.router);{css}'
, 'app.use(express.static(path.join(__dirname, \'public\')));'
, ''
, '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);
@@ -323,10 +324,10 @@ function createApplicationAt(path) {
// CSS Engine support
switch (program.css) {
case 'less':
app = app.replace('{css}', eol + ' app.use(require(\'less-middleware\')({ src: __dirname + \'/public\' }));');
app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));');
break;
case 'stylus':
app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware(__dirname + \'/public\'));');
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));');
break;
default:
app = app.replace('{css}', '');
@@ -334,7 +335,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
@@ -345,7 +346,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

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

View File

@@ -1,4 +1,4 @@
style
style.
body {
padding: 50px;
font: 16px "Helvetica Neue", Helvetica;

View File

@@ -1,8 +1,9 @@
var express = require('../../')
, app = module.exports = express()
, users = require('./db');
// so either you can deal with different types of formatting
// for expected response in index.js
app.get('/', function(req, res){
res.format({
html: function(){
@@ -24,10 +25,11 @@ app.get('/', function(req, res){
});
// or you could write a tiny middleware like
// this to abstract make things a bit more declarative:
// this to add a layer of abstraction
// and make things a bit more declarative:
function format(mod) {
var obj = require(mod);
function format(path) {
var obj = require(path);
return function(req, res){
res.format(obj);
}
@@ -38,4 +40,4 @@ app.get('/users', format('./users'));
if (!module.parent) {
app.listen(3000);
console.log('listening on port 3000');
}
}

View File

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

View File

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

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

@@ -1,4 +1,3 @@
var express = require('../..');
var app = module.exports = express();
@@ -58,10 +57,10 @@ app.use(function(req, res, next){
});
*/
next();
// empty or "flush" the messages so they
// don't build up
req.session.messages = [];
next();
});
// load controllers
@@ -90,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

@@ -2,7 +2,7 @@
html
head
title Search example
style
style.
body {
font: 14px "Helvetica Neue", Helvetica;
padding: 50px;

View File

@@ -1,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -6,14 +5,14 @@
var express = require('../..');
/*
edit /etc/vhosts:
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
// Main server app
var main = express();
@@ -24,7 +23,7 @@ main.get('/', function(req, res){
});
main.get('/:sub', function(req, res){
res.send('requsted ' + req.params.sub);
res.send('requested ' + req.params.sub);
});
// Redirect app
@@ -36,12 +35,12 @@ redirect.all('*', function(req, res){
res.redirect('http://example.com:3000/' + req.subdomains[0]);
});
// Main app
// Vhost app
var app = express();
app.use(express.vhost('*.example.com', redirect))
app.use(express.vhost('example.com', main));
app.use(express.vhost('*.example.com', redirect)) // Serves all subdomains via Redirect app
app.use(express.vhost('example.com', main)); // Serves top level domain via Main server app
app.listen(3000);
console.log('Express app started on port 3000');

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

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

View File

@@ -10,9 +10,7 @@ var connect = require('connect')
, locals = require('./utils').locals
, View = require('./view')
, utils = connect.utils
, path = require('path')
, http = require('http')
, join = path.join;
, http = require('http');
/**
* Application prototype.
@@ -34,7 +32,6 @@ app.init = function(){
this.cache = {};
this.settings = {};
this.engines = {};
this.viewCallbacks = [];
this.defaultConfiguration();
};
@@ -47,6 +44,7 @@ app.init = function(){
app.defaultConfiguration = function(){
// default settings
this.enable('x-powered-by');
this.enable('etag');
this.set('env', process.env.NODE_ENV || 'development');
this.set('subdomain offset', 2);
debug('booting in %s mode', this.get('env'));
@@ -60,6 +58,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
@@ -79,6 +78,7 @@ app.defaultConfiguration = function(){
this.locals.settings = this.settings;
// default configuration
this.set('view', View);
this.set('views', process.cwd() + '/views');
this.set('jsonp callback name', 'callback');
@@ -116,7 +116,6 @@ app.use = function(route, fn){
fn = function(req, res, next) {
var orig = req.app;
app.handle(req, res, function(err){
req.app = res.app = orig;
req.__proto__ = orig.request;
res.__proto__ = orig.response;
next(err);
@@ -161,7 +160,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 seeessly within Express.
* work seamlessly within Express.
*
* @param {String} ext
* @param {Function} fn
@@ -184,7 +183,7 @@ app.engine = function(ext, fn){
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the samesignature as middleware, the only differencing
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
@@ -250,11 +249,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;
@@ -405,7 +400,12 @@ methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
// if no router attacked yet, attach the router
// 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);
// setup route
@@ -483,14 +483,14 @@ app.render = function(name, options, fn){
// view
if (!view) {
view = new View(name, {
view = new (this.get('view'))(name, {
defaultEngine: this.get('view engine'),
root: this.get('views'),
engines: engines
});
if (!view.path) {
var err = new Error('Failed to lookup view "' + name + '"');
var err = new Error('Failed to lookup view "' + name + '" in views directory "' + view.root + '"');
err.view = view;
return fn(err);
}

View File

@@ -2,6 +2,7 @@
* Module dependencies.
*/
var merge = require('merge-descriptors');
var connect = require('connect')
, proto = require('./application')
, Route = require('./router/route')
@@ -16,12 +17,6 @@ var connect = require('connect')
exports = module.exports = createApplication;
/**
* Framework version.
*/
exports.version = '3.1.0';
/**
* Expose mime.
*/
@@ -38,8 +33,8 @@ exports.mime = connect.mime;
function createApplication() {
var app = connect();
utils.merge(app, proto);
app.request = { __proto__: req };
app.response = { __proto__: res };
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
@@ -49,12 +44,7 @@ function createApplication() {
* for example `express.logger` etc.
*/
for (var key in connect.middleware) {
Object.defineProperty(
exports
, key
, Object.getOwnPropertyDescriptor(connect.middleware, key));
}
merge(exports, connect.middleware);
/**
* Error on createServer().

View File

@@ -17,7 +17,6 @@ var utils = require('./utils');
exports.init = function(app){
return function expressInit(req, res, next){
req.app = res.app = app;
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
req.res = res;
res.req = req;

View File

@@ -63,6 +63,7 @@ req.header = function(name){
* The `type` value may be a single mime type string
* such as "application/json", the extension name
* such as "json", a comma-delimted list such as "json, html, text/plain",
* an argument list such as `"json", "html", "text/plain"`,
* or an array `["json", "html", "text/plain"]`. When a list
* or array is given the _best_ match, if any is returned.
*
@@ -89,6 +90,7 @@ req.header = function(name){
*
* // Accept: text/*;q=.5, application/json
* req.accepts(['html', 'json']);
* req.accepts('html', 'json');
* req.accepts('html, json');
* // => "json"
*
@@ -98,7 +100,20 @@ req.header = function(name){
*/
req.accepts = function(type){
return utils.accepts(type, this.get('Accept'));
var args = arguments.length > 1 ? [].slice.apply(arguments) : type;
return utils.accepts(args, 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);
};
/**
@@ -113,7 +128,7 @@ req.accepts = function(type){
req.acceptsCharset = function(charset){
var accepted = this.acceptedCharsets;
return accepted.length
? ~accepted.indexOf(charset)
? !! ~accepted.indexOf(charset)
: true;
};
@@ -129,7 +144,7 @@ req.acceptsCharset = function(charset){
req.acceptsLanguage = function(lang){
var accepted = this.acceptedLanguages;
return accepted.length
? ~accepted.indexOf(lang)
? !! ~accepted.indexOf(lang)
: true;
};
@@ -159,6 +174,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 +235,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 +259,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 +278,7 @@ req.__defineGetter__('acceptedCharsets', function(){
* the `connect.bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} defaultValue
* @param {Mixed} [defaultValue]
* @return {String}
* @api public
*/
@@ -315,11 +348,10 @@ req.is = function(type){
req.__defineGetter__('protocol', function(){
var trustProxy = this.app.get('trust proxy');
return this.connection.encrypted
? 'https'
: trustProxy
? (this.get('X-Forwarded-Proto') || 'http')
: 'http';
if (this.connection.encrypted) return 'https';
if (!trustProxy) return 'http';
var proto = this.get('X-Forwarded-Proto') || 'http';
return proto.split(/\s*,\s*/)[0];
});
/**
@@ -415,7 +447,7 @@ req.__defineGetter__('auth', function(){
req.__defineGetter__('subdomains', function(){
var offset = this.app.get('subdomain offset');
return this.get('Host')
return (this.host || '')
.split('.')
.reverse()
.slice(offset);
@@ -440,7 +472,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

@@ -14,9 +14,9 @@ var http = require('http')
, cookie = require('cookie')
, send = require('send')
, mime = connect.mime
, resolve = require('url').resolve
, basename = path.basename
, extname = path.extname
, join = path.join;
, extname = path.extname;
/**
* Response prototype.
@@ -55,7 +55,9 @@ res.status = function(code){
*/
res.links = function(links){
return this.set('Link', Object.keys(links).map(function(rel){
var link = this.get('Link') || '';
if (link) link += ', ';
return this.set('Link', link + Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
}).join(', '));
};
@@ -78,9 +80,12 @@ 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;
// settings
var app = this.app;
// allow status / body
if (2 == arguments.length) {
@@ -128,7 +133,7 @@ res.send = function(body){
// ETag support
// TODO: W/ support
if (len > 1024) {
if (app.settings.etag && len && 'GET' == req.method) {
if (!this.get('ETag')) {
this.set('ETag', etag(body));
}
@@ -234,9 +239,10 @@ res.jsonp = function(obj){
// jsonp
if (callback) {
if (Array.isArray(callback)) callback = callback[0];
this.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = cb + ' && ' + cb + '(' + body + ');';
body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');';
}
return this.send(body);
@@ -318,10 +324,10 @@ res.sendfile = function(path, options, fn){
}
// streaming
function stream() {
function stream(stream) {
if (done) return;
cleanup();
if (fn) self.on('finish', fn);
if (fn) stream.on('end', fn);
}
// cleanup
@@ -459,17 +465,20 @@ res.format = function(obj){
var key = req.accepts(keys);
this.set('Vary', 'Accept');
this.vary("Accept");
if (key) {
this.set('Content-Type', normalizeType(key));
var type = normalizeType(key).value;
var charset = mime.charsets.lookup(type);
if (charset) type += '; charset=' + charset;
this.set('Content-Type', type);
obj[key](req, this, next);
} else if (fn) {
fn();
} 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);
}
@@ -580,6 +589,7 @@ res.cookie = function(name, val, 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 = 's:' + sign(val, secret);
if ('maxAge' in options) {
@@ -595,8 +605,7 @@ res.cookie = function(name, val, options){
/**
* Set the location header to `url`.
*
* The given `url` can also be the name of a mapped url, for
* example by default express supports "back" which redirects
* The given `url` can also be "back", which redirects
* to the _Referrer_ or _Referer_ headers or "/".
*
* Examples:
@@ -624,23 +633,22 @@ res.cookie = function(name, val, options){
res.location = function(url){
var app = this.app
, req = this.req;
, req = this.req
, path;
// setup redirect map
var map = { back: req.get('Referrer') || '/' };
// perform redirect
url = map[url] || url;
// "back" is an alias for the referrer
if ('back' == url) url = req.get('Referrer') || '/';
// 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
path = req.originalUrl.split('?')[0];
path = path + ('/' == path[path.length - 1] ? '' : '/');
url = resolve(path, url);
// relative to mount-point
} else if ('/' != url[0]) {
path = app.path();
url = path + '/' + url;
}
}
@@ -672,8 +680,7 @@ res.location = function(url){
*/
res.redirect = function(url){
var app = this.app
, head = 'HEAD' == this.req.method
var head = 'HEAD' == this.req.method
, status = 302
, body;
@@ -713,6 +720,44 @@ res.redirect = function(url){
this.end(head ? null : body);
};
/**
* Add `field` to Vary. If already present in the Vary set, then
* this call is simply ignored.
*
* @param {Array|String} field
* @param {ServerResponse} for chaining
* @api public
*/
res.vary = function(field){
var self = this;
// nothing
if (!field) return this;
// array
if (Array.isArray(field)) {
field.forEach(function(field){
self.vary(field);
});
return;
}
var vary = this.get('Vary');
// append
if (vary) {
vary = vary.split(/ *, */);
if (!~vary.indexOf(field)) vary.push(field);
this.set('Vary', vary.join(', '));
return this;
}
// set
this.set('Vary', field);
return this;
};
/**
* Render `view` with the given `options` and optional callback `fn`.
* When a callback function is given a response will _not_ be made

View File

@@ -103,6 +103,9 @@ Router.prototype._dispatch = function(req, res, next){
// match route
req.route = route = self.matchRequest(req, i);
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res, next);
// no route
if (!route) return next(err);
debug('matched %s %s', route.method, route.path);
@@ -170,6 +173,42 @@ 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, next){
var path = parse(req).pathname
, body = this._optionsFor(path).join(',');
if (!body) return next();
res.set('Allow', body).send(body);
};
/**
* Return an array of HTTP verbs or "options" for `path`.
*
* @param {String} path
* @return {Array}
* @api private
*/
Router.prototype._optionsFor = function(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`
@@ -245,7 +284,7 @@ Router.prototype.route = function(method, path, callbacks){
if (!path) throw new Error('Router#' + method + '() requires a path');
// ensure all callbacks are functions
callbacks.forEach(function(fn, i){
callbacks.forEach(function(fn){
if ('function' == typeof fn) return;
var type = {}.toString.call(fn);
var msg = '.' + method + '() requires callback functions but got a ' + type;
@@ -264,6 +303,15 @@ Router.prototype.route = function(method, path, callbacks){
return this;
};
Router.prototype.all = function(path) {
var self = this;
var args = [].slice.call(arguments);
methods.forEach(function(method){
self.route.apply(self, [method].concat(args));
});
return this;
};
methods.forEach(function(method){
Router.prototype[method] = function(path){
var args = [method].concat([].slice.call(arguments));

View File

@@ -57,9 +57,15 @@ Route.prototype.match = function(path){
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
var val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
: m[i];
try {
var val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
: m[i];
} catch(e) {
var err = new Error("Failed to decode param '" + m[i] + "'");
err.status = 400;
throw err;
}
if (key) {
params[key.name] = val;

View File

@@ -6,6 +6,12 @@
var mime = require('connect').mime
, crc32 = require('buffer-crc32');
/**
* toString ref.
*/
var toString = {}.toString;
/**
* Return ETag for `body`.
*
@@ -20,17 +26,15 @@ exports.etag = function(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}
* @api private
*/
exports.locals = function(obj){
obj.viewCallbacks = obj.viewCallbacks || [];
exports.locals = function(){
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
@@ -50,6 +54,7 @@ exports.locals = function(obj){
exports.isAbsolute = function(path){
if ('/' == path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true;
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
};
/**
@@ -77,12 +82,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: {} };
};
/**
@@ -97,9 +104,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;
@@ -125,7 +130,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];
}
}
@@ -150,17 +155,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
@@ -175,7 +197,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];
@@ -186,44 +208,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;
}
/**
@@ -260,7 +292,7 @@ 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 ? '' : '/?')
@@ -279,4 +311,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,6 +38,7 @@ function View(name, options) {
var engines = options.engines;
this.defaultEngine = options.defaultEngine;
var ext = this.ext = extname(name);
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,37 +1,50 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.1.0",
"version": "3.4.8",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
{ "name": "Aaron Heckmann", "email": "aaron.heckmann+github@gmail.com" },
{ "name": "Ciaran Jessup", "email": "ciaranj@gmail.com" },
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
{
"name": "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.7.2",
"commander": "0.6.1",
"connect": "2.12.0",
"commander": "1.3.2",
"range-parser": "0.0.4",
"mkdirp": "0.3.3",
"cookie": "0.0.5",
"buffer-crc32": "0.1.1",
"fresh": "0.1.0",
"methods": "0.0.1",
"send": "0.1.0",
"cookie-signature": "0.0.1",
"debug": "*"
"mkdirp": "0.3.5",
"cookie": "0.1.0",
"buffer-crc32": "0.2.1",
"fresh": "0.2.0",
"methods": "0.1.0",
"send": "0.1.4",
"cookie-signature": "1.0.1",
"merge-descriptors": "0.0.1",
"debug": ">= 0.7.3 < 1"
},
"devDependencies": {
"ejs": "*",
"mocha": "*",
"jade": "*",
"hjs": "*",
"stylus": "*",
"should": "*",
"connect-redis": "*",
"github-flavored-markdown": "*",
"supertest": "0.0.1"
"ejs": "~0.8.4",
"mocha": "~1.15.1",
"jade": "~0.30.0",
"hjs": "~0.0.6",
"stylus": "~0.40.0",
"should": "~2.1.1",
"connect-redis": "~1.4.5",
"marked": "0.2.10",
"supertest": "~0.8.1"
},
"keywords": [
"express",
@@ -46,11 +59,15 @@
],
"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": ">= 0.8.0"
},
"license": "MIT"
}

View File

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

View File

@@ -2,6 +2,7 @@
var express = require('../')
, Router = express.Router
, request = require('./support/http')
, methods = require('methods')
, assert = require('assert');
describe('Router', function(){
@@ -100,4 +101,21 @@ describe('Router', function(){
router.route('get', '/foo', function(){}, function(){});
})
})
describe('.all', function() {
it('should support using .all to capture all http verbs', function() {
var router = new Router();
router.all('/foo', function(){});
var url = '/foo?bar=baz';
methods.forEach(function testMethod(method) {
var route = router.match(method, url);
route.constructor.name.should.equal('Route');
route.method.should.equal(method);
route.path.should.equal('/foo');
});
})
})
})

View File

@@ -7,7 +7,7 @@ describe('markdown', function(){
it('should respond with html', function(done){
request(app)
.get('/')
.expect(/<h1>Markdown Example<\/h1>/,done)
.expect(/<h1[^>]*>Markdown Example<\/h1>/,done)
})
})

61
test/app.options.js Normal file
View File

@@ -0,0 +1,61 @@
var express = require('../')
, request = require('./support/http');
describe('OPTIONS', function(){
it('should default to the routes defined', function(done){
var app = express();
app.del('/', function(){});
app.get('/users', function(req, res){});
app.put('/users', function(req, res){});
request(app)
.options('/users')
.expect('GET,PUT')
.expect('Allow', 'GET,PUT', done);
})
it('should not respond if the path is not defined', function(done){
var app = express();
app.get('/users', function(req, res){});
request(app)
.options('/other')
.expect(404, done);
})
it('should forward requests down the middleware chain', function(done){
var app = express();
var router = new express.Router();
router.get('/users', function(req, res){});
app.use(router.middleware);
app.get('/other', function(req, res){});
request(app)
.options('/other')
.expect('GET')
.expect('Allow', 'GET', done);
})
})
describe('app.options()', function(){
it('should override the default behavior', function(done){
var app = express();
app.options('/users', function(req, res){
res.set('Allow', 'GET');
res.send('GET');
});
app.get('/users', function(req, res){});
app.put('/users', function(req, res){});
request(app)
.options('/users')
.expect('GET')
.expect('Allow', 'GET', done);
})
})

View File

@@ -8,7 +8,7 @@ describe('app', function(){
var app = express();
app.param(function(name, regexp){
if (regexp instanceof RegExp) {
if (Object.prototype.toString.call(regexp) == '[object RegExp]') { // See #1557
return function(req, res, next, val){
var captures;
if (captures = regexp.exec(String(val))) {
@@ -52,13 +52,13 @@ describe('app', function(){
app.get('/post/:id', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});
app.get('/user/:uid', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});
@@ -87,7 +87,7 @@ describe('app', function(){
app.get('/user/:id', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});

View File

@@ -14,7 +14,7 @@ describe('app', function(){
done();
})
})
it('should support absolute paths with "view engine"', function(done){
var app = express();
@@ -40,7 +40,7 @@ describe('app', function(){
done();
})
})
it('should support index.<engine>', function(done){
var app = express();
@@ -59,7 +59,7 @@ describe('app', function(){
var app = express();
app.set('views', __dirname + '/fixtures');
app.render('rawr.jade', function(err){
err.message.should.equal('Failed to lookup view "rawr.jade"');
err.message.should.equal('Failed to lookup view "rawr.jade" in views directory "' + __dirname + '/fixtures"');
done();
});
})
@@ -109,8 +109,31 @@ describe('app', function(){
})
})
})
describe('when a "view" constructor is given', function(){
it('should create an instance of it', function(done){
var app = express();
function View(name, options){
this.name = name;
this.path = 'path is required by application.js as a signal of success even though it is not used there.';
}
View.prototype.render = function(options, fn){
fn(null, 'abstract engine');
};
app.set('view', View);
app.render('something', function(err, str){
if (err) return done(err);
str.should.equal('abstract engine');
done();
})
})
})
})
describe('.render(name, options, fn)', function(){
it('should render the template', function(done){
var app = express();
@@ -125,7 +148,7 @@ describe('app', function(){
done();
})
})
it('should expose app.locals', function(done){
var app = express();
@@ -138,7 +161,7 @@ describe('app', function(){
done();
})
})
it('should give precedence to app.render() locals', function(done){
var app = express();

View File

@@ -27,16 +27,54 @@ describe('app.router', function(){
});
})
it('should decode params', function(done){
var app = express();
describe('decode querystring', function(){
it('should decode correct params', function(done){
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/foo%2Fbar')
.expect('foo/bar', done);
request(app)
.get('/foo%2Fbar')
.expect('foo/bar', done);
})
it('should not accept params in malformed paths', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/%foobar')
.expect(400, done);
})
it('should not decode spaces', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/foo+bar')
.expect('foo+bar', done);
})
it('should work with unicode', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/%ce%b1')
.expect('\u03b1', done);
})
})
it('should be .use()able', function(done){
@@ -48,7 +86,7 @@ describe('app.router', function(){
calls.push('before');
next();
});
app.use(app.router);
app.use(function(req, res, next){
@@ -68,7 +106,7 @@ describe('app.router', function(){
done();
})
})
it('should be auto .use()d on the first app.VERB() call', function(done){
var app = express();
@@ -78,7 +116,7 @@ describe('app.router', function(){
calls.push('before');
next();
});
app.get('/', function(req, res, next){
calls.push('GET /')
next();
@@ -109,7 +147,7 @@ describe('app.router', function(){
.get('/user/12?foo=bar')
.expect('user', done);
})
it('should populate req.params with the captures', function(done){
var app = express();
@@ -124,24 +162,6 @@ describe('app.router', function(){
.expect('editing user 10', done);
})
})
describe('when given an array', function(){
it('should match all paths in the array', function(done){
var app = express();
app.get(['/one', '/two'], function(req, res){
res.end('works');
});
request(app)
.get('/one')
.expect('works', function() {
request(app)
.get('/two')
.expect('works', done);
});
})
})
describe('case sensitivity', function(){
it('should be disabled by default', function(done){
@@ -155,7 +175,7 @@ describe('app.router', function(){
.get('/USER')
.expect('tj', done);
})
describe('when "case sensitive routing" is enabled', function(){
it('should match identical casing', function(done){
var app = express();
@@ -170,7 +190,7 @@ describe('app.router', function(){
.get('/uSer')
.expect('tj', done);
})
it('should not match otherwise', function(done){
var app = express();
@@ -199,7 +219,7 @@ describe('app.router', function(){
.get('/user/')
.expect('tj', done);
})
describe('when "strict routing" is enabled', function(){
it('should match trailing slashes', function(done){
var app = express();
@@ -214,7 +234,7 @@ describe('app.router', function(){
.get('/user/')
.expect('tj', done);
})
it('should match no slashes', function(done){
var app = express();
@@ -228,7 +248,7 @@ describe('app.router', function(){
.get('/user')
.expect('tj', done);
})
it('should fail when omitting the trailing slash', function(done){
var app = express();
@@ -242,7 +262,7 @@ describe('app.router', function(){
.get('/user')
.expect(404, done);
})
it('should fail when adding the trailing slash', function(done){
var app = express();
@@ -275,7 +295,7 @@ describe('app.router', function(){
.expect(404, done);
});
})
it('should allow literal "."', function(done){
var app = express();
@@ -303,7 +323,7 @@ describe('app.router', function(){
.get('/user/tj.json')
.expect('tj', done);
})
it('should work with several', function(done){
var app = express();
@@ -346,7 +366,7 @@ describe('app.router', function(){
.get('/api/users/0.json')
.expect('users/0.json', done);
})
it('should not be greedy immediately after param', function(done){
var app = express();
@@ -370,7 +390,7 @@ describe('app.router', function(){
.get('/user/122/aaa')
.expect('122', done);
})
it('should span multiple segments', function(done){
var app = express();
@@ -382,7 +402,7 @@ describe('app.router', function(){
.get('/file/javascripts/jquery.js')
.expect('javascripts/jquery.js', done);
})
it('should be optional', function(done){
var app = express();
@@ -394,7 +414,7 @@ describe('app.router', function(){
.get('/file/')
.expect('', done);
})
it('should require a preceeding /', function(done){
var app = express();
@@ -420,7 +440,7 @@ describe('app.router', function(){
.get('/user/tj')
.expect('tj', done);
})
it('should match a single segment only', function(done){
var app = express();
@@ -432,7 +452,7 @@ describe('app.router', function(){
.get('/user/tj/edit')
.expect(404, done);
})
it('should allow several capture groups', function(done){
var app = express();
@@ -459,7 +479,7 @@ describe('app.router', function(){
.get('/user/tj')
.expect('viewing tj', done);
})
it('should populate the capture group', function(done){
var app = express();
@@ -473,7 +493,7 @@ describe('app.router', function(){
.expect('editing tj', done);
})
})
describe('.:name', function(){
it('should denote a format', function(done){
var app = express();
@@ -491,7 +511,7 @@ describe('app.router', function(){
});
})
})
describe('.:name?', function(){
it('should denote an optional format', function(done){
var app = express();
@@ -509,7 +529,7 @@ describe('app.router', function(){
});
})
})
describe('when next() is called', function(){
it('should continue lookup', function(done){
var app = express()
@@ -528,7 +548,7 @@ describe('app.router', function(){
calls.push('/foo');
next();
});
app.get('/foo', function(req, res, next){
calls.push('/foo 2');
res.end('done');
@@ -542,7 +562,7 @@ describe('app.router', function(){
})
})
})
describe('when next(err) is called', function(){
it('should break out of app.router', function(done){
var app = express()
@@ -561,7 +581,7 @@ describe('app.router', function(){
calls.push('/foo');
next(new Error('fail'));
});
app.get('/foo', function(req, res, next){
assert(0);
});

View File

@@ -4,10 +4,6 @@ var express = require('../')
, assert = require('assert');
describe('exports', function(){
it('should have .version', function(){
express.should.have.property('version');
})
it('should expose connect middleware', function(){
express.should.have.property('bodyParser');
express.should.have.property('session');
@@ -19,19 +15,19 @@ describe('exports', function(){
})
it('should expose Router', function(){
express.Router.should.be.a('function');
express.Router.should.be.a.Function;
})
it('should expose the application prototype', function(){
express.application.set.should.be.a('function');
express.application.set.should.be.a.Function;
})
it('should expose the request prototype', function(){
express.request.accepts.should.be.a('function');
express.request.accepts.should.be.a.Function;
})
it('should expose the response prototype', function(){
express.response.send.should.be.a('function');
express.response.send.should.be.a.Function;
})
it('should permit modifying the .application prototype', function(){
@@ -51,7 +47,7 @@ describe('exports', function(){
.get('/')
.expect('bar', done);
})
it('should permit modifying the .response prototype', function(done){
express.response.foo = function(){ this.send('bar'); };
var app = express();

View File

@@ -0,0 +1,37 @@
var express = require('../')
, request = require('./support/http');
describe('req', function(){
describe('.acceptedEncodings', function(){
it('should return an array of accepted encodings', function(done){
var app = express();
app.use(function(req, res){
req.acceptedEncodings.should.eql(['gzip', 'deflate']);
res.end();
});
request(app)
.get('/')
.set('Accept-Encoding', ' gzip, deflate')
.expect(200, done);
})
describe('when Accept-Encoding is not present', function(){
it('should default to []', function(done){
var app = express();
app.use(function(req, res){
req.acceptedEncodings.should.have.length(0);
res.end();
});
request(app)
.get('/')
.set('Accept-Encoding', '')
.expect(200, done);
})
})
})
})

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();
@@ -56,7 +56,20 @@ describe('req', function(){
.expect('html', done);
})
describe('.accept(types)', function(){
it('should accept an argument list of type names', function(done){
var app = express();
app.use(function(req, res, next){
res.end(req.accepts('json', 'html'));
});
request(app)
.get('/')
.set('Accept', 'application/json')
.expect('json', done);
})
describe('.accepts(types)', function(){
it('should return the first when Accept is not present', 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

@@ -1,18 +1,34 @@
var express = require('../');
function req(ret) {
return {
get: function(){ return ret }
, __proto__: express.request
};
}
var express = require('../')
, request = require('./support/http')
, assert = require('assert');
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');
it('should return the Host when present', function(done){
var app = express();
app.use(function(req, res){
res.end(req.host);
});
request(app)
.post('/')
.set('Host', 'example.com')
.expect('example.com', done);
})
it('should return undefined otherwise', function(done){
var app = express();
app.use(function(req, res){
req.headers.host = null;
res.end(String(req.host));
});
request(app)
.post('/')
.expect('undefined', done);
})
})
})

83
test/req.secure.js Normal file
View File

@@ -0,0 +1,83 @@
var express = require('../')
, request = require('./support/http');
describe('req', function(){
describe('.secure', function(){
describe('when X-Forwarded-Proto is missing', function(){
it('should return false when http', function(done){
var app = express();
app.get('/', function(req, res){
res.send(req.secure ? 'yes' : 'no');
});
request(app)
.get('/')
.expect('no', done)
})
})
})
describe('.secure', function(){
describe('when X-Forwarded-Proto is present', function(){
it('should return false when http', function(done){
var app = express();
app.get('/', function(req, res){
res.send(req.secure ? 'yes' : 'no');
});
request(app)
.get('/')
.set('X-Forwarded-Proto', 'https')
.expect('no', done)
})
it('should return true when "trust proxy" is enabled', function(done){
var app = express();
app.enable('trust proxy');
app.get('/', function(req, res){
res.send(req.secure ? 'yes' : 'no');
});
request(app)
.get('/')
.set('X-Forwarded-Proto', 'https')
.expect('yes', done)
})
it('should return false when initial proxy is http', function(done){
var app = express();
app.enable('trust proxy');
app.get('/', function(req, res){
res.send(req.secure ? 'yes' : 'no');
});
request(app)
.get('/')
.set('X-Forwarded-Proto', 'http, https')
.expect('no', done)
})
it('should return true when initial proxy is https', function(done){
var app = express();
app.enable('trust proxy');
app.get('/', function(req, res){
res.send(req.secure ? 'yes' : 'no');
});
request(app)
.get('/')
.set('X-Forwarded-Proto', 'https, http')
.expect('yes', done)
})
})
})
})

View File

@@ -5,47 +5,34 @@ var express = require('../')
describe('req', function(){
describe('.signedCookies', function(){
it('should return a signed JSON cookie', function(done){
var app = express()
, cookieHeader
, val;
var app = express();
app.use(express.cookieParser('secret'));
app.use(function(req, res){
res.send(req.signedCookies);
if ('/set' == req.path) {
res.cookie('obj', { foo: 'bar' }, { signed: true });
res.end();
} else {
res.send(req.signedCookies);
}
});
app.response.req = { secret: 'secret' };
app.response.cookie('obj', { foo: 'bar' }, { signed: true });
cookieHeader = app.response.get('set-cookie');
val = JSON.stringify({ obj: { foo: 'bar' } });
request(app)
.get('/')
.set('Cookie', cookieHeader)
.expect(val, done);
})
.get('/set')
.end(function(err, res){
if (err) return done(err);
var cookie = res.header['set-cookie'];
it('should return a signed cookie', function(done){
var app = express()
, cookieHeader
, val;
app.use(express.cookieParser('secret'));
app.use(function(req, res){
res.send(req.signedCookies);
request(app)
.get('/')
.set('Cookie', cookie)
.end(function(err, res){
if (err) return don(err);
res.body.should.eql({ obj: { foo: 'bar' } });
done();
});
});
app.response.req = { secret: 'secret' };
app.response.cookie('foo', 'bar', { signed: true });
cookieHeader = app.response.get('set-cookie');
val = JSON.stringify({ foo: 'bar' });
request(app)
.get('/')
.set('Cookie', cookieHeader)
.expect(val, done);
})
})
})

View File

@@ -15,7 +15,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.example.com')
.expect('["ferrets","tobi"]', done);
.expect(["ferrets","tobi"], done);
})
})
@@ -30,7 +30,22 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'example.com')
.expect('[]', done);
.expect([], done);
})
})
describe('with no host', function(){
it('should return an empty array', function(done){
var app = express();
app.use(function(req, res){
req.headers.host = null;
res.send(req.subdomains);
});
request(app)
.get('/')
.expect([], done);
})
})
@@ -47,7 +62,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.sub.example.com')
.expect('["com","example","sub","ferrets","tobi"]', done);
.expect(["com","example","sub","ferrets","tobi"], done);
})
})
@@ -63,7 +78,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.sub.example.com')
.expect('["ferrets","tobi"]', done);
.expect(["ferrets","tobi"], done);
})
})
@@ -79,7 +94,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'sub.example.com')
.expect('[]', done);
.expect([], done);
})
})
})

View File

@@ -53,7 +53,7 @@ app3.use(function(req, res, next){
})
});
describe('req', function(){
describe('res', function(){
describe('.format(obj)', function(){
describe('with canonicalized mime types', function(){
test(app);
@@ -79,24 +79,41 @@ function test(app) {
request(app)
.get('/')
.set('Accept', 'text/html; q=.5, application/json, */*; q=.1')
.expect('{"message":"hey"}', done);
.expect({"message":"hey"}, done);
})
it('should allow wildcard type/subtypes', function(done){
request(app)
.get('/')
.set('Accept', 'text/html; q=.5, application/*, */*; q=.1')
.expect('{"message":"hey"}', done);
.expect({"message":"hey"}, done);
})
it('should default the Content-Type', function(done){
request(app)
.get('/')
.set('Accept', 'text/html; q=.5, text/plain')
.expect('Content-Type', 'text/plain')
.expect('Content-Type', 'text/plain; charset=UTF-8')
.expect('hey', done);
})
it('should set the correct charset for the Content-Type', function() {
request(app)
.get('/')
.set('Accept', 'text/html')
.expect('Content-Type', 'text/html; charset=UTF-8');
request(app)
.get('/')
.set('Accept', 'text/plain')
.expect('Content-Type', 'text/plain; charset=UTF-8');
request(app)
.get('/')
.set('Accept', 'application/json')
.expect('Content-Type', 'application/json');
})
it('should Vary: Accept', function(done){
request(app)
.get('/')

View File

@@ -16,11 +16,27 @@ describe('res', function(){
.get('/?callback=something')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('something && something({"count":1});');
res.text.should.equal('typeof something === \'function\' && something({"count":1});');
done();
})
})
it('should use first callback parameter with jsonp', function(done){
var app = express();
app.use(function(req, res){
res.jsonp({ count: 1 });
});
request(app)
.get('/?callback=something&callback=somethingelse')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('typeof something === \'function\' && something({"count":1});');
done();
})
})
it('should allow renaming callback', function(done){
var app = express();
@@ -34,10 +50,10 @@ describe('res', function(){
.get('/?clb=something')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('something && something({"count":1});');
res.text.should.equal('typeof something === \'function\' && something({"count":1});');
done();
})
})
})
it('should allow []', function(done){
var app = express();
@@ -50,7 +66,7 @@ describe('res', function(){
.get('/?callback=callbacks[123]')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('callbacks[123] && callbacks[123]({"count":1});');
res.text.should.equal('typeof callbacks[123] === \'function\' && callbacks[123]({"count":1});');
done();
})
})
@@ -66,7 +82,7 @@ describe('res', function(){
.get('/?callback=foo;bar()')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('foobar && foobar({});');
res.text.should.equal('typeof foobar === \'function\' && foobar({});');
done();
})
})
@@ -82,7 +98,7 @@ describe('res', function(){
.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"});');
res.text.should.equal('typeof foo === \'function\' && foo({"str":"\\u2028 \\u2029 woot"});');
done();
});
});
@@ -122,7 +138,7 @@ describe('res', function(){
})
})
})
describe('when given an object', function(){
it('should respond with json', function(done){
var app = express();
@@ -195,7 +211,7 @@ describe('res', function(){
})
})
})
describe('.json(status, object)', function(){
it('should respond with json and set the .statusCode', function(done){
var app = express();

View File

@@ -3,6 +3,11 @@ var express = require('../')
, res = express.response;
describe('res', function(){
beforeEach(function() {
res.removeHeader('link');
});
describe('.links(obj)', function(){
it('should set Link header field', function(){
res.links({
@@ -15,5 +20,22 @@ describe('res', function(){
'<http://api.example.com/users?page=2>; rel="next", '
+ '<http://api.example.com/users?page=5>; rel="last"');
})
it('should set Link header field for multiple calls', function() {
res.links({
next: 'http://api.example.com/users?page=2',
last: 'http://api.example.com/users?page=5'
});
res.links({
prev: 'http://api.example.com/users?page=1',
});
res.get('link')
.should.equal(
'<http://api.example.com/users?page=2>; rel="next", '
+ '<http://api.example.com/users?page=5>; rel="last", '
+ '<http://api.example.com/users?page=1>; rel="prev"');
})
})
})

View File

@@ -64,7 +64,7 @@ describe('res', function(){
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/./edit');
res.headers.should.have.property('location', '/post/1/edit');
done();
})
})
@@ -81,7 +81,24 @@ describe('res', function(){
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/../new');
res.headers.should.have.property('location', '/post/new');
done();
})
})
})
describe('with leading ./ and containing ..', function(){
it('should construct path-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('./skip/../../new').end();
});
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/new');
done();
})
})

View File

@@ -74,7 +74,7 @@ describe('res', function(){
})
})
})
describe('when accepting html', function(){
it('should respond with html', function(done){
var app = express();
@@ -110,7 +110,7 @@ describe('res', function(){
})
})
})
describe('when accepting text', function(){
it('should respond with text', function(done){
var app = express();
@@ -169,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

@@ -1,6 +1,7 @@
var express = require('../')
, request = require('./support/http');
, request = require('./support/http')
, assert = require('assert');
describe('res', function(){
describe('.send(null)', function(){
@@ -16,7 +17,7 @@ describe('res', function(){
.expect('', done);
})
})
describe('.send(undefined)', function(){
it('should set body to ""', function(done){
var app = express();
@@ -45,7 +46,7 @@ describe('res', function(){
.expect(201, done);
})
})
describe('.send(code, body)', function(){
it('should set .statusCode and body', function(done){
var app = express();
@@ -107,7 +108,24 @@ describe('res', function(){
.expect('ETag', '"-1498647312"')
.end(done);
})
it('should not set ETag for non-GET/HEAD', function(done){
var app = express();
app.use(function(req, res){
var str = Array(1024 * 2).join('-');
res.send(str);
});
request(app)
.post('/')
.end(function(err, res){
if (err) return done(err);
assert(!res.header.etag, 'has an ETag');
done();
});
})
it('should not override Content-Type', function(done){
var app = express();
@@ -122,7 +140,7 @@ describe('res', function(){
.expect(200, done);
})
})
describe('.send(Buffer)', function(){
it('should send as octet-stream', function(done){
var app = express();
@@ -172,7 +190,7 @@ describe('res', function(){
})
})
})
describe('.send(Object)', function(){
it('should send as application/json', function(done){
var app = express();
@@ -224,7 +242,7 @@ describe('res', function(){
})
})
})
describe('when .statusCode is 304', function(){
it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){
var app = express();
@@ -300,4 +318,77 @@ describe('res', function(){
.get('/?callback=foo')
.expect('{"foo":"bar"}', done);
})
describe('"etag" setting', function(){
describe('when enabled', function(){
it('should send ETag even when content-length < 1024', function(done){
var app = express();
app.use(function(req, res){
res.send('kajdslfkasdf');
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('etag');
done();
});
})
it('should send ETag ', function(done){
var app = express();
app.use(function(req, res){
var str = Array(1024 * 2).join('-');
res.send(str);
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('etag', '"-1498647312"');
done();
});
});
});
describe('when disabled', function(){
it('should send no ETag', function(done){
var app = express();
app.use(function(req, res){
var str = Array(1024 * 2).join('-');
res.send(str);
});
app.disable('etag');
request(app)
.get('/')
.end(function(err, res){
res.headers.should.not.have.property('etag');
done();
});
});
it('should send ETag when manually set', function(done){
var app = express();
app.disable('etag');
app.use(function(req, res){
res.set('etag', 1);
res.send(200);
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('etag');
done();
});
});
});
})
})

View File

@@ -51,7 +51,7 @@ describe('res', function(){
.get('/')
.end(function(err, res){
assert(1 == calls, 'called too many times');
res.text.should.equal("ENOENT, stat 'test/fixtures/nope.html'");
res.text.should.startWith("ENOENT, stat");
res.statusCode.should.equal(200);
done();
});

55
test/res.vary.js Normal file
View File

@@ -0,0 +1,55 @@
var express = require('../')
, should = require('should');
function response() {
var res = Object.create(express.response);
res._headers = {};
return res;
}
describe('res.vary()', function(){
describe('with no arguments', function(){
it('should not set Vary', function(){
var res = response();
res.vary();
should.not.exist(res.get('Vary'));
})
})
describe('with an empty array', function(){
it('should not set Vary', function(){
var res = response();
res.vary([]);
should.not.exist(res.get('Vary'));
})
})
describe('with an array', function(){
it('should set the values', function(){
var res = response();
res.vary(['Accept', 'Accept-Language', 'Accept-Encoding']);
res.get('Vary').should.equal('Accept, Accept-Language, Accept-Encoding');
})
})
describe('with a string', function(){
it('should set the value', function(){
var res = response();
res.vary('Accept');
res.get('Vary').should.equal('Accept');
})
})
describe('when the value is present', function(){
it('should not add it again', function(){
var res = response();
res.vary('Accept');
res.vary('Accept-Encoding');
res.vary('Accept-Encoding');
res.vary('Accept-Encoding');
res.vary('Accept');
res.get('Vary').should.equal('Accept, Accept-Encoding');
})
})
})

View File

@@ -6,7 +6,7 @@ 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"');
})
@@ -27,7 +27,7 @@ describe('utils.isAbsolute()', function(){
assert(utils.isAbsolute('c:\\'));
assert(!utils.isAbsolute(':\\'));
})
it('should unices', function(){
assert(utils.isAbsolute('/foo/bar'));
assert(!utils.isAbsolute('foo/bar'));
@@ -49,44 +49,60 @@ 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: {}, originalIndex: 0 }]);
})
it('should parse qvalues', function(){
utils.parseQuality('text/html; q=0.5')
.should.eql([{ value: 'text/html', quality: 0.5 }]);
utils.parseQuality('text/html; q=.2')
.should.eql([{ value: 'text/html', quality: 0.2 }]);
it('should parse qvalues', function(){
utils.parseParams('text/html; q=0.5')
.should.eql([{ value: 'text/html', quality: 0.5, params: {}, originalIndex: 0 }]);
utils.parseParams('text/html; q=.2')
.should.eql([{ value: 'text/html', quality: 0.2, params: {}, originalIndex: 0 }]);
})
it('should parse accept parameters', function(){
utils.parseParams('application/json; ver=2.0')
.should.eql([{ value: 'application/json', quality: 1, params: {ver: "2.0"}, originalIndex: 0 }]);
utils.parseParams('text/html; q=0.5; level=2')
.should.eql([{ value: 'text/html', quality: 0.5, params: {level: "2"}, originalIndex: 0 }]);
utils.parseParams('text/html;q=.2;ver=beta')
.should.eql([{ value: 'text/html', quality: 0.2, params: {ver: "beta"}, originalIndex: 0 }]);
})
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: {}, originalIndex: 0 }]);
})
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');
})
it('should work with long lists', function(){
var str = 'en, nl, fr, de, ja, it, es, pt, pt-PT, da, fi, nb, sv, ko, zh-Hans, zh-Hant, ru, pl';
var arr = utils.parseParams(str).map(function(o){ return o.value });
arr.should.eql(str.split(', '));
})
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');
})
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);
})
})
@@ -96,7 +112,7 @@ describe('utils.parseAccept(str)', function(){
var arr = utils.parseAccept('text/html');
arr[0].type.should.equal('text');
})
it('should provide .subtype', function(){
var arr = utils.parseAccept('text/html');
arr[0].subtype.should.equal('html');
@@ -110,7 +126,7 @@ describe('utils.accepts(type, str)', function(){
.should.equal('text/html');
})
})
describe('when a string is empty', function(){
it('should return the value', function(){
utils.accepts('text/html', '')
@@ -156,44 +172,44 @@ describe('utils.accepts(type, str)', function(){
utils.accepts('text/html', 'text/plain, text/html')
.should.equal('text/html');
})
it('should return undefined otherwise', function(){
assert(null == utils.accepts('text/html', 'text/plain, application/json'));
})
})
describe('when accepting */subtype', function(){
it('should return the value when present', function(){
utils.accepts('text/html', 'text/*')
.should.equal('text/html');
})
it('should return undefined otherwise', function(){
assert(null == utils.accepts('text/html', 'image/*'));
})
})
describe('when accepting type/*', function(){
it('should return the value when present', function(){
utils.accepts('text/html', '*/html')
.should.equal('text/html');
})
it('should return undefined otherwise', function(){
assert(null == utils.accepts('text/html', '*/json'));
})
})
describe('when an extension is given', function(){
it('should return the value when present', function(){
utils.accepts('html', 'text/html, application/json')
.should.equal('html');
})
it('should return undefined otherwise', function(){
assert(null == utils.accepts('html', 'text/plain, application/json'));
})
it('should support *', function(){
utils.accepts('html', 'text/*')
.should.equal('html');
@@ -202,4 +218,4 @@ describe('utils.accepts(type, str)', function(){
.should.equal('html');
})
})
})
})