Comparar commits

...

120 Commits

Autor SHA1 Mensagem Data
TJ Holowaychuk 20e8f08cb2 Release 3.0.0rc2 2012-08-03 13:32:53 -07:00
TJ Holowaychuk bac0c64633 escape res.redirect() link 2012-08-02 19:41:37 -07:00
TJ Holowaychuk 48923055eb docs 2012-08-01 13:18:30 -07:00
TJ Holowaychuk 0f20a5e06a add CORS example 2012-08-01 13:17:46 -07:00
TJ Holowaychuk 56bfb9249f Merge branch 'master' of github.com:visionmedia/express 2012-07-31 20:51:07 -07:00
TJ Holowaychuk 5ed1544cab remove generated docs 2012-07-31 20:50:52 -07:00
TJ Holowaychuk e5c7be9364 Merge pull request #1250 from silvinci/1249-mvc-boot.js-fix-rc
Fixed double inclusion of methods in mvc example
2012-07-26 12:53:06 -07:00
Jan Buschtöns 73ce9d028c Line 40 removed. Fixed! 2012-07-26 21:46:22 +02:00
TJ Holowaychuk 75debbe5bc update connect dep 2012-07-25 09:26:20 -07:00
TJ Holowaychuk 5f33d89ea5 fix vhost example 2012-07-24 15:41:12 -07:00
TJ Holowaychuk 42fd29efe8 deprecate .createServer() & remove old stale examples 2012-07-24 15:40:05 -07:00
TJ Holowaychuk 2d91eac811 Release 3.0.0rc1 2012-07-24 13:32:49 -07:00
TJ Holowaychuk a50f02e87d update cookie dep 2012-07-24 13:30:44 -07:00
TJ Holowaychuk 214f913b0c merge 2012-07-24 13:28:13 -07:00
TJ Holowaychuk 1021c86300 update connect dep 2012-07-24 13:25:50 -07:00
TJ Holowaychuk 386516815a Merge branch 'master' of github.com:visionmedia/express 2012-07-24 09:57:00 -07:00
TJ Holowaychuk d5e5647bba fix express(1) -h flag, use -H for hogan. Closes #1245 2012-07-24 09:56:48 -07:00
TJ Holowaychuk a861ea7eaf Merge pull request #1243 from saintedlama/master
Fixes path joining in app stub generator on win32 systems
2012-07-24 08:46:11 -07:00
TJ Holowaychuk cb844132e6 Merge pull request #1244 from RubenVerborgh/master
EventEmitter memory leak with successful sendfile
2012-07-24 08:36:53 -07:00
Ruben Verborgh 8050308706 Remove socket error handler if file was sent successfully. 2012-07-24 11:57:02 +02:00
Christoph Walcher e79f72bf88 Fixes path joining in win32 systems 2012-07-24 07:18:16 +02:00
TJ Holowaychuk 07b6c9f563 update connect dep 2012-07-23 11:35:54 -07:00
TJ Holowaychuk 8f4e61a474 fix res.render docs 2012-07-20 17:22:21 -07:00
TJ Holowaychuk 54d37c60f5 add more examples to view-locals 2012-07-18 10:41:07 -07:00
TJ Holowaychuk a93d375acc add res.redirect("//foo.com") support 2012-07-18 09:29:11 -07:00
TJ Holowaychuk d66f0e5eb9 change res.redirect() to use scheme-relative urls 2012-07-18 09:07:21 -07:00
TJ Holowaychuk e84db12783 update send dep 2012-07-16 19:27:53 -07:00
TJ Holowaychuk 1ad2ecefe8 Release 3.0.0beta7 2012-07-16 09:25:03 -07:00
TJ Holowaychuk 08b68ec8cd udpate connect dep 2012-07-16 09:23:40 -07:00
TJ Holowaychuk 48be9233d8 add res.download() content-disposition on error removal test 2012-07-13 09:24:09 -07:00
TJ Holowaychuk a512d9b47d Release 3.0.0beta6 2012-07-13 09:19:24 -07:00
TJ Holowaychuk 62234cc106 clean up package.json 2012-07-13 09:00:26 -07:00
TJ Holowaychuk ab61837885 change res.sendfile() to use send() module 2012-07-13 08:58:40 -07:00
TJ Holowaychuk 7a5041bf9c keywords 2012-07-12 12:51:29 -07:00
TJ Holowaychuk 4d219135b2 update connect dep 2012-07-12 12:03:21 -07:00
TJ Holowaychuk 26eeb64640 add err.view property for view errors. Closes #1226 2012-07-11 08:45:37 -07:00
riadh 5426eb0b62 add "jsonp callback name" setting 2012-07-06 08:53:31 -07:00
TJ Holowaychuk b9e32ec2c4 styling 2012-07-06 08:15:28 -07:00
TJ Holowaychuk 32b8613708 fix matchRequest tests 2012-07-06 08:14:53 -07:00
TJ Holowaychuk 90eddb3439 dont .toLowerCase() twice 2012-07-06 08:12:47 -07:00
TJ Holowaychuk accd6180c1 rename matchReq -> matchRequest 2012-07-06 08:12:33 -07:00
TJ Holowaychuk e770b674ff Merge pull request #1212 from riadhchtara/Issue944
issue#944 : Changing Router#match() signature
2012-07-06 08:08:11 -07:00
TJ Holowaychuk 4f7c4d1051 remove app.locals.use and res.locals.use
there are a few reasons for this:

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

lastly this api in a sense promotes some obscure uses since
they may be scattered throughout rather than explicitly
given to specific routes or used globally as middleware etc
2012-07-05 18:46:19 -07:00
TJ Holowaychuk 78845e7d23 fix app.locals.use() when mounting apps 2012-07-05 18:31:02 -07:00
TJ Holowaychuk 93f1cecc92 update connect dep 2012-07-05 09:43:24 -07:00
riadh 8ccd89f6ad Update Issue944 2012-07-05 13:32:33 +03:00
riadh 0a210cce7a fix to test 2012-07-04 18:51:48 +02:00
riadhchtara f110248462 Issue 944:
Changing Router#match() signature from (req[, i]) to (method, url[, i]), and making it public
2012-07-04 13:19:40 +02:00
TJ Holowaychuk 96e4014a70 Replace res.send() with res.send = require("response-send") 2012-07-03 21:09:17 -07:00
TJ Holowaychuk 2173d00829 whitespace ocd 2012-07-03 13:57:40 -07:00
riadh 3827f5ef8b tests for issues 1115 fix for utils 2012-07-03 13:56:21 -07:00
riadh 73bed61afa * immediately after :param would become not greedy 2012-07-03 13:56:21 -07:00
TJ Holowaychuk ee89ff5026 Release 3.0.0beta5 2012-07-03 10:20:19 -07:00
TJ Holowaychuk 8df9e745d5 Merge branch 'master' of github.com:visionmedia/express 2012-07-03 09:39:52 -07:00
TJ Holowaychuk c76e954504 upgrade connect dep 2012-07-03 09:30:53 -07:00
TJ Holowaychuk 136f054614 Merge pull request #1202 from strk/make_check
Add "make check" support.
2012-07-02 11:41:09 -07:00
Sandro Santilli e9a4b4f8ff Add "make check" support.
This is to follow GNU coding stadards. See:
www.gnu.org/prep/standards/html_node/Standard-Targets.html
2012-07-02 15:45:33 +02:00
TJ Holowaychuk 5415c98ff9 add route-map tests 2012-06-30 12:18:32 -07:00
TJ Holowaychuk 39efa452fc Added route-map example 2012-06-29 09:01:06 -07:00
TJ Holowaychuk bddcdee3fe docs 2012-06-27 13:58:35 -07:00
TJ Holowaychuk 2e8d44b444 docs 2012-06-27 13:36:12 -07:00
TJ Holowaychuk 70d68419b0 Added res.json(obj, status) support back for BC 2012-06-27 13:34:53 -07:00
TJ Holowaychuk 26fab2a27d update auth example to utilize cores pbkdf2 2012-06-27 13:20:39 -07:00
TJ Holowaychuk 0f5560eebd updated tests to use "supertest" 2012-06-26 17:14:07 -07:00
TJ Holowaychuk 170dcc2907 Added "methods" dep 2012-06-26 11:38:55 -07:00
TJ Holowaychuk 18d6c78ef4 fixed a test race condition 2012-06-25 12:42:51 -07:00
TJ Holowaychuk 1248f0338b Release 3.0.0beta4 2012-06-25 12:08:22 -07:00
TJ Holowaychuk b400814d00 Added req.auth
tests to come
2012-06-22 16:25:31 -07:00
TJ Holowaychuk a934929bb3 update connect dep 2012-06-22 10:48:07 -07:00
TJ Holowaychuk 140efb574c Revert "Added + support to the router"
This reverts commit 6aaa7dc26d.
2012-06-22 08:20:23 -07:00
TJ Holowaychuk 3c0c96114f Added res.send(body, status) support back for backwards compat 2012-06-21 16:37:26 -07:00
TJ Holowaychuk 362c96c8c1 refactor res.redirect() relative check 2012-06-21 08:25:43 -07:00
TJ Holowaychuk 5db3d0f9fc Merge branch 'master' of github.com:visionmedia/express 2012-06-18 09:37:37 -07:00
TJ Holowaychuk 80c814c393 Merge pull request #1182 from langpavel/patch-1
Refactoring: no need for add 'del' and 'all' to methods
2012-06-18 09:37:25 -07:00
TJ Holowaychuk bf66465937 Merge pull request #1183 from nullfirm/master
Re: [express] add hogan.js template engine for express@3.0 (#1176)
2012-06-17 21:36:24 -07:00
Min-su Ok 72eea7e6cd modify from --hjs to --hogan. 2012-06-18 13:17:51 +09:00
TJ Holowaychuk 8f4cd13c89 docs 2012-06-17 19:00:41 -07:00
Pavel Lang 6556cefc71 Refactoring: no need for add 'del' and 'all' to methods 2012-06-18 04:55:25 +03:00
TJ Holowaychuk 7bfb58920a docs 2012-06-17 18:08:35 -07:00
TJ Holowaychuk 2e324ccf5f docs 2012-06-17 18:08:14 -07:00
TJ Holowaychuk 1a10ee76b3 update range-parser dep 2012-06-17 18:06:04 -07:00
TJ Holowaychuk 619e6349f6 ws 2012-06-17 17:48:52 -07:00
TJ Holowaychuk b1ff68548f add inline range example 2012-06-17 17:41:28 -07:00
TJ Holowaychuk 376b6c3bad add note about inclusive ranges 2012-06-17 17:37:42 -07:00
TJ Holowaychuk f4c8a59b17 Added req.range(size) 2012-06-17 17:33:05 -07:00
TJ Holowaychuk e6129d8ba5 Added res.links(obj) 2012-06-17 16:34:42 -07:00
TJ Holowaychuk d2baf11b8a docs 2012-06-17 13:21:13 -07:00
TJ Holowaychuk 82b5b12ca7 Added .default() support to res.format() 2012-06-17 13:15:55 -07:00
TJ Holowaychuk c145ab9b81 Update mkdirp 2012-06-15 16:15:16 -07:00
TJ Holowaychuk c10223b803 GET / HEAD only for req.fresh 2012-06-15 16:09:32 -07:00
TJ Holowaychuk 1e09b54ad2 update fresh 2012-06-15 16:07:27 -07:00
TJ Holowaychuk d073e0aeb5 Added 2xx / 304 check to req.fresh 2012-06-15 16:03:20 -07:00
TJ Holowaychuk ce7293de13 misc 2012-06-15 15:44:44 -07:00
TJ Holowaychuk 108e66c24b Fixed res.send() freshness check, respect res.statusCode 2012-06-15 15:42:46 -07:00
TJ Holowaychuk f90401b8c0 Release 3.0.0beta3 2012-06-15 11:40:49 -07:00
TJ Holowaychuk a32e705d49 Merge branch 'master' of github.com:visionmedia/express 2012-06-15 11:39:04 -07:00
TJ Holowaychuk 640cf4ca21 Changed: res.send() always checks freshness 2012-06-15 11:38:51 -07:00
TJ Holowaychuk 442e782692 Merge pull request #1176 from nullfirm/master
add hogan.js template engine for express@3.0
2012-06-15 09:01:01 -07:00
Min-su Ok 0a874ad8b3 add hogan.js template engine for express@3.0 2012-06-14 18:44:53 +09:00
Min-su Ok 18083f0c13 add hogan.js template engine for express@3.0 2012-06-14 18:44:46 +09:00
TJ Holowaychuk f25aaf11e9 Added another example to content-negotiation 2012-06-13 17:32:44 -07:00
TJ Holowaychuk 9b09257b28 upgrade connect 2012-06-11 09:53:15 -07:00
TJ Holowaychuk f895516a2c Added fresh dep 2012-06-10 12:21:03 -07:00
TJ Holowaychuk 76aa718b75 update markdown docs 2012-06-08 14:11:52 -07:00
TJ Holowaychuk 0acee67339 Merge branch 'master' of github.com:visionmedia/express 2012-06-08 09:47:56 -07:00
TJ Holowaychuk 4475e335ef mime export test 2012-06-08 09:47:45 -07:00
TJ Holowaychuk 895673141d Fixed: expose connects mime module. Cloases #1165 2012-06-08 09:47:13 -07:00
TJ Holowaychuk e4cd99ae1c Merge pull request #1164 from danneu/patch-2
Removed extra "of" in `res.set` comment.
2012-06-07 08:52:04 -07:00
Dan Neumann c39a398d83 Removed extra "of" in res.set comment. 2012-06-07 00:17:45 -05:00
TJ Holowaychuk 2787bd5bf0 Release 3.0.0beta2 2012-06-06 14:46:52 -07:00
TJ Holowaychuk 6aaa7dc26d Added + support to the router 2012-06-06 14:38:22 -07:00
TJ Holowaychuk ebf60d2340 coerce res.cookie values to strings 2012-06-06 12:34:51 -07:00
TJ Holowaychuk 02d43846f6 fixing cookies for connect 2.3.1 2012-06-06 12:25:14 -07:00
TJ Holowaychuk 8930cd563c update connect dep 2012-06-06 12:00:35 -07:00
TJ Holowaychuk 2b90cd7d51 Added req.host 2012-06-05 19:24:49 -07:00
TJ Holowaychuk d5fde6a4b9 added test to illustrate req.params as an array 2012-06-05 19:02:28 -07:00
TJ Holowaychuk 99b2e0fa08 refactored req.param() 2012-06-05 18:59:26 -07:00
TJ Holowaychuk ebcb1ca90e Changed req.param() to check route first
body / query-string taking precedence is a little sketchy
but you should use this method sparingly, think of it
as PHPs $_REQUEST
2012-06-05 18:51:42 -07:00
TJ Holowaychuk 1763b073f9 docs 2012-06-04 09:48:19 -07:00
TJ Holowaychuk 908e467548 docs 2012-06-02 20:04:51 -07:00
96 arquivos alterados com 1365 adições e 2432 exclusões
+75
Ver Arquivo
@@ -1,4 +1,79 @@
3.0.0rc2 / 2012-08-03
==================
* add CORS example
* update connect dep
* deprecate `.createServer()` & remove old stale examples
* fix: escape `res.redirect()` link
* fix vhost example
3.0.0rc1 / 2012-07-24
==================
* add more examples to view-locals
* add scheme-relative redirects (`res.redirect("//foo.com")`) support
* update cookie dep
* update connect dep
* update send dep
* fix `express(1)` -h flag, use -H for hogan. Closes #1245
* fix `res.sendfile()` socket error handling regression
3.0.0beta7 / 2012-07-16
==================
* update connect dep for `send()` root normalization regression
3.0.0beta6 / 2012-07-13
==================
* add `err.view` property for view errors. Closes #1226
* add "jsonp callback name" setting
* add support for "/foo/:bar*" non-greedy matches
* change `res.sendfile()` to use `send()` module
* change `res.send` to use "response-send" module
* remove `app.locals.use` and `res.locals.use`, use regular middleware
3.0.0beta5 / 2012-07-03
==================
* add "make check" support
* add route-map example
* add `res.json(obj, status)` support back for BC
* add "methods" dep, remove internal methods module
* update connect dep
* update auth example to utilize cores pbkdf2
* updated tests to use "supertest"
3.0.0beta4 / 2012-06-25
==================
* Added `req.auth`
* Added `req.range(size)`
* Added `res.links(obj)`
* Added `res.send(body, status)` support back for backwards compat
* Added `.default()` support to `res.format()`
* Added 2xx / 304 check to `req.fresh`
* Revert "Added + support to the router"
* Fixed `res.send()` freshness check, respect res.statusCode
3.0.0beta3 / 2012-06-15
==================
* Added hogan `--hjs` to express(1) [nullfirm]
* Added another example to content-negotiation
* Added `fresh` dep
* Changed: `res.send()` always checks freshness
* Fixed: expose connects mime module. Cloases #1165
3.0.0beta2 / 2012-06-06
==================
* Added `+` support to the router
* Added `req.host`
* Changed `req.param()` to check route first
* Update connect dep
3.0.0beta1 / 2012-06-01
==================
+6 -13
Ver Arquivo
@@ -1,24 +1,20 @@
MOCHA_OPTS=
REPORTER = dot
docs: docs/express.md
docs/express.md: docs/application.md docs/request.md docs/response.md
cat $^ > $@
docs/%.md: lib/%.js
@mkdir -p docs
dox --raw < $< | ./support/docs > $@
check: test
test: test-unit test-acceptance
test-unit:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER)
--reporter $(REPORTER) \
$(MOCHA_OPTS)
test-acceptance:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--bail \
test/acceptance/*.js
test-cov: lib-cov
@@ -27,10 +23,7 @@ test-cov: lib-cov
lib-cov:
@jscoverage lib lib-cov
docclean:
rm -fr docs
benchmark:
@./support/bench
.PHONY: docs docclean test test-unit test-acceptance benchmark
.PHONY: test test-unit test-acceptance benchmark
+1 -1
Ver Arquivo
@@ -77,7 +77,7 @@ app.listen(3000);
First install the dev dependencies to install all the example / test suite deps:
$ cd express
$ npm install -d
$ npm install
then run whichever tests you want:
+25 -2
Ver Arquivo
@@ -19,6 +19,7 @@ program
.option('-s, --sessions', 'add session support')
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
.option('-H, --hogan', 'add hogan.js engine support')
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
.option('-f, --force', 'force on non-empty directory')
.parse(process.argv);
@@ -36,6 +37,7 @@ var eol = 'win32' == os.platform() ? '\r\n' : '\n'
program.template = 'jade';
if (program.ejs) program.template = 'ejs';
if (program.jshtml) program.template = 'jshtml';
if (program.hogan) program.template = 'hjs';
/**
* Routes index template.
@@ -122,6 +124,23 @@ var jshtmlIndex = [
, '<p>Welcome to @write(title)</p>'
].join(eol);
/**
* Hogan.js index template.
*/
var hoganIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title>{{ title }}</title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1>{{ title }}</h1>'
, ' <p>Welcome to {{ title }}</p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* Default css template.
*/
@@ -176,7 +195,8 @@ var app = [
, ''
, 'var express = require(\'express\')'
, ' , routes = require(\'./routes\')'
, ' , http = require(\'http\');'
, ' , http = require(\'http\')'
, ' , path = require(\'path\');'
, ''
, 'var app = express();'
, ''
@@ -189,7 +209,7 @@ var app = [
, ' app.use(express.bodyParser());'
, ' app.use(express.methodOverride());{sess}'
, ' app.use(app.router);{css}'
, ' app.use(express.static(__dirname + \'/public\'));'
, ' app.use(express.static(path.join(__dirname, \'public\')));'
, '});'
, ''
, 'app.configure(\'development\', function(){'
@@ -275,6 +295,9 @@ function createApplicationAt(path) {
write(path + '/views/layout.jshtml', jshtmlLayout);
write(path + '/views/index.jshtml', jshtmlIndex);
break;
case 'hjs':
write(path + '/views/index.hjs', hoganIndex);
break;
}
});
-181
Ver Arquivo
@@ -1,181 +0,0 @@
# app
Application prototype.
# app.use()
Proxy `connect#use()` to apply settings to
mounted applications.
# app.engine()
Register the given template engine callback `fn`
as `ext`.
By default will `require()` the engine based on the
file extension. For example if you try to render
a "foo.jade" file Express will invoke the following internally:
app.engine('jade', require('jade').__express);
For engines that do not provide `.__express` out of the box,
or if you wish to "map" a different extension to the template engine
you may use this method. For example mapping the EJS template engine to
".html" files
app.engine('html', require('ejs').renderFile);
In this case EJS provides a `.renderFile()` method with
the same signature that Express expects: `(path, options, callback)`,
though note that it aliases this method as `ejs.__express` internally
so if you're using ".ejs" extensions you dont need to do anything.
Some template engines do not follow this convention, the
[Consolidate.js](https://github.com/visionmedia/consolidate.js)
library was created to map all of node's popular template
engines to follow this convention, thus allowing them to
work seemlessly within Express.
# app.param()
Map the given param placeholder `name`(s) to the given callback(s).
Parameter mapping is used to provide pre-conditions to routes
which use normalized placeholders. For example a _:user_id_ parameter
could automatically load a user's information from the database without
any additional code,
The callback uses the samesignature as middleware, the only differencing
being that the value of the placeholder is passed, in this case the _id_
of the user. Once the `next()` function is invoked, just like middleware
it will continue on to execute the route, or subsequent parameter functions.
app.param('user_id', function(req, res, next, id){
User.find(id, function(err, user){
if (err) {
next(err);
} else if (user) {
req.user = user;
next();
} else {
next(new Error('failed to load user'));
}
});
});
# app.set()
Assign `setting` to `val`, or return `setting`'s value.
app.set('foo', 'bar');
app.get('foo');
// => "bar"
Mounted servers inherit their parent server's settings.
# app.enabled()
Check if `setting` is enabled (truthy).
app.enabled('foo')
// => false
app.enable('foo')
app.enabled('foo')
// => true
# app.disabled()
Check if `setting` is disabled.
app.disabled('foo')
// => true
app.enable('foo')
app.disabled('foo')
// => false
# app.enable()
Enable `setting`.
# app.disable()
Disable `setting`.
# app.configure()
Configure callback for zero or more envs,
when no `env` is specified that callback will
be invoked for all environments. Any combination
can be used multiple times, in any order desired.
## Examples
app.configure(function(){
// executed for all envs
});
app.configure('stage', function(){
// executed staging env
});
app.configure('stage', 'production', function(){
// executed for stage and production
});
## Note
These callbacks are invoked immediately, and
are effectively sugar for the following.
var env = process.env.NODE_ENV || 'development';
switch (env) {
case 'development':
...
break;
case 'stage':
...
break;
case 'production':
...
break;
}
# app.all()
Special-cased "all" method, applying the given route `path`,
middleware, and callback to _every_ HTTP method.
# app.render()
Render the given view `name` name with `options`
and a callback accepting an error and the
rendered template string.
## Example
app.render('email', { name: 'Tobi' }, function(err, html){
// ...
})
# app.listen()
Listen for connections.
A node `http.Server` is returned, with this
application (which is a `Function`) as its
callback. If you wish to create both an HTTP
and HTTPS server you may do so with the "http"
and "https" modules as shown here.
var http = require('http')
, https = require('https')
, express = require('express')
, app = express();
http.createServer(app).listen(80);
http.createServer({ ... }, app).listen(443);
-511
Ver Arquivo
@@ -1,511 +0,0 @@
# app
Application prototype.
# app.use()
Proxy `connect#use()` to apply settings to
mounted applications.
# app.engine()
Register the given template engine callback `fn`
as `ext`.
By default will `require()` the engine based on the
file extension. For example if you try to render
a "foo.jade" file Express will invoke the following internally:
app.engine('jade', require('jade').__express);
For engines that do not provide `.__express` out of the box,
or if you wish to "map" a different extension to the template engine
you may use this method. For example mapping the EJS template engine to
".html" files
app.engine('html', require('ejs').renderFile);
In this case EJS provides a `.renderFile()` method with
the same signature that Express expects: `(path, options, callback)`,
though note that it aliases this method as `ejs.__express` internally
so if you're using ".ejs" extensions you dont need to do anything.
Some template engines do not follow this convention, the
[Consolidate.js](https://github.com/visionmedia/consolidate.js)
library was created to map all of node's popular template
engines to follow this convention, thus allowing them to
work seemlessly within Express.
# app.param()
Map the given param placeholder `name`(s) to the given callback(s).
Parameter mapping is used to provide pre-conditions to routes
which use normalized placeholders. For example a _:user_id_ parameter
could automatically load a user's information from the database without
any additional code,
The callback uses the samesignature as middleware, the only differencing
being that the value of the placeholder is passed, in this case the _id_
of the user. Once the `next()` function is invoked, just like middleware
it will continue on to execute the route, or subsequent parameter functions.
app.param('user_id', function(req, res, next, id){
User.find(id, function(err, user){
if (err) {
next(err);
} else if (user) {
req.user = user;
next();
} else {
next(new Error('failed to load user'));
}
});
});
# app.set()
Assign `setting` to `val`, or return `setting`'s value.
app.set('foo', 'bar');
app.get('foo');
// => "bar"
Mounted servers inherit their parent server's settings.
# app.enabled()
Check if `setting` is enabled (truthy).
app.enabled('foo')
// => false
app.enable('foo')
app.enabled('foo')
// => true
# app.disabled()
Check if `setting` is disabled.
app.disabled('foo')
// => true
app.enable('foo')
app.disabled('foo')
// => false
# app.enable()
Enable `setting`.
# app.disable()
Disable `setting`.
# app.configure()
Configure callback for zero or more envs,
when no `env` is specified that callback will
be invoked for all environments. Any combination
can be used multiple times, in any order desired.
## Examples
app.configure(function(){
// executed for all envs
});
app.configure('stage', function(){
// executed staging env
});
app.configure('stage', 'production', function(){
// executed for stage and production
});
## Note
These callbacks are invoked immediately, and
are effectively sugar for the following.
var env = process.env.NODE_ENV || 'development';
switch (env) {
case 'development':
...
break;
case 'stage':
...
break;
case 'production':
...
break;
}
# app.all()
Special-cased "all" method, applying the given route `path`,
middleware, and callback to _every_ HTTP method.
# app.render()
Render the given view `name` name with `options`
and a callback accepting an error and the
rendered template string.
## Example
app.render('email', { name: 'Tobi' }, function(err, html){
// ...
})
# app.listen()
Listen for connections.
A node `http.Server` is returned, with this
application (which is a `Function`) as its
callback. If you wish to create both an HTTP
and HTTPS server you may do so with the "http"
and "https" modules as shown here.
var http = require('http')
, https = require('https')
, express = require('express')
, app = express();
http.createServer(app).listen(80);
http.createServer({ ... }, app).listen(443);
# req
Request prototype.
# req.get
Return request header.
The `Referrer` header field is special-cased,
both `Referrer` and `Referer` are interchangeable.
## Examples
req.get('Content-Type');
// => "text/plain"
req.get('content-type');
// => "text/plain"
req.get('Something');
// => undefined
Aliased as `req.header()`.
# req.accepts()
Check if the given `type(s)` is acceptable, returning
the best match when true, otherwise `undefined`, in which
case you should respond with 406 "Not Acceptable".
The `type` value may be a single mime type string
such as "application/json", the extension name
such as "json", a comma-delimted list such as "json, html, text/plain",
or an array `["json", "html", "text/plain"]`. When a list
or array is given the _best_ match, if any is returned.
## Examples
// Accept: text/html
req.accepts('html');
// => "html"
// Accept: text/*, application/json
req.accepts('html');
// => "html"
req.accepts('text/html');
// => "text/html"
req.accepts('json, text');
// => "json"
req.accepts('application/json');
// => "application/json"
// Accept: text/*, application/json
req.accepts('image/png');
req.accepts('png');
// => undefined
// Accept: text/*;q=.5, application/json
req.accepts(['html', 'json']);
req.accepts('html, json');
// => "json"
# req.acceptsCharset()
Check if the given `charset` is acceptable,
otherwise you should respond with 406 "Not Acceptable".
# req.acceptsLanguage()
Check if the given `lang` is acceptable,
otherwise you should respond with 406 "Not Acceptable".
# req.param()
Return the value of param `name` when present or `defaultValue`.
- Checks body params, ex: id=12, {"id":12}
- Checks route placeholders, ex: _/user/:id_
- Checks query string params, ex: ?id=12
To utilize request bodies, `req.body`
should be an object. This can be done by using
the `connect.bodyParser()` middleware.
# req.is()
Check if the incoming request contains the "Content-Type"
header field, and it contains the give mime `type`.
## Examples
// With Content-Type: text/html; charset=utf-8
req.is('html');
req.is('text/html');
req.is('text/*');
// => true
// When Content-Type is application/json
req.is('json');
req.is('application/json');
req.is('application/*');
// => true
req.is('html');
// => false
Now within our route callbacks, we can use to to assert content types
such as "image/jpeg", "image/png", etc.
app.post('/image/upload', function(req, res, next){
if (req.is('image/*')) {
// do something
} else {
next();
}
});
# res
Response prototype.
# res.status()
Set status `code`.
# res.send()
Send a response.
## Examples
res.send(new Buffer('wahoo'));
res.send({ some: 'json' });
res.send('<p>some html</p>');
res.send(404, 'Sorry, cant find that');
res.send(404);
# res.json()
Send JSON response.
## Examples
res.json(null);
res.json({ user: 'tj' });
res.json(500, 'oh noes!');
res.json(404, 'I dont have that');
# res.sendfile()
Transfer the file at the given `path`.
Automatically sets the _Content-Type_ response header field.
The callback `fn(err)` is invoked when the transfer is complete
or when an error occurs. Be sure to check `res.sentHeader`
if you wish to attempt responding, as the header and some data
may have already been transferred.
## Options
- `maxAge` defaulting to 0
- `root` root directory for relative filenames
## Examples
The following example illustrates how `res.sendfile()` may
be used as an alternative for the `static()` middleware for
dynamic situations. The code backing `res.sendfile()` is actually
the same code, so HTTP cache support etc is identical.
app.get('/user/:uid/photos/:file', function(req, res){
var uid = req.params.uid
, file = req.params.file;
req.user.mayViewFilesFrom(uid, function(yes){
if (yes) {
res.sendfile('/uploads/' + uid + '/' + file);
} else {
res.send(403, 'Sorry! you cant see that.');
}
});
});
# res.download()
Transfer the file at the given `path` as an attachment.
Optionally providing an alternate attachment `filename`,
and optional callback `fn(err)`. The callback is invoked
when the data transfer is complete, or when an error has
ocurred. Be sure to check `res.headerSent` if you plan to respond.
# res.format()
Respond to the Acceptable formats using an `obj`
of mime-type callbacks.
This method uses `req.accepted`, an array of
acceptable types ordered by their quality values.
When "Accept" is not present the _first_ callback
is invoked, otherwise the first match is used. When
no match is performed the server responds with
406 "Not Acceptable".
Content-Type is set for you, however if you choose
you may alter this within the callback using `res.type()`
or `res.set('Content-Type', ...)`.
res.format({
'text/plain': function(){
res.send('hey');
},
'text/html': function(){
res.send('<p>hey</p>');
},
'appliation/json': function(){
res.send({ message: 'hey' });
}
});
In addition to canonicalized MIME types you may
## also use extnames mapped to these types
res.format({
text: function(){
res.send('hey');
},
html: function(){
res.send('<p>hey</p>');
},
json: function(){
res.send({ message: 'hey' });
}
});
# res.attachment()
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
# res.set()
Set header `field` to `val`, or pass
an object of of header fields.
## Examples
res.set('Accept', 'application/json');
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
# res.get()
Get value for header `field`.
# res.clearCookie()
Clear cookie `name`.
# res.signedCookie()
Set a signed cookie with the given `name` and `val`.
See `res.cookie()` for details.
# res.cookie()
Set cookie `name` to `val`, with the given `options`.
## Options
- `maxAge` max-age in milliseconds, converted to `expires`
- `path` defaults to "/"
## Examples
// "Remember Me" for 15 minutes
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
// save as above
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
# res.redirect()
Redirect to the given `url` with optional response `status`
defaulting to 302.
The given `url` can also be the name of a mapped url, for
example by default express supports "back" which redirects
to the _Referrer_ or _Referer_ headers or "/".
## Examples
res.redirect('/foo/bar');
res.redirect('http://example.com');
res.redirect(301, 'http://example.com');
res.redirect('../login'); // /blog/post/1 -> /blog/login
## Mounting
When an application is mounted, and `res.redirect()`
is given a path that does _not_ lead with "/". For
example suppose a "blog" app is mounted at "/blog",
the following redirect would result in "/blog/login":
res.redirect('login');
While the leading slash would result in a redirect to "/login":
res.redirect('/login');
# res.render()
Render `view` with the given `options` and optional callback `fn`.
When a callback function is given a response will _not_ be made
automatically, otherwise a response of _200_ and _text/html_ is given.
## Options
- `status` Response status code (`res.statusCode`)
- `charset` Set the charset (`res.charset`)
## Reserved locals
- `cache` boolean hinting to the engine it should cache
- `filename` filename of the view being rendered
-118
Ver Arquivo
@@ -1,118 +0,0 @@
# req
Request prototype.
# req.get
Return request header.
The `Referrer` header field is special-cased,
both `Referrer` and `Referer` are interchangeable.
## Examples
req.get('Content-Type');
// => "text/plain"
req.get('content-type');
// => "text/plain"
req.get('Something');
// => undefined
Aliased as `req.header()`.
# req.accepts()
Check if the given `type(s)` is acceptable, returning
the best match when true, otherwise `undefined`, in which
case you should respond with 406 "Not Acceptable".
The `type` value may be a single mime type string
such as "application/json", the extension name
such as "json", a comma-delimted list such as "json, html, text/plain",
or an array `["json", "html", "text/plain"]`. When a list
or array is given the _best_ match, if any is returned.
## Examples
// Accept: text/html
req.accepts('html');
// => "html"
// Accept: text/*, application/json
req.accepts('html');
// => "html"
req.accepts('text/html');
// => "text/html"
req.accepts('json, text');
// => "json"
req.accepts('application/json');
// => "application/json"
// Accept: text/*, application/json
req.accepts('image/png');
req.accepts('png');
// => undefined
// Accept: text/*;q=.5, application/json
req.accepts(['html', 'json']);
req.accepts('html, json');
// => "json"
# req.acceptsCharset()
Check if the given `charset` is acceptable,
otherwise you should respond with 406 "Not Acceptable".
# req.acceptsLanguage()
Check if the given `lang` is acceptable,
otherwise you should respond with 406 "Not Acceptable".
# req.param()
Return the value of param `name` when present or `defaultValue`.
- Checks body params, ex: id=12, {"id":12}
- Checks route placeholders, ex: _/user/:id_
- Checks query string params, ex: ?id=12
To utilize request bodies, `req.body`
should be an object. This can be done by using
the `connect.bodyParser()` middleware.
# req.is()
Check if the incoming request contains the "Content-Type"
header field, and it contains the give mime `type`.
## Examples
// With Content-Type: text/html; charset=utf-8
req.is('html');
req.is('text/html');
req.is('text/*');
// => true
// When Content-Type is application/json
req.is('json');
req.is('application/json');
req.is('application/*');
// => true
req.is('html');
// => false
Now within our route callbacks, we can use to to assert content types
such as "image/jpeg", "image/png", etc.
app.post('/image/upload', function(req, res, next){
if (req.is('image/*')) {
// do something
} else {
next();
}
});
-212
Ver Arquivo
@@ -1,212 +0,0 @@
# res
Response prototype.
# res.status()
Set status `code`.
# res.send()
Send a response.
## Examples
res.send(new Buffer('wahoo'));
res.send({ some: 'json' });
res.send('<p>some html</p>');
res.send(404, 'Sorry, cant find that');
res.send(404);
# res.json()
Send JSON response.
## Examples
res.json(null);
res.json({ user: 'tj' });
res.json(500, 'oh noes!');
res.json(404, 'I dont have that');
# res.sendfile()
Transfer the file at the given `path`.
Automatically sets the _Content-Type_ response header field.
The callback `fn(err)` is invoked when the transfer is complete
or when an error occurs. Be sure to check `res.sentHeader`
if you wish to attempt responding, as the header and some data
may have already been transferred.
## Options
- `maxAge` defaulting to 0
- `root` root directory for relative filenames
## Examples
The following example illustrates how `res.sendfile()` may
be used as an alternative for the `static()` middleware for
dynamic situations. The code backing `res.sendfile()` is actually
the same code, so HTTP cache support etc is identical.
app.get('/user/:uid/photos/:file', function(req, res){
var uid = req.params.uid
, file = req.params.file;
req.user.mayViewFilesFrom(uid, function(yes){
if (yes) {
res.sendfile('/uploads/' + uid + '/' + file);
} else {
res.send(403, 'Sorry! you cant see that.');
}
});
});
# res.download()
Transfer the file at the given `path` as an attachment.
Optionally providing an alternate attachment `filename`,
and optional callback `fn(err)`. The callback is invoked
when the data transfer is complete, or when an error has
ocurred. Be sure to check `res.headerSent` if you plan to respond.
# res.format()
Respond to the Acceptable formats using an `obj`
of mime-type callbacks.
This method uses `req.accepted`, an array of
acceptable types ordered by their quality values.
When "Accept" is not present the _first_ callback
is invoked, otherwise the first match is used. When
no match is performed the server responds with
406 "Not Acceptable".
Content-Type is set for you, however if you choose
you may alter this within the callback using `res.type()`
or `res.set('Content-Type', ...)`.
res.format({
'text/plain': function(){
res.send('hey');
},
'text/html': function(){
res.send('<p>hey</p>');
},
'appliation/json': function(){
res.send({ message: 'hey' });
}
});
In addition to canonicalized MIME types you may
## also use extnames mapped to these types
res.format({
text: function(){
res.send('hey');
},
html: function(){
res.send('<p>hey</p>');
},
json: function(){
res.send({ message: 'hey' });
}
});
# res.attachment()
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
# res.set()
Set header `field` to `val`, or pass
an object of of header fields.
## Examples
res.set('Accept', 'application/json');
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
# res.get()
Get value for header `field`.
# res.clearCookie()
Clear cookie `name`.
# res.signedCookie()
Set a signed cookie with the given `name` and `val`.
See `res.cookie()` for details.
# res.cookie()
Set cookie `name` to `val`, with the given `options`.
## Options
- `maxAge` max-age in milliseconds, converted to `expires`
- `path` defaults to "/"
## Examples
// "Remember Me" for 15 minutes
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
// save as above
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
# res.redirect()
Redirect to the given `url` with optional response `status`
defaulting to 302.
The given `url` can also be the name of a mapped url, for
example by default express supports "back" which redirects
to the _Referrer_ or _Referer_ headers or "/".
## Examples
res.redirect('/foo/bar');
res.redirect('http://example.com');
res.redirect(301, 'http://example.com');
res.redirect('../login'); // /blog/post/1 -> /blog/login
## Mounting
When an application is mounted, and `res.redirect()`
is given a path that does _not_ lead with "/". For
example suppose a "blog" app is mounted at "/blog",
the following redirect would result in "/blog/login":
res.redirect('login');
While the leading slash would result in a redirect to "/login":
res.redirect('/login');
# res.render()
Render `view` with the given `options` and optional callback `fn`.
When a callback function is given a response will _not_ be made
automatically, otherwise a response of _200_ and _text/html_ is given.
## Options
- `status` Response status code (`res.statusCode`)
- `charset` Set the charset (`res.charset`)
## Reserved locals
- `cache` boolean hinting to the engine it should cache
- `filename` filename of the view being rendered
+29 -24
Ver Arquivo
@@ -4,20 +4,24 @@
*/
var express = require('../../lib/express')
, crypto = require('crypto');
, hash = require('./pass').hash;
var app = module.exports = express();
// config
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
// middleware
app.use(express.bodyParser());
app.use(express.cookieParser('shhhh, very secret'));
app.use(express.session());
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
// Session-persisted message middleware
app.locals.use(function(req,res){
app.use(function(req, res, next){
var err = req.session.error
, msg = req.session.success;
delete req.session.error;
@@ -25,26 +29,25 @@ app.locals.use(function(req,res){
res.locals.message = '';
if (err) res.locals.message = '<p class="msg error">' + err + '</p>';
if (msg) res.locals.message = '<p class="msg success">' + msg + '</p>';
})
next();
});
// dummy database
// Generate a salt for the user to prevent rainbow table attacks
// for better security take a look at the bcrypt c++ addon:
// https://github.com/ncb000gt/node.bcrypt.js
var users = {
tj: {
name: 'tj'
, salt: 'randomly-generated-salt'
, pass: hash('foobar', 'randomly-generated-salt')
}
tj: { name: 'tj' }
};
// Used to generate a hash of the plain-text password + salt
function hash(msg, key) {
return crypto
.createHmac('sha256', key)
.update(msg)
.digest('hex');
}
// when you create a user, generate a salt
// and hash the password ('foobar' is the pass here)
hash('foobar', function(err, salt, hash){
if (err) throw err;
// store the salt & hash in the "db"
users.tj.salt = salt;
users.tj.hash = hash;
});
// Authenticate using our plain-object database of doom!
function authenticate(name, pass, fn) {
@@ -55,9 +58,11 @@ function authenticate(name, pass, fn) {
// apply the same algorithm to the POSTed password, applying
// the hash against the pass / salt, if there is a match we
// found the user
if (user.pass == hash(pass, user.salt)) return fn(null, user);
// Otherwise password is invalid
fn(new Error('invalid password'));
hash(pass, user.salt, function(err, hash){
if (err) return fn(err);
if (hash == user.hash) return fn(null, user);
fn(new Error('invalid password'));
})
}
function restrict(req, res, next) {
+46
Ver Arquivo
@@ -0,0 +1,46 @@
// check out https://github.com/visionmedia/node-pwd
/**
* Module dependencies.
*/
var crypto = require('crypto');
/**
* Bytesize.
*/
var len = 128;
/**
* Iterations. ~300ms
*/
var iterations = 12000;
/**
* Hashes a password with optional `salt`, otherwise
* generate a salt for `pass` and invoke `fn(err, salt, hash)`.
*
* @param {String} password to hash
* @param {String} optional salt
* @param {Function} callback
* @api public
*/
exports.hash = function (pwd, salt, fn) {
if (3 == arguments.length) {
crypto.pbkdf2(pwd, salt, iterations, len, fn);
} else {
fn = salt;
crypto.randomBytes(len, function(err, salt){
if (err) return fn(err);
salt = salt.toString('base64');
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
if (err) return fn(err);
fn(null, salt, hash);
});
});
}
};
+8
Ver Arquivo
@@ -0,0 +1,8 @@
var users = [];
users.push({ name: 'Tobi' });
users.push({ name: 'Loki' });
users.push({ name: 'Jane' });
module.exports = users;
+14 -7
Ver Arquivo
@@ -1,12 +1,7 @@
var express = require('../../')
, app = module.exports = express();
var users = [];
users.push({ name: 'Tobi' });
users.push({ name: 'Loki' });
users.push({ name: 'Jane' });
, app = module.exports = express()
, users = require('./db');
app.get('/', function(req, res){
res.format({
@@ -28,6 +23,18 @@ app.get('/', function(req, res){
})
});
// or you could write a tiny middleware like
// this to abstract make things a bit more declarative:
function format(mod) {
var obj = require(mod);
return function(req, res){
res.format(obj);
}
}
app.get('/users', format('./users'));
if (!module.parent) {
app.listen(3000);
console.log('listening on port 3000');
+18
Ver Arquivo
@@ -0,0 +1,18 @@
var users = require('./db');
exports.html = function(req, res){
res.send('<ul>' + users.map(function(user){
return '<li>' + user.name + '</li>';
}).join('') + '</ul>');
};
exports.text = function(req, res){
res.send(users.map(function(user){
return ' - ' + user.name + '\n';
}).join(''));
};
exports.json = function(req, res){
res.json(users);
};
+45
Ver Arquivo
@@ -0,0 +1,45 @@
/**
* Module dependencies.
*/
var express = require('../..')
, app = express()
, api = express();
// app middleware
app.use(express.static(__dirname + '/public'));
// api middleware
api.use(express.logger('dev'));
api.use(express.bodyParser());
/**
* CORS support.
*/
api.all('*', function(req, res, next){
// use "*" here to accept any origin
res.set('Access-Control-Allow-Origin', 'http://localhost:3000');
res.set('Access-Control-Allow-Methods', 'GET, POST');
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
// res.set('Access-Control-Allow-Max-Age', 3600);
next();
});
/**
* POST a user.
*/
api.post('/user', function(req, res){
console.log(req.body);
res.send(201);
});
app.listen(3000);
api.listen(3001);
console.log('app listening on 3000');
console.log('api listening on 3001');
+12
Ver Arquivo
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<script>
var req = new XMLHttpRequest;
req.open('POST', 'http://localhost:3001/user', false);
req.setRequestHeader('Content-Type', 'application/json');
req.send('{"name":"tobi","species":"ferret"}');
console.log(req.responseText);
</script>
</body>
</html>
-8
Ver Arquivo
@@ -45,14 +45,6 @@ app.use(function(req, res, next){
next();
});
// if you wanted to _always_ expose
// the user you might do something like this:
/*
app.locals.use(function(req, res){
if (req.user) res.locals.expose.user = req.user;
})
*/
app.get('/', function(req, res){
res.redirect('/user');
});
+2 -3
Ver Arquivo
@@ -9,10 +9,9 @@ var express = require('../../lib/express');
var pub = __dirname + '/public';
// Auto-compile sass to css with "compiler"
// and then serve with connect's staticProvider
// setup middleware
var app = express.createServer();
var app = express();
app.use(app.router);
app.use(express.static(pub));
app.use(express.errorHandler());
+18 -17
Ver Arquivo
@@ -25,8 +25,24 @@ app.response.message = function(msg){
return this;
};
// log
if (!module.parent) app.use(express.logger('dev'));
// serve static files
app.use(express.static(__dirname + '/public'));
// session support
app.use(express.cookieParser('some secret here'));
app.use(express.session());
// parse request bodies (req.body)
app.use(express.bodyParser());
// support _method (PUT in forms etc)
app.use(express.methodOverride());
// expose the "messages" local variable when views are rendered
app.locals.use(function(req, res){
app.use(function(req, res, next){
var msgs = req.session.messages || [];
// expose "messages" local variable
@@ -45,24 +61,9 @@ app.locals.use(function(req, res){
// empty or "flush" the messages so they
// don't build up
req.session.messages = [];
next();
});
// log
if (!module.parent) app.use(express.logger('dev'));
// serve static files
app.use(express.static(__dirname + '/public'));
// session support
app.use(express.cookieParser('some secret here'));
app.use(express.session());
// parse request bodies (req.body)
app.use(express.bodyParser());
// support _method (PUT in forms etc)
app.use(express.methodOverride());
// load controllers
require('./lib/boot')(app, { verbose: !module.parent });
-1
Ver Arquivo
@@ -37,7 +37,6 @@ module.exports = function(parent, options){
case 'show':
method = 'get';
path = '/' + name + '/:' + name + '_id';
app[method](path, obj[key]);
break;
case 'list':
method = 'get';
-32
Ver Arquivo
@@ -1,32 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var app = express.createServer();
// Optional since express defaults to CWD/views
app.set('views', __dirname + '/views');
// Set our default template engine to "jade"
// which prevents the need for extensions
// (although you can still mix and match)
app.set('view engine', 'jade');
// Dummy record
var ninja = {
name: 'leonardo',
summary: { email: 'hunter.loftis+github@gmail.com', master: 'splinter', description: 'peaceful leader' },
weapons: ['katana', 'fists', 'shell'],
victims: ['shredder', 'brain', 'beebop', 'rocksteady']
};
app.get('/', function(req, res){
res.render('ninja', { ninja: ninja });
});
app.listen(3000);
console.log('Express app started on port 3000');
-5
Ver Arquivo
@@ -1,5 +0,0 @@
!!!
html
head
title Partials Example
body!= body
-1
Ver Arquivo
@@ -1 +0,0 @@
li= value
-1
Ver Arquivo
@@ -1 +0,0 @@
li.weapon= weapon
-22
Ver Arquivo
@@ -1,22 +0,0 @@
h1= ninja.name
// file, partial name, and partial object all match ('summary')
// the partial filename prefix '_' is completely optional.
// In this case we need to specify ninja.summary as the object
// option, since it is a "plain" object Express cannot otherwise
// tell if it is intended to be locals, or THE summary object
#summary!= partial('summary', { object: ninja.summary })
// file, partial name = '_weapon', resolves to 'weapon' object within partial
#weapons
h2 Weapons
// the weapon partial is rendered once per item in
// the weapons array or "collection"
ul!= partial('weapon', ninja.weapons)
// partial name 'victim' resolves to 'victim.jade'
// or 'victim/index.jade', providing the "victim" local
#victims
h2 Victims
ul!= partial('victim', ninja.victims)
-4
Ver Arquivo
@@ -1,4 +0,0 @@
h2 Summary
p= summary.email
p= summary.description
p taught by master #{summary.master}
@@ -1,5 +0,0 @@
// this is insane overkill, I do not recommend
// doing tiny partials like this as it gets expensive
// with collections, however this illustrates the new
// partial lookup mechanism
!= partial('../../li', { object: victim, as: 'value' })
+63
Ver Arquivo
@@ -0,0 +1,63 @@
var express = require('../../lib/express')
, verbose = process.env.NODE_ENV != 'test'
, app = module.exports = express();
app.map = function(a, route){
route = route || '';
for (var key in a) {
switch (typeof a[key]) {
// { '/path': { ... }}
case 'object':
app.map(a[key], route + key);
break;
// get: function(){ ... }
case 'function':
if (verbose) console.log('%s %s', key, route);
app[key](route, a[key]);
break;
}
}
};
var users = {
list: function(req, res){
res.send('user list');
},
get: function(req, res){
res.send('user ' + req.params.uid);
},
del: function(req, res){
res.send('delete users');
}
};
var pets = {
list: function(req, res){
res.send('user ' + req.params.uid + '\'s pets');
},
del: function(req, res){
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
}
};
app.map({
'/users': {
get: users.list,
del: users.del,
'/:uid': {
get: users.get,
'/pets': {
get: pets.list,
'/:pid': {
del: pets.del
}
}
}
}
});
app.listen(3000);
@@ -4,7 +4,7 @@
var express = require('../../lib/express');
var app = express.createServer();
var app = express();
// Example requests:
// curl http://localhost:3000/user/0
@@ -3,8 +3,8 @@
* Module dependencies.
*/
var express = require('../../lib/express')
, app = express.createServer()
var express = require('../..')
, app = express()
, site = require('./site')
, post = require('./post')
, user = require('./user');
-44
Ver Arquivo
@@ -1,44 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../')
, path = require('path')
, exec = require('child_process').exec
, fs = require('fs');
/**
* Error handler.
*/
function errorHandler(voice) {
return function(err, req, res, next) {
var parts = err.stack.split('\n')[1].split(/[()]/)[1].split(':')
, filename = parts.shift()
, basename = path.basename(filename)
, lineno = parts.shift()
, col = parts.shift()
, lines = fs.readFileSync(filename, 'utf8').split('\n')
, line = lines[lineno - 1].replace(/\./, ' ');
exec('say -v "' + voice + '" '
+ err.message
+ ' on line ' + lineno
+ ' of ' + basename + '.'
+ ' The contents of this line is '
+ ' "' + line + '".');
res.send(500);
}
}
var app = express.createServer();
app.get('/', function(request, response){
if (request.is(foo)) response.end('bar');
});
app.use(errorHandler('Vicki'));
app.listen(3000);
-51
Ver Arquivo
@@ -1,51 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express')
, stylus = require('stylus');
var app = express.createServer();
// $ npm install stylus
// completely optional, however
// the compile function allows you to
// define additional functions exposed to Stylus,
// alter settings, etc
function compile(str, path) {
return stylus(str)
.set('filename', path)
.set('compress', true);
};
// add the stylus middleware, which re-compiles when
// a stylesheet has changed, compiling FROM src,
// TO dest. dest is optional, defaulting to src
app.use(stylus.middleware({
src: __dirname + '/views'
, dest: __dirname + '/public'
, compile: compile
}));
// minimal setup both reading and writting to ./public
// would look like:
// app.use(stylus.middleware({ src: __dirname + '/public' }));
// the middleware itself does not serve the static
// css files, so we need to expose them with staticProvider
app.use(express.static(__dirname + '/public'));
app.set('views', __dirname + '/views');
app.get('/', function(req, res){
res.render('index.jade');
});
app.listen(3000);
console.log('server listening on port 3000');
-1
Ver Arquivo
@@ -1 +0,0 @@
*.css
-2
Ver Arquivo
@@ -1,2 +0,0 @@
h1 Stylus
p Just an example of using Stylus with Express.
-6
Ver Arquivo
@@ -1,6 +0,0 @@
html
head
title Stylus Example
link(rel='stylesheet', href='/reset.css')
link(rel='stylesheet', href='/main.css')
body!= body
-8
Ver Arquivo
@@ -1,8 +0,0 @@
body
font 14px helvetica, arial, sans-serif
padding 50px
h1
font-size 50px
p
margin 15px 0
@@ -3,13 +3,13 @@
* Module dependencies.
*/
var express = require('../../lib/express');
var express = require('../..');
// Edit /etc/vhosts
// First app
var one = express.createServer();
var one = express();
one.use(express.logger());
@@ -23,7 +23,7 @@ one.get('/:sub', function(req, res){
// App two
var two = express.createServer();
var two = express();
two.get('/', function(req, res){
res.send('Hello from app two!')
@@ -31,7 +31,7 @@ two.get('/', function(req, res){
// Redirect app
var redirect = express.createServer();
var redirect = express();
redirect.all('*', function(req, res){
console.log(req.subdomains);
@@ -40,7 +40,7 @@ redirect.all('*', function(req, res){
// Main app
var app = express.createServer();
var app = express();
app.use(express.vhost('*.localhost', redirect))
app.use(express.vhost('localhost', one));
+41 -23
Ver Arquivo
@@ -88,7 +88,6 @@ function count2(req, res, next) {
function users2(req, res, next) {
User.all(function(err, users){
if (err) return next(err);
// this would not be ideal for *this*
res.locals.users = users.filter(ferrets);
next();
})
@@ -103,31 +102,50 @@ app.get('/middleware-locals', count2, users2, function(req, res, next){
});
// let's assume we wanted to load the users
// and count for every res.render() call, we
// could use app.locals.use() for this. These
// are callbacks which run in parallel ONLY
// when res.render() is invoked. If no views
// are rendered, there is no overhead.
// This may be ideal if you want to load auxiliary
// user information, but only for templates. Note
// that (req, res) are available to you, so you may
// access req.session.user etc.
// Keep in mind these execute in *parallel*, so these
// callbacks should not depend on each other, this
// also makes them slightly more efficient than
// using middleware which execute sequentially
app.locals.use(count2);
app.locals.use(users2);
app.get('/locals', function(req, res){
res.render('user', { title: 'Users' });
});
// keep in mind that middleware may be placed anywhere
// and in various combinations, so if you have locals
// that you wish to make available to all subsequent
// middleware/routes you can do something like this:
/*
app.use(function(req, res, next){
res.locals.user = req.user;
res.locals.sess = req.session;
next();
});
*/
// or suppose you have some /admin
// "global" local variables:
/*
app.use('/api', function(req, res, next){
res.locals.user = req.user;
res.locals.sess = req.session;
next();
});
*/
// the following is effectively the same,
// but uses a route instead:
/*
app.all('/api/*', function(req, res, next){
res.locals.user = req.user;
res.locals.sess = req.session;
next();
});
*/
app.listen(3000);
console.log('Application listening on port 3000');
+6 -6
Ver Arquivo
@@ -1,11 +1,10 @@
/**
* Module dependencies.
*/
var connect = require('connect')
, Router = require('./router')
, methods = Router.methods.concat('del', 'all')
, methods = require('methods')
, middleware = require('./middleware')
, debug = require('debug')('express:application')
, locals = require('./utils').locals
@@ -59,12 +58,11 @@ app.defaultConfiguration = function(){
this.use(connect.query());
this.use(middleware.init(this));
// inherit view callbacks
// inherit protos
this.on('mount', function(parent){
this.request.__proto__ = parent.request;
this.response.__proto__ = parent.response;
this.engines.__proto__ = parent.engines;
this.viewCallbacks = parent.viewCallbacks.slice(0);
});
// router
@@ -85,6 +83,7 @@ app.defaultConfiguration = function(){
// default configuration
this.enable('jsonp callback');
this.set('jsonp callback name', 'callback');
this.configure('development', function(){
this.set('json spaces', 2);
@@ -427,7 +426,6 @@ methods.forEach(function(method){
app.all = function(path){
var args = arguments;
methods.forEach(function(method){
if ('all' == method || 'del' == method) return;
app[method].apply(this, args);
}, this);
return this;
@@ -492,7 +490,9 @@ app.render = function(name, options, fn){
});
if (!view.path) {
return fn(new Error('Failed to lookup view "' + name + '"'));
var err = new Error('Failed to lookup view "' + name + '"');
err.view = view;
return fn(err);
}
// prime the cache
+19 -9
Ver Arquivo
@@ -10,6 +10,7 @@ var http = require('http')
, Router = require('./router')
, req = require('./request')
, res = require('./response')
, send = require('send')
, utils = connect.utils;
/**
@@ -22,7 +23,13 @@ exports = module.exports = createApplication;
* Framework version.
*/
exports.version = '3.0.0beta1';
exports.version = '3.0.0rc2';
/**
* Expose mime.
*/
exports.mime = send.mime;
/**
* Create an express application.
@@ -53,10 +60,19 @@ for (var key in connect.middleware) {
}
/**
* Backwards compat.
* Error on createServer().
*/
exports.createServer = createApplication;
exports.createServer = function(){
console.warn('Warning: express.createServer() is deprecated, express');
console.warn('applications no longer inherit from http.Server,');
console.warn('please use:');
console.warn('');
console.warn(' var express = require("express");');
console.warn(' var app = express();');
console.warn('');
return createApplication();
};
/**
* Expose the prototypes.
@@ -73,12 +89,6 @@ exports.response = res;
exports.Route = Route;
exports.Router = Router;
/**
* Expose HTTP methods.
*/
exports.methods = require('./router/methods');
// Error handler title
exports.errorHandler.title = 'Express';
+1 -1
Ver Arquivo
@@ -26,7 +26,7 @@ exports.init = function(app){
req.__proto__ = app.request;
res.__proto__ = app.response;
res.locals = utils.locals(res);
res.locals = res.locals || utils.locals(res);
next();
}
+87 -28
Ver Arquivo
@@ -6,8 +6,10 @@
var http = require('http')
, utils = require('./utils')
, connect = require('connect')
, fresh = require('fresh')
, parseRange = require('range-parser')
, parse = connect.utils.parseUrl
, mime = require('mime');
, mime = connect.mime;
/**
* Request prototype.
@@ -131,6 +133,32 @@ req.acceptsLanguage = function(lang){
: true;
};
/**
* Parse Range header field,
* capping to the given `size`.
*
* Unspecified ranges such as "0-" require
* knowledge of your resource length. In
* the case of a byte range this is of course
* the total number of bytes. If the Range
* header field is not given `null` is returned,
* `-1` when unsatisfiable, `-2` when syntactically invalid.
*
* NOTE: remember that ranges are inclusive, so
* for example "Range: users=0-3" should respond
* with 4 users when available, not 3.
*
* @param {Number} size
* @return {Array}
* @api public
*/
req.range = function(size){
var range = this.get('Range');
if (!range) return;
return parseRange(size, range);
};
/**
* Return an array of Accepted media types
* ordered from highest quality to lowest.
@@ -208,8 +236,8 @@ req.__defineGetter__('acceptedCharsets', function(){
/**
* Return the value of param `name` when present or `defaultValue`.
*
* - Checks body params, ex: id=12, {"id":12}
* - Checks route placeholders, ex: _/user/:id_
* - Checks body params, ex: id=12, {"id":12}
* - Checks query string params, ex: ?id=12
*
* To utilize request bodies, `req.body`
@@ -223,19 +251,12 @@ req.__defineGetter__('acceptedCharsets', function(){
*/
req.param = function(name, defaultValue){
// req.body
if (this.body && undefined !== this.body[name]) return this.body[name];
// route params
if (this.params
&& this.params.hasOwnProperty(name)
&& undefined !== this.params[name]) {
return this.params[name];
}
// query-string
if (undefined !== this.query[name]) return this.query[name];
var params = this.params || {};
var body = this.body || {};
var query = this.query || {};
if (null != params[name] && params.hasOwnProperty(name)) return params[name];
if (null != body[name]) return body[name];
if (null != query[name]) return query[name];
return defaultValue;
};
@@ -260,17 +281,6 @@ req.param = function(name, defaultValue){
* req.is('html');
* // => false
*
* Now within our route callbacks, we can use to to assert content types
* such as "image/jpeg", "image/png" as shown here:
*
* app.post('/image/upload', function(req, res, next){
* if (req.is('image/*')) {
* // do something
* } else {
* next();
* }
* });
*
* @param {String} type
* @return {Boolean}
* @api public
@@ -358,6 +368,33 @@ req.__defineGetter__('ips', function(){
: [];
});
/**
* Return basic auth credentials.
*
* Examples:
*
* // http://tobi:hello@example.com
* req.auth
* // => { username: 'tobi', password: 'hello' }
*
* @return {Object}
* @api public
*/
req.__defineGetter__('auth', function(){
// missing
var auth = this.get('Authorization');
if (!auth) return {};
// malformed
auth = auth.split(' ')[1];
if (!auth) return {};
// credentials
auth = new Buffer(auth, 'base64').toString().split(':');
return { username: auth[0], password: auth[1] };
});
/**
* Return subdomains as an array.
*
@@ -386,6 +423,17 @@ req.__defineGetter__('path', function(){
return parse(this).pathname;
});
/**
* Parse the "Host" header field hostname.
*
* @return {String}
* @api public
*/
req.__defineGetter__('host', function(){
return this.get('Host').split(':')[0];
});
/**
* Check if the request is fresh, aka
* Last-Modified and/or the ETag
@@ -396,7 +444,18 @@ req.__defineGetter__('path', function(){
*/
req.__defineGetter__('fresh', function(){
return ! this.stale;
var method = this.method;
var s = this.res.statusCode;
// GET or HEAD for weak freshness validation only
if ('GET' != method && 'HEAD' != method) return false;
// 2xx or 304 as per rfc2616 14.26
if ((s >= 200 && s < 300) || 304 == s) {
return fresh(this.headers, this.res._headers);
}
return false;
});
/**
@@ -409,7 +468,7 @@ req.__defineGetter__('fresh', function(){
*/
req.__defineGetter__('stale', function(){
return connect.utils.modified(this, this.res);
return !this.fresh;
});
/**
+116 -85
Ver Arquivo
@@ -1,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -12,8 +11,10 @@ var fs = require('fs')
, normalizeTypes = require('./utils').normalizeTypes
, statusCodes = http.STATUS_CODES
, send = connect.static.send
, cookie = require('cookie')
, send = require('send')
, crc = require('crc')
, mime = require('mime')
, mime = connect.mime
, basename = path.basename
, extname = path.extname
, join = path.join;
@@ -39,6 +40,27 @@ res.status = function(code){
return this;
};
/**
* Set Link header field with the given `links`.
*
* Examples:
*
* res.links({
* next: 'http://api.example.com/users?page=2',
* last: 'http://api.example.com/users?page=5'
* });
*
* @param {Object} links
* @return {ServerResponse}
* @api public
*/
res.links = function(links){
return this.set('Link', Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
}).join(', '));
};
/**
* Send a response.
*
@@ -63,10 +85,15 @@ res.send = function(body){
// allow status / body
if (2 == arguments.length) {
this.statusCode = body;
body = arguments[1];
// res.send(body, status) backwards compat
if ('number' != typeof body && 'number' == typeof arguments[1]) {
this.statusCode = arguments[1];
} else {
this.statusCode = body;
body = arguments[1];
}
}
// convert string objects to primitives
if (body instanceof String) body = body.toString();
@@ -109,9 +136,11 @@ res.send = function(body){
if (!this.get('ETag')) this.set('ETag', Buffer.isBuffer(body)
? crc.buffer.crc32(body)
: crc.crc32(body));
if (req.fresh) this.statusCode = 304;
}
// freshness
if (req.fresh) this.statusCode = 304;
// strip irrelevant headers
if (204 == this.statusCode || 304 == this.statusCode) {
this.removeHeader('Content-Type');
@@ -143,20 +172,28 @@ res.send = function(body){
res.json = function(obj){
// allow status / body
if (2 == arguments.length) {
this.statusCode = obj;
obj = arguments[1];
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
} else {
this.statusCode = obj;
obj = arguments[1];
}
}
// settings
var app = this.app
, jsonp = app.get('jsonp callback')
, replacer = app.get('json replacer')
, spaces = app.get('json spaces')
, body = JSON.stringify(obj, replacer, spaces)
, callback = this.req.query.callback;
, callback = this.req.query[app.get('jsonp callback name')];
// content-type
this.charset = this.charset || 'utf-8';
this.set('Content-Type', 'application/json');
// jsonp
if (callback && jsonp) {
this.set('Content-Type', 'text/javascript');
body = callback.replace(/[^[]\w$.]/g, '') + '(' + body + ');';
@@ -209,7 +246,8 @@ res.sendfile = function(path, options, fn){
var self = this
, req = self.req
, next = this.req.next
, options = options || {};
, options = options || {}
, done;
// support function as second arg
if ('function' == typeof options) {
@@ -217,30 +255,49 @@ res.sendfile = function(path, options, fn){
options = {};
}
// callback
options.callback = function(err){
if (err) {
// cast ENOENT
if ('ENOENT' == err.code) err = utils.error(404);
// socket errors
req.socket.on('error', error);
// ditch content-disposition to prevent funky responses
if (!self.headerSent) self.removeHeader('Content-Disposition');
// errors
function error(err) {
if (done) return;
done = true;
// woot! callback available
if (fn) return fn(err);
// clean up
cleanup();
if (!self.headerSent) self.removeHeader('Content-Disposition');
// lost in limbo if there's no callback
if (self.headerSent) return;
// callback available
if (fn) return fn(err);
return req.next(err);
}
// list in limbo if there's no callback
if (self.headerSent) return;
fn && fn();
};
// delegate
next(err);
}
// streaming
function stream() {
if (done) return;
cleanup();
if (fn) self.on('finish', fn);
}
// cleanup
function cleanup() {
req.socket.removeListener('error', error);
}
// transfer
options.path = encodeURIComponent(path);
send(this.req, this, next, options);
var file = send(req, path);
if (options.root) file.root(options.root);
file.maxage(options.maxAge || 0);
file.on('error', error);
file.on('directory', next);
file.on('stream', stream);
file.pipe(this);
this.on('finish', cleanup);
};
/**
@@ -343,21 +400,24 @@ res.type = function(type){
*
* By default Express passes an `Error`
* with a `.status` of 406 to `next(err)`
* if a match is not made, however you may
* provide an optional callback `fn` to
* be invoked instead.
* if a match is not made. If you provide
* a `.default` callback it will be invoked
* instead.
*
* @param {Object} obj
* @param {Function} fn
* @return {ServerResponse} for chaining
* @api public
*/
res.format = function(obj, fn){
res.format = function(obj){
var keys = Object.keys(obj)
, req = this.req
, next = req.next
, key = req.accepts(keys);
, next = req.next;
var fn = obj.default;
if (fn) delete obj.default;
var key = req.accepts(keys);
this.set('Vary', 'Accept');
@@ -394,7 +454,7 @@ res.attachment = function(filename){
/**
* Set header `field` to `val`, or pass
* an object of of header fields.
* an object of header fields.
*
* Examples:
*
@@ -481,8 +541,7 @@ res.cookie = function(name, val, options){
if (signed) val = utils.sign(val, secret);
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
if (null == options.path) options.path = '/';
var cookie = utils.serializeCookie(name, val, options);
this.set('Set-Cookie', cookie);
this.set('Set-Cookie', cookie.serialize(name, String(val), options));
return this;
};
@@ -539,11 +598,11 @@ res.redirect = function(url){
url = map[url] || url;
// relative
if (!~url.indexOf('://')) {
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
var path = app.path();
// relative to path
if (0 == url.indexOf('./') || 0 == url.indexOf('..')) {
if ('.' == url[0]) {
url = req.path + '/' + url;
// relative to mount-point
} else if ('/' != url[0]) {
@@ -552,7 +611,7 @@ res.redirect = function(url){
// Absolute
var host = req.get('Host');
url = req.protocol + '://' + host + url;
url = '//' + host + url;
}
// Support text/{plain,html} by default
@@ -562,11 +621,14 @@ res.redirect = function(url){
},
html: function(){
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
var u = utils.escape(url);
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
},
default: function(){
body = '';
}
}, function(){
body = '';
})
});
// Respond
this.statusCode = status;
@@ -581,11 +643,6 @@ res.redirect = function(url){
* automatically, otherwise a response of _200_ and _text/html_ is given.
*
* Options:
*
* - `status` Response status code (`res.statusCode`)
* - `charset` Set the charset (`res.charset`)
*
* Reserved locals:
*
* - `cache` boolean hinting to the engine it should cache
* - `filename` filename of the view being rendered
@@ -607,41 +664,15 @@ res.render = function(view, options, fn){
fn = options, options = {};
}
function render() {
// merge res.locals
options.locals = self.locals;
// merge res.locals
options.locals = self.locals;
// default callback to respond
fn = fn || function(err, str){
if (err) return req.next(err);
self.send(str);
};
// default callback to respond
fn = fn || function(err, str){
if (err) return req.next(err);
self.send(str);
};
// render
app.render(view, options, fn);
}
// invoke view callbacks
var callbacks = app.viewCallbacks.concat(self.viewCallbacks)
, pending = callbacks.length
, len = pending
, done;
if (len) {
for (var i = 0; i < len; ++i) {
callbacks[i](req, self, function(err){
if (done) return;
if (err) {
req.next(err);
done = true;
return;
}
--pending || render();
});
}
} else {
render();
}
// render
app.render(view, options, fn);
};
+22 -11
Ver Arquivo
@@ -1,4 +1,3 @@
/**
* Module dependencies.
*/
@@ -6,7 +5,8 @@
var Route = require('./route')
, utils = require('../utils')
, debug = require('debug')('express:router')
, parse = require('connect').utils.parseUrl;
, parse = require('connect').utils.parseUrl
, methods = require('methods');
/**
* Expose `Router` constructor.
@@ -14,12 +14,6 @@ var Route = require('./route')
exports = module.exports = Router;
/**
* Expose HTTP methods.
*/
var methods = exports.methods = require('./methods');
/**
* Initialize a new `Router` with the given `options`.
*
@@ -108,7 +102,7 @@ Router.prototype._dispatch = function(req, res, next){
}
// match route
req.route = route = self.match(req, i);
req.route = route = self.matchRequest(req, i);
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
@@ -225,7 +219,7 @@ Router.prototype._optionsFor = function(path){
* @api private
*/
Router.prototype.match = function(req, i, head){
Router.prototype.matchRequest = function(req, i, head){
var method = req.method.toLowerCase()
, url = parse(req)
, path = url.pathname
@@ -235,7 +229,7 @@ Router.prototype.match = function(req, i, head){
// HEAD support
if (!head && 'head' == method) {
route = this.match(req, i, true);
route = this.matchRequest(req, i, true);
if (route) return route;
method = 'get';
}
@@ -254,6 +248,23 @@ Router.prototype.match = function(req, i, head){
}
};
/**
* Attempt to match a route for `method`
* and `url` with optional starting
* index of `i` defaulting to 0.
*
* @param {String} method
* @param {String} url
* @param {Number} i
* @return {Route}
* @api private
*/
Router.prototype.match = function(method, url, i, head){
var req = { method: method, url: url };
return this.matchRequest(req, i, head);
};
/**
* Route `method`, `path`, and one or more callbacks.
*
-30
Ver Arquivo
@@ -1,30 +0,0 @@
/**
* HTTP methods supported by node.
*/
module.exports = [
'get'
, 'post'
, 'put'
, 'head'
, 'delete'
, 'options'
, 'trace'
, 'copy'
, 'lock'
, 'mkcol'
, 'move'
, 'propfind'
, 'proppatch'
, 'unlock'
, 'report'
, 'mkactivity'
, 'checkout'
, 'merge'
, 'm-search'
, 'notify'
, 'subscribe'
, 'unsubscribe'
, 'patch'
];
+4 -15
Ver Arquivo
@@ -3,7 +3,7 @@
* Module dependencies.
*/
var mime = require('mime');
var mime = require('connect').mime;
/**
* Make `locals()` bound to the given `obj`.
@@ -23,18 +23,6 @@ exports.locals = function(obj){
return obj;
};
locals.use = function(fn){
if (3 == fn.length) {
obj.viewCallbacks.push(fn);
} else {
obj.viewCallbacks.push(function(req, res, done){
fn(req, res);
done();
});
}
return obj;
};
return locals;
};
@@ -264,7 +252,7 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
path = path
.concat(strict ? '' : '/?')
.replace(/\/\(/g, '(?:/')
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){
keys.push({ name: key, optional: !! optional });
slash = slash || '';
return ''
@@ -272,7 +260,8 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ (optional || '');
+ (optional || '')
+ (star ? '(/*)?' : '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.*)');
+22 -6
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.0.0beta1",
"version": "3.0.0rc2",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
@@ -10,24 +10,40 @@
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
],
"dependencies": {
"connect": "2.3.0",
"connect": "2.4.2",
"commander": "0.6.1",
"mime": "1.2.5",
"mkdirp": "0.3.2",
"range-parser": "0.0.4",
"mkdirp": "0.3.3",
"cookie": "0.0.4",
"crc": "0.2.0",
"fresh": "0.1.0",
"methods": "0.0.1",
"send": "0.0.3",
"debug": "*"
},
"devDependencies": {
"ejs": "*",
"mocha": "*",
"jade": "*",
"hjs": "*",
"stylus": "*",
"should": "*",
"connect-redis": "*",
"github-flavored-markdown": "*"
"github-flavored-markdown": "*",
"supertest": "0.0.1"
},
"keywords": [
"express",
"framework",
"sinatra",
"web",
"rest",
"restful",
"router",
"app",
"api"
],
"publishConfig": { "tag": "3.0" },
"keywords": ["express", "framework", "sinatra", "web", "rest", "restful", "router"],
"repository": "git://github.com/visionmedia/express",
"main": "index",
"bin": { "express": "./bin/express" },
+31 -5
Ver Arquivo
@@ -12,27 +12,53 @@ describe('Router', function(){
app = express();
})
describe('.match(req, i)', function(){
describe('.match(method, url, i)', function(){
it('should match based on index', function(){
router.route('get', '/foo', function(){});
router.route('get', '/foob?', function(){});
router.route('get', '/bar', function(){});
var method = 'GET';
var url = '/foo?bar=baz';
var route = router.match(method, url, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
var route = router.match(method, url, 1);
route.path.should.equal('/foob?');
var route = router.match(method, url, 2);
assert(!route);
url = '/bar';
var route = router.match(method, url);
route.path.should.equal('/bar');
})
})
describe('.matchRequest(req, i)', function(){
it('should match based on index', function(){
router.route('get', '/foo', function(){});
router.route('get', '/foob?', function(){});
router.route('get', '/bar', function(){});
var req = { method: 'GET', url: '/foo?bar=baz' };
var route = router.match(req, 0);
var route = router.matchRequest(req, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
var route = router.match(req, 1);
var route = router.matchRequest(req, 1);
req._route_index.should.equal(1);
route.path.should.equal('/foob?');
var route = router.match(req, 2);
var route = router.matchRequest(req, 2);
assert(!route);
req.url = '/bar';
var route = router.match(req);
var route = router.matchRequest(req);
route.path.should.equal('/bar');
})
})
+10 -61
Ver Arquivo
@@ -1,8 +1,8 @@
var app = require('../../examples/auth/app')
, request = require('../support/http');
function redirects(to,fn){
return function(res){
function redirects(to, fn){
return function(err, res){
res.statusCode.should.equal(302)
res.headers.should.have.property('location').match(to);
fn()
@@ -14,80 +14,29 @@ function getCookie(res) {
}
describe('auth', function(){
var cookie;
describe('GET /',function(){
it('should redirect to /login', function(done){
request(app)
.get('/')
.end(redirects(/\/login$/,done))
.get('/')
.end(redirects(/\/login$/, done))
})
})
describe('GET /restricted (w/o cookie)',function(){
it('should redirect to /login', function(done){
request(app)
.get('/restricted')
.end(redirects(/\/login$/,done))
.get('/restricted')
.end(redirects(/\/login$/,done))
})
})
describe('POST /login', function(){
it('should fail without proper credentials', function(done){
request(app)
.post('/login')
.set('content-type','application/x-www-form-urlencoded')
.write('&username=not-tj&password=foobar')
.end(redirects(/\/login$/,done))
})
it('should authenticate', function(done){
request(app)
.post('/login')
.set('content-type', 'application/x-www-form-urlencoded')
.write('username=tj&password=foobar')
.end(function(res){
res.statusCode.should.equal(302);
cookie = getCookie(res);
request(app)
.get('/login')
.set('Cookie', cookie)
.end(function(res){
res.body.should.include('Authenticated as tj');
done();
})
})
})
})
describe('GET /restricted (w. cookie)',function(){
it('should respond with 200', function(done){
request(app)
.get('/restricted')
.set('Cookie', cookie)
.expect(200, done);
})
})
describe('GET /logout',function(){
it('should respond with 302 and clear cookie',function(done){
request(app)
.get('/logout')
.set('Cookie', cookie)
.end(function(res){
res.statusCode.should.equal(302);
res.headers.should.not.have.property('set-cookie')
done();
})
})
})
describe('GET /restricted (w. expired cookie)',function(){
it('should respond with 302',function(done){
request(app)
.get('/restricted')
.set('Cookie', cookie)
.expect(302, done)
.post('/login')
.type('urlencoded')
.send('username=not-tj&password=foobar')
.end(redirects(/\/login$/, done))
})
})
})
+4 -8
Ver Arquivo
@@ -7,20 +7,16 @@ describe('content-negotiation', function(){
it('should default to text/html', function(done){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>');
done();
})
.expect('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>')
.end(done);
})
it('should accept to text/plain', function(done){
request(app)
.get('/')
.set('Accept', 'text/plain')
.end(function(res){
res.body.should.equal(' - Tobi\n - Loki\n - Jane\n');
done();
})
.expect(' - Tobi\n - Loki\n - Jane\n')
.end(done);
})
})
})
+15 -14
Ver Arquivo
@@ -1,3 +1,4 @@
var app = require('../../examples/cookies/app')
, request = require('../support/http');
@@ -5,29 +6,29 @@ describe('cookies', function(){
describe('GET /', function(){
it('should have a form', function(done){
request(app)
.get('/')
.expect(/<form/,done)
.get('/')
.expect(/<form/, done);
})
it('should respond with no cookies', function(done){
request(app)
.get('/')
.end(function(res){
res.headers.should.not.have.property('set-cookie')
done()
})
.get('/')
.end(function(err, res){
res.headers.should.not.have.property('set-cookie')
done()
})
})
})
describe('POST /', function(){
it('should set a cookie', function(done){
request(app)
.post('/')
.set('Content-Type','application/x-www-form-urlencoded')
.write('remember=1')
.end(function(res){
res.headers.should.have.property('set-cookie')
done()
})
.post('/')
.send({ remember: 1 })
.end(function(err, res){
res.headers.should.have.property('set-cookie')
done()
})
})
})
})
+11 -10
Ver Arquivo
@@ -1,3 +1,4 @@
var app = require('../../examples/downloads/app')
, request = require('../support/http');
@@ -5,28 +6,28 @@ describe('downloads', function(){
describe('GET /', function(){
it('should have a link to amazing.txt', function(done){
request(app)
.get('/')
.expect(/href="\/files\/amazing.txt"/,done)
.get('/')
.expect(/href="\/files\/amazing.txt"/, done)
})
})
describe('GET /files/amazing.txt', function(){
it('should have a download header', function(done){
request(app)
.get('/files/amazing.txt')
.end(function(res){
res.should.have.property('statusCode',200)
res.headers.should.have.property('content-disposition', 'attachment; filename="amazing.txt"')
done()
})
.get('/files/amazing.txt')
.end(function(err, res){
res.status.should.equal(200);
res.headers.should.have.property('content-disposition', 'attachment; filename="amazing.txt"')
done()
})
})
})
describe('GET /files/missing.txt', function(){
it('should respond with 404', function(done){
request(app)
.get('/files/missing.txt')
.expect(404,done)
.get('/files/missing.txt')
.expect(404, done)
})
})
})
+4 -4
Ver Arquivo
@@ -7,12 +7,12 @@ describe('ejs', function(){
it('should respond with html', function(done){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.should.have.status(200);
res.should.have.header('Content-Type', 'text/html; charset=utf-8');
res.body.should.include('<li>tobi tobi@learnboost.com</li>');
res.body.should.include('<li>loki loki@learnboost.com</li>');
res.body.should.include('<li>jane jane@learnboost.com</li>');
res.text.should.include('<li>tobi tobi@learnboost.com</li>');
res.text.should.include('<li>loki loki@learnboost.com</li>');
res.text.should.include('<li>jane jane@learnboost.com</li>');
done();
});
})
+10 -15
Ver Arquivo
@@ -37,7 +37,6 @@ describe('error-pages', function(){
})
})
describe('Accept: application/json',function(){
describe('GET /403', function(){
it('should respond with 403', function(done){
@@ -53,9 +52,8 @@ describe('error-pages', function(){
request(app)
.get('/404')
.set('Accept','application/json')
.end(function(res){
res.should.have.property('statusCode',200)
res.should.have.property('body',JSON.stringify({error:'Not found'}))
.end(function(err, res){
res.body.should.eql({ error: 'Not found' });
done()
})
})
@@ -65,7 +63,7 @@ describe('error-pages', function(){
it('should respond with 500', function(done){
request(app)
.get('/500')
.set('Accept','application/json')
.set('Accept', 'application/json')
.expect(500, done)
})
})
@@ -76,22 +74,19 @@ describe('error-pages', function(){
describe('GET /403', function(){
it('should respond with 403', function(done){
request(app)
.get('/403')
.set('Accept','text/plain')
.expect(403, done)
.get('/403')
.set('Accept','text/plain')
.expect(403, done)
})
})
describe('GET /404', function(){
it('should respond with 404', function(done){
request(app)
.get('/404')
.set('Accept','text/plain')
.end(function(res){
res.should.have.property('statusCode',200)
res.should.have.property('body','Not found')
done()
})
.get('/404')
.set('Accept', 'text/plain')
.expect(200)
.expect('Not found', done);
})
})
-42
Ver Arquivo
@@ -1,42 +0,0 @@
var app = require('../../examples/multipart/app')
, request = require('../support/http')
, path = 'test/acceptance/fixtures/grey.png'
, fs = require('fs')
var logo = fs.readFileSync(path)
, boundary = '------expressmultipart';
describe('multipart', function(){
describe('GET /', function(){
it('should respond with a form', function(done){
request(app)
.get('/')
.expect(/<form/, done)
})
})
describe('POST /', function(){
it('should upload logo as multipart', function(done){
request(app)
.post('/')
.set('content-type','multipart/form-data; boundary='+boundary.slice(2))
.write(boundary + '\r\n')
.write('Content-Disposition: form-data; name="title"\r\n')
.write('\r\n')
.write('grey\r\n')
.write(boundary + '\r\n')
.write('Content-Disposition: form-data; name="image"; filename="grey.png"\r\n')
.write('Content-Type: image/png\r\n')
.write('\r\n')
.write(logo+'\r\n')
.write(boundary+'--\r\n')
.end(function(res){
res.body.should.match(/uploaded grey.png/)
res.body.should.match(/\(224 Kb\)/)
res.body.should.match(/as grey/)
done()
})
})
})
})
+19 -20
Ver Arquivo
@@ -7,7 +7,7 @@ describe('mvc', function(){
it('should redirect to /users', function(done){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.should.have.status(302);
res.headers.location.should.include('/users');
done();
@@ -19,11 +19,11 @@ describe('mvc', function(){
it('should display a list of users', function(done){
request(app)
.get('/users')
.end(function(res){
res.body.should.include('<h1>Users</h1>');
res.body.should.include('>TJ<');
res.body.should.include('>Guillermo<');
res.body.should.include('>Nathan<');
.end(function(err, res){
res.text.should.include('<h1>Users</h1>');
res.text.should.include('>TJ<');
res.text.should.include('>Guillermo<');
res.text.should.include('>Nathan<');
done();
})
})
@@ -34,8 +34,8 @@ describe('mvc', function(){
it('should display the user', function(done){
request(app)
.get('/user/0')
.end(function(res){
res.body.should.include('<h1>TJ <a href="/user/0/edit">edit');
.end(function(err, res){
res.text.should.include('<h1>TJ <a href="/user/0/edit">edit');
done();
})
})
@@ -43,10 +43,10 @@ describe('mvc', function(){
it('should display the users pets', function(done){
request(app)
.get('/user/0')
.end(function(res){
res.body.should.include('/pet/0">Tobi');
res.body.should.include('/pet/1">Loki');
res.body.should.include('/pet/2">Jane');
.end(function(err, res){
res.text.should.include('/pet/0">Tobi');
res.text.should.include('/pet/1">Loki');
res.text.should.include('/pet/2">Jane');
done();
})
})
@@ -65,9 +65,9 @@ describe('mvc', function(){
it('should display the edit form', function(done){
request(app)
.get('/user/1/edit')
.end(function(res){
res.body.should.include('<h1>Guillermo</h1>');
res.body.should.include('value="put"');
.end(function(err, res){
res.text.should.include('<h1>Guillermo</h1>');
res.text.should.include('value="put"');
done();
})
})
@@ -77,13 +77,12 @@ describe('mvc', function(){
it('should update the user', function(done){
request(app)
.put('/user/1')
.set('Content-Type', 'application/json')
.write('{"user":{"name":"Tobo"}}')
.end(function(res){
.send({ user: { name: 'Tobo' }})
.end(function(err, res){
request(app)
.get('/user/1/edit')
.end(function(res){
res.body.should.include('<h1>Tobo</h1>');
.end(function(err, res){
res.text.should.include('<h1>Tobo</h1>');
done();
})
})
+1 -1
Ver Arquivo
@@ -37,7 +37,7 @@ describe('resource', function(){
describe('DELETE /users/1', function(){
it('should respond with users 1 through 3', function(done){
request(app)
.delete('/users/1')
.del('/users/1')
.expect(/^destroyed/,done)
})
})
+45
Ver Arquivo
@@ -0,0 +1,45 @@
var request = require('supertest')
, app = require('../../examples/route-map');
describe('route-map', function(){
describe('GET /users', function(){
it('should respond with users', function(done){
request(app)
.get('/users')
.expect('user list', done);
})
})
describe('DELETE /users', function(){
it('should delete users', function(done){
request(app)
.del('/users')
.expect('delete users', done);
})
})
describe('GET /users/:id', function(){
it('should get a user', function(done){
request(app)
.get('/users/12')
.expect('user 12', done);
})
})
describe('GET /users/:id/pets', function(){
it('should get a users pets', function(done){
request(app)
.get('/users/12/pets')
.expect('user 12\'s pets', done);
})
})
describe('GET /users/:id/pets/:pid', function(){
it('should get a users pet', function(done){
request(app)
.del('/users/12/pets/2')
.expect('delete 12\'s pet 2', done);
})
})
})
+4 -4
Ver Arquivo
@@ -24,9 +24,9 @@ describe('web-service', function(){
it('should respond users json', function(done){
request(app)
.get('/api/users?api-key=foo')
.end(function(res){
.end(function(err, res){
res.should.be.json;
res.body.should.equal('[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]');
res.text.should.equal('[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]');
done();
});
})
@@ -37,10 +37,10 @@ describe('web-service', function(){
it('should respond with 404 json', function(done){
request(app)
.get('/api/something?api-key=bar')
.end(function(res){
.end(function(err, res){
res.should.have.status(404);
res.should.be.json;
res.body.should.equal('{"error":"Lame, can\'t find that"}');
res.text.should.equal('{"error":"Lame, can\'t find that"}');
done();
});
})
+1 -1
Ver Arquivo
@@ -29,7 +29,7 @@ describe('app.all()', function(){
});
request(app)
.delete('/tobi')
.del('/tobi')
.expect(404, done);
})
})
+1 -1
Ver Arquivo
@@ -11,7 +11,7 @@ describe('app.del()', function(){
});
request(app)
.delete('/tobi')
.del('/tobi')
.expect('deleted tobi!', done);
})
})
+2 -2
Ver Arquivo
@@ -6,10 +6,10 @@ describe('app', function(){
describe('.locals(obj)', function(){
it('should merge locals', function(){
var app = express();
Object.keys(app.locals).should.eql(['use', 'settings']);
Object.keys(app.locals).should.eql(['settings']);
app.locals({ user: 'tobi', age: 1 });
app.locals({ age: 2 });
Object.keys(app.locals).should.eql(['use', 'settings', 'user', 'age']);
Object.keys(app.locals).should.eql(['settings', 'user', 'age']);
app.locals.user.should.equal('tobi');
app.locals.age.should.equal(2);
})
-92
Ver Arquivo
@@ -1,92 +0,0 @@
var express = require('../')
, request = require('./support/http');
describe('app', function(){
describe('.locals.use(fn)', function(){
it('should run in parallel on res.render()', function(done){
var app = express();
var calls = [];
app.set('views', __dirname + '/fixtures');
app.locals.first = 'tobi';
app.locals.use(function(req, res, done){
process.nextTick(function(){
calls.push('one');
res.locals.last = 'holowaychuk';
done();
});
});
app.locals.use(function(req, res, done){
process.nextTick(function(){
calls.push('two');
res.locals.species = 'ferret';
done();
});
});
app.use(function(req, res){
calls.push('use');
res.render('pet.jade');
});
request(app)
.get('/')
.end(function(res){
calls.should.eql(['use', 'one', 'two']);
res.body.should.equal('<p>tobi holowaychuk is a ferret</p>');
done();
})
})
describe('with arity < 3', function(){
it('should done() for you', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.first = 'tobi';
app.locals.use(function(req, res){
res.locals.last = 'holowaychuk';
res.locals.species = 'ferret';
});
app.use(function(req, res){
res.render('pet.jade');
});
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi holowaychuk is a ferret</p>');
done();
})
})
})
it('should not override res.render() locals', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.first = 'tobi';
app.locals.use(function(req, res){
res.locals.last = 'holowaychuk';
res.locals.species = 'ferret';
});
app.use(function(req, res){
res.render('pet.jade', { last: 'ibot' });
});
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi ibot is a ferret</p>');
done();
})
})
})
})
+4 -11
Ver Arquivo
@@ -12,12 +12,8 @@ describe('OPTIONS', function(){
request(app)
.options('/users')
.end(function(res){
res.body.should.equal('GET,PUT');
res.headers.should.have.property('content-type');
res.headers.should.have.property('allow', 'GET,PUT');
done();
});
.expect('GET,PUT')
.expect('Allow', 'GET,PUT', done);
})
})
@@ -35,10 +31,7 @@ describe('app.options()', function(){
request(app)
.options('/users')
.end(function(res){
res.body.should.equal('GET');
res.headers.should.have.property('allow', 'GET');
done();
});
.expect('GET')
.expect('Allow', 'GET', done);
})
})
+7 -16
Ver Arquivo
@@ -29,14 +29,11 @@ describe('app', function(){
request(app)
.get('/user/tj')
.end(function(res){
res.body.should.equal('tj');
.end(function(err, res){
res.text.should.equal('tj');
request(app)
.get('/user/123')
.end(function(res){
res.should.have.status(404);
done();
});
.expect(404, done);
});
})
@@ -67,15 +64,12 @@ describe('app', function(){
request(app)
.get('/user/123')
.end(function(res){
res.body.should.equal('123');
.end(function(err, res){
res.text.should.equal('123');
request(app)
.get('/post/123')
.end(function(res){
res.body.should.equal('123');
done();
})
.expect('123', done);
})
})
})
@@ -99,10 +93,7 @@ describe('app', function(){
request(app)
.get('/user/123')
.end(function(res){
res.body.should.equal('123');
done();
})
.expect('123', done);
})
})
})
+1 -4
Ver Arquivo
@@ -17,10 +17,7 @@ describe('app', function(){
request(app)
.get('/foo?name=tobi')
.end(function(res){
res.body.should.equal('name=tobi');
done();
});
.expect('name=tobi', done);
})
})
})
+2 -8
Ver Arquivo
@@ -17,10 +17,7 @@ describe('app', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('HEY');
done();
});
.expect('HEY', done);
})
it('should not be influenced by other app protos', function(done){
@@ -41,10 +38,7 @@ describe('app', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('HEY');
done();
});
.expect('HEY', done);
})
})
})
+30 -3
Ver Arquivo
@@ -1,11 +1,14 @@
var express = require('../')
, request = require('./support/http')
, assert = require('assert');
, assert = require('assert')
, methods = require('methods');
describe('app.router', function(){
describe('methods supported', function(){
express.methods.forEach(function(method){
methods.forEach(function(method){
it('should include ' + method.toUpperCase(), function(done){
if (method == 'delete') method = 'del';
var app = express();
var calls = [];
@@ -265,7 +268,7 @@ describe('app.router', function(){
request(app)
.get('/user/10')
.end(function(res){
.end(function(err, res){
res.statusCode.should.equal(200);
request(app)
.get('/user/tj')
@@ -344,6 +347,30 @@ describe('app.router', function(){
.expect('users/0.json', done);
})
it('should not be greedy immediately after param', function(done){
var app = express();
app.get('/user/:user*', function(req, res){
res.end(req.params.user);
});
request(app)
.get('/user/122')
.expect('122', done);
})
it('should eat everything after /', function(done){
var app = express();
app.get('/user/:user*', function(req, res){
res.end(req.params.user);
});
request(app)
.get('/user/122/aaa')
.expect('122', done);
})
it('should span multiple segments', function(done){
var app = express();
+4 -7
Ver Arquivo
@@ -12,14 +12,11 @@ describe('exports', function(){
express.should.have.property('session');
express.should.have.property('static');
})
it('should expose HTTP methods', function(){
express.methods.should.be.an.instanceof(Array);
express.methods.should.include('get');
express.methods.should.include('put');
express.methods.should.include('post');
it('should expose .mime', function(){
express.mime.should.equal(require('connect').mime);
})
it('should expose Router', function(){
express.Router.should.be.a('function');
})
+2 -5
Ver Arquivo
@@ -13,10 +13,7 @@ describe('throw after .end()', function(){
request(app)
.get('/')
.end(function(res){
res.should.have.status(200);
res.body.should.equal('yay');
done();
});
.expect('yay')
.expect(200, done);
})
})
+2 -8
Ver Arquivo
@@ -15,10 +15,7 @@ describe('req', function(){
request(app)
.get('/')
.set('If-None-Match', '12345')
.end(function(res){
res.body.should.equal('true');
done();
});
.expect(304, done);
})
it('should return false when the resource is modified', function(done){
@@ -32,10 +29,7 @@ describe('req', function(){
request(app)
.get('/')
.set('If-None-Match', '12345')
.end(function(res){
res.body.should.equal('false');
done();
});
.expect('false', done);
})
})
})
+2 -8
Ver Arquivo
@@ -16,10 +16,7 @@ describe('req', function(){
request(app)
.post('/')
.set('Content-Type', 'application/json')
.end(function(res){
res.body.should.equal('application/json');
done();
});
.expect('application/json', done);
})
it('should special-case Referer', function(done){
@@ -32,10 +29,7 @@ describe('req', function(){
request(app)
.post('/')
.set('Referrer', 'http://foobar.com')
.end(function(res){
res.body.should.equal('http://foobar.com');
done();
});
.expect('http://foobar.com', done);
})
})
})
+18
Ver Arquivo
@@ -0,0 +1,18 @@
var express = require('../');
function req(ret) {
return {
get: function(){ return ret }
, __proto__: express.request
};
}
describe('req', function(){
describe('.host', function(){
it('should return hostname', function(){
req('example.com:3000').host.should.equal('example.com');
req('example.com').host.should.equal('example.com');
})
})
})
+6 -19
Ver Arquivo
@@ -13,10 +13,7 @@ describe('req', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('tj');
done();
})
.expect('tj', done);
})
})
@@ -30,10 +27,7 @@ describe('req', function(){
request(app)
.get('/?name=tj')
.end(function(res){
res.body.should.equal('tj');
done();
})
.expect('tj', done);
})
it('should check req.body', function(done){
@@ -47,27 +41,20 @@ describe('req', function(){
request(app)
.post('/')
.set('Content-Type', 'application/json')
.write('{"name":"tj"}')
.end(function(res){
res.body.should.equal('tj');
done();
})
.send({ name: 'tj' })
.expect('tj', done);
})
it('should check req.params', function(done){
var app = express();
app.get('/user/:name', function(req, res){
res.end(req.param('name'));
res.end(req.param('filter') + req.param('name'));
});
request(app)
.get('/user/tj')
.end(function(res){
res.body.should.equal('tj');
done();
})
.expect('undefinedtj', done);
})
})
})
+1 -4
Ver Arquivo
@@ -13,10 +13,7 @@ describe('req', function(){
request(app)
.get('/login?redirect=/post/1/comments')
.end(function(res){
res.body.should.equal('/login');
done();
})
.expect('/login', done);
})
})
})
+4 -16
Ver Arquivo
@@ -13,10 +13,7 @@ describe('req', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('http');
done();
})
.expect('http', done);
})
describe('when "trust proxy" is enabled', function(){
@@ -32,10 +29,7 @@ describe('req', function(){
request(app)
.get('/')
.set('X-Forwarded-Proto', 'https')
.end(function(res){
res.body.should.equal('https');
done();
})
.expect('https', done);
})
it('should default to http', function(done){
@@ -49,10 +43,7 @@ describe('req', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('http');
done();
})
.expect('http', done);
})
})
@@ -67,10 +58,7 @@ describe('req', function(){
request(app)
.get('/')
.set('X-Forwarded-Proto', 'https')
.end(function(res){
res.body.should.equal('http');
done();
})
.expect('http', done);
})
})
})
+31
Ver Arquivo
@@ -0,0 +1,31 @@
var express = require('../');
function req(ret) {
return {
get: function(){ return ret }
, __proto__: express.request
};
}
describe('req', function(){
describe('.range(size)', function(){
it('should return parsed ranges', function(){
var ret = [{ start: 0, end: 50 }, { start: 60, end: 100 }];
ret.type = 'bytes';
req('bytes=0-50,60-100').range(120).should.eql(ret);
})
it('should cap to the given size', function(){
var ret = [{ start: 0, end: 74 }];
ret.type = 'bytes';
req('bytes=0-100').range(75).should.eql(ret);
})
it('should have a .type', function(){
var ret = [{ start: 0, end: Infinity }];
ret.type = 'users';
req('users=0-').range(Infinity).should.eql(ret);
})
})
})
+2 -8
Ver Arquivo
@@ -15,10 +15,7 @@ describe('req', function(){
request(app)
.get('/')
.set('If-None-Match', '12345')
.end(function(res){
res.body.should.equal('false');
done();
});
.expect(304, done);
})
it('should return true when the resource is modified', function(done){
@@ -32,10 +29,7 @@ describe('req', function(){
request(app)
.get('/')
.set('If-None-Match', '12345')
.end(function(res){
res.body.should.equal('true');
done();
});
.expect('true', done);
})
})
})
+2 -8
Ver Arquivo
@@ -15,10 +15,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.example.com')
.end(function(res){
res.body.should.equal('["ferrets","tobi"]');
done();
})
.expect('["ferrets","tobi"]', done);
})
})
@@ -33,10 +30,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'example.com')
.end(function(res){
res.body.should.equal('[]');
done();
})
.expect('[]', done);
})
})
})
+3 -12
Ver Arquivo
@@ -13,10 +13,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-disposition', 'attachment');
done();
})
.expect('Content-Disposition', 'attachment', done);
})
})
@@ -31,10 +28,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-disposition', 'attachment; filename="image.png"');
done();
})
.expect('Content-Disposition', 'attachment; filename="image.png"', done);
})
it('should set the Content-Type', function(done){
@@ -47,10 +41,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-type', 'image/png');
done();
})
.expect('Content-Type', 'image/png', done);
})
})
})
+2 -8
Ver Arquivo
@@ -15,10 +15,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('text/x-foo; charset=utf-8');
done();
})
.expect("text/x-foo; charset=utf-8", done);
})
it('should take precedence over res.send() defaults', function(done){
@@ -31,10 +28,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-type', 'text/html; charset=whoop');
done();
})
.expect('Content-Type', 'text/html; charset=whoop', done);
})
})
})
+6 -6
Ver Arquivo
@@ -13,9 +13,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
var val = 'sid=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT';
res.headers['set-cookie'].should.eql([val]);
.end(function(err, res){
var val = 'sid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT';
res.header['set-cookie'].should.eql([val]);
done();
})
})
@@ -31,9 +31,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
var val = 'sid=; path=/admin; expires=Thu, 01 Jan 1970 00:00:00 GMT';
res.headers['set-cookie'].should.eql([val]);
.end(function(err, res){
var val = 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT';
res.header['set-cookie'].should.eql([val]);
done();
})
})
+16 -15
Ver Arquivo
@@ -1,6 +1,7 @@
var express = require('../')
, request = require('./support/http');
, request = require('./support/http')
, cookie = require('cookie');
describe('res', function(){
describe('.cookie(name, object)', function(){
@@ -13,8 +14,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
var val = ['user=j%3A%7B%22name%22%3A%22tobi%22%7D; path=/'];
.end(function(err, res){
var val = ['user=' + encodeURIComponent('j:{"name":"tobi"}') + '; Path=/'];
res.headers['set-cookie'].should.eql(val);
done();
})
@@ -31,8 +32,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
var val = ['name=tobi; path=/'];
.end(function(err, res){
var val = ['name=tobi; Path=/'];
res.headers['set-cookie'].should.eql(val);
done();
})
@@ -49,8 +50,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
var val = ['name=tobi; path=/', 'age=1; path=/'];
.end(function(err, res){
var val = ['name=tobi; Path=/', 'age=1; Path=/'];
res.headers['set-cookie'].should.eql(val);
done();
})
@@ -68,8 +69,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
var val = ['name=tobi; path=/; httpOnly; secure'];
.end(function(err, res){
var val = ['name=tobi; Path=/; HttpOnly; Secure'];
res.headers['set-cookie'].should.eql(val);
done();
})
@@ -86,7 +87,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers['set-cookie'][0].should.not.include('Thu, 01 Jan 1970 00:00:01 GMT');
done();
})
@@ -105,10 +106,10 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
var val = res.headers['set-cookie'][0];
val = decodeURIComponent(val.split('.')[0]);
val.should.equal('user=j:{"name":"tobi"}');
val = cookie.parse(val.split('.')[0]);
val.user.should.equal('j:{"name":"tobi"}');
done();
})
})
@@ -126,8 +127,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
var val = ['name=tobi.xJjV2iZ6EI7C8E5kzwbfA9PVLl1ZR07UTnuTgQQ4EnQ; path=/'];
.end(function(err, res){
var val = ['name=tobi.xJjV2iZ6EI7C8E5kzwbfA9PVLl1ZR07UTnuTgQQ4EnQ; Path=/'];
res.headers['set-cookie'].should.eql(val);
done();
})
+44 -5
Ver Arquivo
@@ -14,10 +14,10 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.should.have.header('Content-Type', 'text/html; charset=UTF-8');
res.should.have.header('Content-Disposition', 'attachment; filename="user.html"');
res.body.should.equal('<p>{{user.name}}</p>');
res.text.should.equal('<p>{{user.name}}</p>');
done();
});
})
@@ -33,7 +33,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.should.have.header('Content-Type', 'text/html; charset=UTF-8');
res.should.have.header('Content-Disposition', 'attachment; filename="document"');
done();
@@ -52,7 +52,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.should.have.header('Content-Type', 'text/html; charset=UTF-8');
res.should.have.header('Content-Disposition', 'attachment; filename="user.html"');
});
@@ -70,10 +70,49 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.should.have.header('Content-Type', 'text/html; charset=UTF-8');
res.should.have.header('Content-Disposition', 'attachment; filename="document"');
});
})
})
describe('on failure', function(){
it('should invoke the callback', function(done){
var app = express()
, calls = 0;
app.use(function(req, res){
res.download('test/fixtures/foobar.html', function(err){
assert(404 == err.status);
assert('ENOENT' == err.code);
done();
});
});
request(app)
.get('/')
.end(function(){});
})
it('should remove Content-Disposition', function(done){
var app = express()
, calls = 0;
app.use(function(req, res){
res.download('test/fixtures/foobar.html', function(err){
res.end('failed');
});
});
request(app)
.get('/')
.expect('failed')
.end(function(err, res){
if (err) return done(err);
res.header.should.not.have.property('content-disposition');
done();
});
})
})
})
+23 -14
Ver Arquivo
@@ -44,6 +44,15 @@ app2.use(function(err, req, res, next){
res.send(err.status, 'Supports: ' + err.types.join(', '));
})
var app3 = express();
app3.use(function(req, res, next){
res.format({
text: function(){ res.send('hey') },
default: function(){ res.send('default') }
})
});
describe('req', function(){
describe('.format(obj)', function(){
describe('with canonicalized mime types', function(){
@@ -53,6 +62,15 @@ describe('req', function(){
describe('with extnames', function(){
test(app2);
})
describe('given .default', function(){
it('should be invoked instead of auto-responding', function(done){
request(app3)
.get('/')
.set('Accept: text/html')
.expect('default', done);
})
})
})
})
@@ -75,21 +93,15 @@ function test(app) {
request(app)
.get('/')
.set('Accept', 'text/html; q=.5, text/plain')
.end(function(res){
res.headers['content-type'].should.equal('text/plain');
res.body.should.equal('hey');
done();
});
.expect('Content-Type', 'text/plain')
.expect('hey', done);
})
it('should Vary: Accept', function(done){
request(app)
.get('/')
.set('Accept', 'text/html; q=.5, text/plain')
.end(function(res){
res.headers.vary.should.equal('Accept');
done();
});
.expect('Vary', 'Accept', done);
})
describe('when Accept is not present', function(){
@@ -105,11 +117,8 @@ function test(app) {
request(app)
.get('/')
.set('Accept', 'foo/bar')
.end(function(res){
res.should.have.status(406);
res.body.should.equal('Supports: text/plain, text/html, application/json');
done();
});
.expect('Supports: text/plain, text/html, application/json')
.expect(406, done)
})
})
}
+52 -16
Ver Arquivo
@@ -15,13 +15,30 @@ describe('res', function(){
request(app)
.get('/?callback=something')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.body.should.equal('something({"count":1});');
res.text.should.equal('something({"count":1});');
done();
})
})
it('should allow renaming callback', function(done){
var app = express();
app.set('jsonp callback name', 'clb');
app.use(function(req, res){
res.json({ count: 1 });
});
request(app)
.get('/?clb=something')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('something({"count":1});');
done();
})
})
it('should allow []', function(done){
var app = express();
@@ -31,9 +48,9 @@ describe('res', function(){
request(app)
.get('/?callback=callbacks[123]')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.body.should.equal('callbacks[123]({"count":1});');
res.text.should.equal('callbacks[123]({"count":1});');
done();
})
})
@@ -49,9 +66,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.body.should.equal('null');
res.text.should.equal('null');
done();
})
})
@@ -67,9 +84,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.body.should.equal('["foo","bar","baz"]');
res.text.should.equal('["foo","bar","baz"]');
done();
})
})
@@ -85,9 +102,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.body.should.equal('{"name":"tobi"}');
res.text.should.equal('{"name":"tobi"}');
done();
})
})
@@ -109,8 +126,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('{"name":"tobi"}');
.end(function(err, res){
res.text.should.equal('{"name":"tobi"}');
done();
});
})
@@ -140,8 +157,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('{\n "name": "tobi",\n "age": 2\n}');
.end(function(err, res){
res.text.should.equal('{\n "name": "tobi",\n "age": 2\n}');
done();
});
})
@@ -158,10 +175,29 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.body.should.equal('{"id":1}');
res.text.should.equal('{"id":1}');
done();
})
})
})
describe('.json(object, status)', function(){
it('should respond with json and set the .statusCode for backwards compat', function(done){
var app = express();
app.use(function(req, res){
res.json({ id: 1 }, 201);
});
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('{"id":1}');
done();
})
})
+19
Ver Arquivo
@@ -0,0 +1,19 @@
var express = require('../')
, res = express.response;
describe('res', function(){
describe('.links(obj)', function(){
it('should set Link header field', function(){
res.links({
next: 'http://api.example.com/users?page=2',
last: 'http://api.example.com/users?page=5'
});
res.get('link')
.should.equal(
'<http://api.example.com/users?page=2>; rel="next", '
+ '<http://api.example.com/users?page=5>; rel="last"');
})
})
})
+22 -3
Ver Arquivo
@@ -17,9 +17,28 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
done();
})
.expect(200, done);
})
})
it('should work when mounted', function(done){
var app = express();
var blog = express();
app.use(blog);
blog.use(function(req, res, next){
res.locals.foo = 'bar';
next();
});
app.use(function(req, res){
res.locals.foo.should.equal('bar');
res.end();
});
request(app)
.get('/')
.expect(200, done);
})
})
-104
Ver Arquivo
@@ -1,104 +0,0 @@
var express = require('../')
, request = require('./support/http');
describe('res', function(){
describe('.locals.use(fn)', function(){
it('should run in parallel on res.render()', function(done){
var app = express();
var calls = [];
app.set('views', __dirname + '/fixtures');
app.locals.first = 'tobi';
app.use(function(req, res, next){
res.locals.use(function(req, res, done){
process.nextTick(function(){
calls.push('one');
res.locals.last = 'holowaychuk';
done();
});
});
next();
});
app.use(function(req, res, next){
res.locals.use(function(req, res, done){
process.nextTick(function(){
calls.push('two');
res.locals.species = 'ferret';
done();
});
});
next();
});
app.use(function(req, res){
calls.push('render');
res.render('pet.jade');
});
request(app)
.get('/')
.end(function(res){
calls.should.eql(['render', 'one', 'two']);
res.body.should.equal('<p>tobi holowaychuk is a ferret</p>');
done();
})
})
describe('with arity < 3', function(){
it('should done() for you', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.first = 'tobi';
app.use(function(req, res, next){
res.locals.use(function(req, res){
res.locals.last = 'holowaychuk';
res.locals.species = 'ferret';
});
next();
});
app.use(function(req, res){
res.render('pet.jade');
});
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi holowaychuk is a ferret</p>');
done();
})
})
})
it('should not override res.render() locals', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.first = 'tobi';
app.use(function(req, res, next){
res.locals.use(function(req, res){
res.locals.last = 'holowaychuk';
res.locals.species = 'ferret';
});
next();
});
app.use(function(req, res){
res.render('pet.jade', { last: 'ibot' });
});
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi ibot is a ferret</p>');
done();
})
})
})
})
+61 -45
Ver Arquivo
@@ -4,26 +4,6 @@ var express = require('../')
describe('res', function(){
describe('.redirect(url)', function(){
it('should respect X-Forwarded-Proto when "trust proxy" is enabled', function(done){
var app = express();
app.enable('trust proxy');
app.use(function(req, res){
res.redirect('/login');
});
request(app)
.get('/')
.set('Host', 'example.com')
.set('X-Forwarded-Proto', 'https')
.end(function(res){
res.statusCode.should.equal(302);
res.headers.should.have.property('location', 'https://example.com/login');
done();
})
})
it('should default to a 302 redirect', function(done){
var app = express();
@@ -33,15 +13,34 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.statusCode.should.equal(302);
res.headers.should.have.property('location', 'http://google.com');
done();
})
})
describe('with leading //', function(){
it('should pass through scheme-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.redirect('//cuteoverload.com');
});
request(app)
.get('/')
.set('Host', 'example.com')
.end(function(err, res){
res.headers.should.have.property('location', '//cuteoverload.com');
done();
})
})
})
describe('with leading /', function(){
it('should construct host-relative urls', function(done){
it('should construct scheme-relative urls', function(done){
var app = express();
app.use(function(req, res){
@@ -51,8 +50,8 @@ describe('res', function(){
request(app)
.get('/')
.set('Host', 'example.com')
.end(function(res){
res.headers.should.have.property('location', 'http://example.com/login');
.end(function(err, res){
res.headers.should.have.property('location', '//example.com/login');
done();
})
})
@@ -69,8 +68,8 @@ describe('res', function(){
request(app)
.get('/post/1')
.set('Host', 'example.com')
.end(function(res){
res.headers.should.have.property('location', 'http://example.com/post/1/./edit');
.end(function(err, res){
res.headers.should.have.property('location', '//example.com/post/1/./edit');
done();
})
})
@@ -87,8 +86,8 @@ describe('res', function(){
request(app)
.get('/post/1')
.set('Host', 'example.com')
.end(function(res){
res.headers.should.have.property('location', 'http://example.com/post/1/../new');
.end(function(err, res){
res.headers.should.have.property('location', '//example.com/post/1/../new');
done();
})
})
@@ -105,8 +104,8 @@ describe('res', function(){
request(app)
.get('/')
.set('Host', 'example.com')
.end(function(res){
res.headers.should.have.property('location', 'http://example.com/login');
.end(function(err, res){
res.headers.should.have.property('location', '//example.com/login');
done();
})
})
@@ -129,8 +128,8 @@ describe('res', function(){
request(app)
.get('/blog/admin')
.set('Host', 'example.com')
.end(function(res){
res.headers.should.have.property('location', 'http://example.com/blog/admin/login');
.end(function(err, res){
res.headers.should.have.property('location', '//example.com/blog/admin/login');
done();
})
})
@@ -150,8 +149,8 @@ describe('res', function(){
request(app)
.get('/blog')
.set('Host', 'example.com')
.end(function(res){
res.headers.should.have.property('location', 'http://example.com/blog/admin/login');
.end(function(err, res){
res.headers.should.have.property('location', '//example.com/blog/admin/login');
done();
})
})
@@ -171,8 +170,8 @@ describe('res', function(){
request(app)
.get('/blog')
.set('Host', 'example.com')
.end(function(res){
res.headers.should.have.property('location', 'http://example.com/admin/login');
.end(function(err, res){
res.headers.should.have.property('location', '//example.com/admin/login');
done();
})
})
@@ -190,7 +189,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.statusCode.should.equal(303);
res.headers.should.have.property('location', 'http://google.com');
done();
@@ -208,9 +207,9 @@ describe('res', function(){
request(app)
.head('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('location', 'http://google.com');
res.body.should.equal('');
res.text.should.equal('');
done();
})
})
@@ -227,9 +226,26 @@ describe('res', function(){
request(app)
.get('/')
.set('Accept', 'text/html')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('location', 'http://google.com');
res.body.should.equal('<p>Moved Temporarily. Redirecting to <a href="http://google.com">http://google.com</a></p>');
res.text.should.equal('<p>Moved Temporarily. Redirecting to <a href="http://google.com">http://google.com</a></p>');
done();
})
})
it('should escape the url', function(done){
var app = express();
app.use(function(req, res){
res.redirect('<lame>');
});
request(app)
.get('/')
.set('Host', 'http://example.com')
.set('Accept', 'text/html')
.end(function(err, res){
res.text.should.equal('<p>Moved Temporarily. Redirecting to <a href="//http://example.com/&lt;lame&gt;">//http://example.com/&lt;lame&gt;</a></p>');
done();
})
})
@@ -246,10 +262,10 @@ describe('res', function(){
request(app)
.get('/')
.set('Accept', 'text/plain, */*')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('location', 'http://google.com');
res.headers.should.have.property('content-length', '51');
res.body.should.equal('Moved Temporarily. Redirecting to http://google.com');
res.text.should.equal('Moved Temporarily. Redirecting to http://google.com');
done();
})
})
@@ -266,12 +282,12 @@ describe('res', function(){
request(app)
.get('/')
.set('Accept', 'foo/bar')
.end(function(res){
.end(function(err, res){
res.should.have.status(302);
res.headers.should.have.property('location', 'http://google.com');
res.headers.should.not.have.property('content-type');
res.headers.should.have.property('content-length', '0');
res.body.should.equal('');
res.text.should.equal('');
done();
})
})
+15 -61
Ver Arquivo
@@ -15,10 +15,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi</p>');
done();
});
.expect('<p>tobi</p>', done);
})
it('should support absolute paths with "view engine"', function(done){
@@ -33,10 +30,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi</p>');
done();
});
.expect('<p>tobi</p>', done);
})
it('should expose app.locals', function(done){
@@ -51,10 +45,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi</p>');
done();
});
.expect('<p>tobi</p>', done);
})
it('should support index.<engine>', function(done){
@@ -69,10 +60,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<h1>blog post</h1>');
done();
});
.expect('<h1>blog post</h1>', done);
})
describe('when an error occurs', function(){
@@ -91,10 +79,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.match(/user is not defined/);
done();
});
.expect(/user is not defined/, done);
})
})
@@ -111,10 +96,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>This is an email</p>');
done();
});
.expect('<p>This is an email</p>', done);
})
})
})
@@ -133,10 +115,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi</p>');
done();
});
.expect('<p>tobi</p>', done);
})
it('should expose app.locals', function(done){
@@ -151,10 +130,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi</p>');
done();
});
.expect('<p>tobi</p>', done);
})
it('should expose res.locals', function(done){
@@ -169,10 +145,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>tobi</p>');
done();
});
.expect('<p>tobi</p>', done);
})
it('should give precedence to res.locals over app.locals', function(done){
@@ -188,10 +161,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>jane</p>');
done();
});
.expect('<p>jane</p>', done);
})
it('should give precedence to res.render() locals over res.locals', function(done){
@@ -207,10 +177,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>jane</p>');
done();
});
.expect('<p>jane</p>', done);
})
it('should give precedence to res.render() locals over app.locals', function(done){
@@ -226,10 +193,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>jane</p>');
done();
});
.expect('<p>jane</p>', done);
})
})
@@ -249,10 +213,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>loki</p>');
done();
});
.expect('<p>loki</p>', done);
})
})
@@ -272,10 +233,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>loki</p>');
done();
});
.expect('<p>loki</p>', done);
})
describe('when an error occurs', function(){
@@ -292,12 +250,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.match(/is not defined/);
done();
});
.expect(/is not defined/, done);
})
})
})
})
+76 -53
Ver Arquivo
@@ -13,10 +13,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('');
done();
})
.expect('', done);
})
})
@@ -30,10 +27,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('');
done();
})
.expect('', done);
})
})
@@ -47,11 +41,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('Created');
res.statusCode.should.equal(201);
done();
})
.expect('Created')
.expect(201, done);
})
})
@@ -65,14 +56,26 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('Created :)');
res.statusCode.should.equal(201);
done();
})
.expect('Created :)')
.expect(201, done);
})
})
describe('.send(body, code)', function(){
it('should be supported for backwards compat', function(done){
var app = express();
app.use(function(req, res){
res.send('Bad!', 400);
});
request(app)
.get('/')
.expect('Bad!')
.expect(400, done);
})
})
describe('.send(String)', function(){
it('should send as html', function(done){
var app = express();
@@ -83,9 +86,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/html; charset=utf-8');
res.body.should.equal('<p>hey</p>');
res.text.should.equal('<p>hey</p>');
res.statusCode.should.equal(200);
done();
})
@@ -101,10 +104,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('etag', '-1498647312');
done();
})
.expect('ETag', '-1498647312')
.end(done);
})
it('should not override Content-Type', function(done){
@@ -116,12 +117,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-type', 'text/plain');
res.body.should.equal('hey');
res.statusCode.should.equal(200);
done();
})
.expect('Content-Type', 'text/plain')
.expect('hey')
.expect(200, done);
})
})
@@ -135,9 +133,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/html; charset=utf-8');
res.body.should.equal('<p>hey</p>');
res.text.should.equal('<p>hey</p>');
res.statusCode.should.equal(200);
done();
})
@@ -152,9 +150,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/plain');
res.body.should.equal('hey');
res.text.should.equal('hey');
res.statusCode.should.equal(200);
done();
})
@@ -171,9 +169,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/octet-stream');
res.body.should.equal('hello');
res.text.should.equal('hello');
res.statusCode.should.equal(200);
done();
})
@@ -189,10 +187,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('etag', '-1498647312');
done();
})
.expect('ETag', '-1498647312')
.end(done);
})
it('should not override Content-Type', function(done){
@@ -204,9 +200,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/plain');
res.body.should.equal('hey');
res.text.should.equal('hey');
res.statusCode.should.equal(200);
done();
})
@@ -223,9 +219,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.body.should.equal('{"name":"tobi"}');
res.text.should.equal('{"name":"tobi"}');
done();
})
})
@@ -241,10 +237,7 @@ describe('res', function(){
request(app)
.head('/')
.end(function(res){
res.body.should.equal('');
done();
})
.expect('', done);
})
})
@@ -258,10 +251,10 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.not.have.property('content-type');
res.headers.should.not.have.property('content-length');
res.body.should.equal('');
res.text.should.equal('');
done();
})
})
@@ -277,15 +270,29 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.headers.should.not.have.property('content-type');
res.headers.should.not.have.property('content-length');
res.body.should.equal('');
res.text.should.equal('');
done();
})
})
})
it('should always check regardless of length', function(done){
var app = express();
app.use(function(req, res, next){
res.set('ETag', 'asdf');
res.send('hey');
});
request(app)
.get('/')
.set('If-None-Match', 'asdf')
.expect(304, done);
})
it('should respond with 304 Not Modified when fresh', function(done){
var app = express();
@@ -299,4 +306,20 @@ describe('res', function(){
.set('If-None-Match', '-1498647312')
.expect(304, done);
})
it('should not perform freshness check unless 2xx or 304', function(done){
var app = express();
app.use(function(req, res, next){
res.status(500);
res.set('ETag', 'asdf');
res.send('hey');
});
request(app)
.get('/')
.set('If-None-Match', 'asdf')
.expect('hey')
.expect(500, done);
})
})
+51 -53
Ver Arquivo
@@ -6,23 +6,20 @@ var express = require('../')
describe('res', function(){
describe('.sendfile(path, fn)', function(){
it('should invoke the callback when complete', function(done){
var app = express()
, calls = 0;
var app = express();
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', function(err){
assert(!err);
++calls;
req.socket.listeners('error').should.have.length(1); // node's original handler
done();
});
});
request(app)
.get('/')
.end(function(res){
calls.should.equal(1);
res.statusCode.should.equal(200);
done();
});
.expect(200)
.end(function(){});
})
it('should utilize the same options as express.static()', function(done){
@@ -34,10 +31,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.should.have.header('Cache-Control', 'public, max-age=60');
done();
});
.expect('Cache-Control', 'public, max-age=60')
.end(done);
})
it('should invoke the callback on 404', function(done){
@@ -46,17 +41,17 @@ describe('res', function(){
app.use(function(req, res){
res.sendfile('test/fixtures/nope.html', function(err){
assert(!res.headerSent);
++calls;
assert(!res.headerSent);
res.send(err.message);
});
});
request(app)
.get('/')
.end(function(res){
calls.should.equal(1);
res.body.should.equal('Not Found');
.end(function(err, res){
assert(1 == calls, 'called too many times');
res.text.should.equal("ENOENT, stat 'test/fixtures/nope.html'");
res.statusCode.should.equal(200);
done();
});
@@ -72,10 +67,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.should.have.header('content-type', 'text/plain');
done();
});
.expect('Content-Type', 'text/plain')
.end(done);
})
it('should invoke the callback on 403', function(done){
@@ -92,12 +85,27 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('Forbidden');
res.statusCode.should.equal(200);
calls.should.equal(1);
done();
.expect('Forbidden')
.expect(200, done);
})
it('should invoke the callback on socket error', function(done){
var app = express()
, calls = 0;
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', function(err){
assert(!res.headerSent);
req.socket.listeners('error').should.have.length(1); // node's original handler
done();
});
req.socket.emit('error', new Error('broken!'));
});
request(app)
.get('/')
.end(function(){});
})
})
@@ -112,8 +120,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>{{user.name}}</p>');
.end(function(err, res){
res.text.should.equal('<p>{{user.name}}</p>');
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
done();
});
@@ -130,8 +138,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>{{user.name}}</p>');
.end(function(err, res){
res.text.should.equal('<p>{{user.name}}</p>');
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
done();
});
@@ -146,8 +154,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('<p>{{user.name}}</p>');
.end(function(err, res){
res.text.should.equal('<p>{{user.name}}</p>');
res.headers.should.have.property('content-type', 'text/html; charset=UTF-8');
done();
});
@@ -162,10 +170,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.statusCode.should.equal(403);
done();
});
.expect(403, done);
})
it('should allow ../ when "root" is set', function(done){
@@ -177,10 +182,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.statusCode.should.equal(200);
done();
});
.expect(200, done);
})
it('should disallow requesting out of "root"', function(done){
@@ -192,10 +194,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.statusCode.should.equal(403);
done();
});
.expect(403, done);
})
it('should next(404) when not found', function(done){
@@ -217,7 +216,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
.end(function(err, res){
res.statusCode.should.equal(404);
calls.should.equal(1);
done();
@@ -226,17 +225,16 @@ describe('res', function(){
describe('with non-GET', function(){
it('should still serve', function(done){
var app = express()
, calls = 0;
var app = express()
, calls = 0;
app.use(function(req, res){
res.sendfile(__dirname + '/fixtures/name.txt');
});
app.use(function(req, res){
res.sendfile(__dirname + '/fixtures/name.txt');
});
request(app)
.get('/')
.expect('tobi', done);
request(app)
.get('/')
.expect('tobi', done);
})
})
})
+5 -9
Ver Arquivo
@@ -14,10 +14,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-type', 'text/x-foo');
done();
})
.expect('Content-Type', 'text/x-foo')
.end(done);
})
it('should coerce to a string', function(){
@@ -40,11 +38,9 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('x-foo', 'bar');
res.headers.should.have.property('x-bar', 'baz');
done();
})
.expect('X-Foo', 'bar')
.expect('X-Bar', 'baz')
.end(done);
})
it('should coerce to a string', function(){
+2 -5
Ver Arquivo
@@ -13,11 +13,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.body.should.equal('Created');
res.statusCode.should.equal(201);
done();
})
.expect('Created')
.expect(201, done);
})
})
})
+3 -12
Ver Arquivo
@@ -13,10 +13,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-type', 'application/javascript');
done();
})
.expect('Content-Type', 'application/javascript', done);
})
it('should default to application/octet-stream', function(done){
@@ -28,10 +25,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-type', 'application/octet-stream');
done();
})
.expect('Content-Type', 'application/octet-stream', done);
})
it('should set the Content-Type with type/subtype', function(done){
@@ -44,10 +38,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(res){
res.headers.should.have.property('content-type', 'application/vnd.amazon.ebook');
done();
})
.expect('Content-Type', 'application/vnd.amazon.ebook', done);
})
})
})
+1 -104
Ver Arquivo
@@ -1,105 +1,2 @@
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
, methods = require('../../').methods
, http = require('http');
module.exports = request;
function request(app) {
return new Request(app);
}
function Request(app) {
var self = this;
this.data = [];
this.header = {};
this.app = app;
if (!this.server) {
this.server = http.Server(app);
this.server.listen(0, function(){
self.addr = self.server.address();
self.listening = true;
});
}
}
/**
* Inherit from `EventEmitter.prototype`.
*/
Request.prototype.__proto__ = EventEmitter.prototype;
methods.forEach(function(method){
Request.prototype[method] = function(path){
return this.request(method, path);
};
});
Request.prototype.set = function(field, val){
this.header[field] = val;
return this;
};
Request.prototype.write = function(data){
this.data.push(data);
return this;
};
Request.prototype.request = function(method, path){
this.method = method;
this.path = path;
return this;
};
Request.prototype.expect = function(body, fn){
this.end(function(res){
if ('number' == typeof body) {
res.statusCode.should.equal(body);
} else if (body instanceof RegExp) {
res.body.should.match(body);
} else {
res.body.should.equal(body);
}
fn();
});
};
Request.prototype.end = function(fn){
var self = this;
if (this.listening) {
var req = http.request({
method: this.method
, port: this.addr.port
, host: this.addr.address
, path: this.path
, headers: this.header
});
this.data.forEach(function(chunk){
req.write(chunk);
});
req.on('response', function(res){
var buf = '';
res.setEncoding('utf8');
res.on('data', function(chunk){ buf += chunk });
res.on('end', function(){
res.body = buf;
fn(res);
});
});
req.end();
} else {
this.server.on('listening', function(){
self.end(fn);
});
}
return this;
};
module.exports = require('supertest');