mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
94 Commits
3.0.0beta3
...
3.0.0rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20e8f08cb2 | ||
|
|
bac0c64633 | ||
|
|
48923055eb | ||
|
|
0f20a5e06a | ||
|
|
56bfb9249f | ||
|
|
5ed1544cab | ||
|
|
e5c7be9364 | ||
|
|
73ce9d028c | ||
|
|
75debbe5bc | ||
|
|
5f33d89ea5 | ||
|
|
42fd29efe8 | ||
|
|
2d91eac811 | ||
|
|
a50f02e87d | ||
|
|
214f913b0c | ||
|
|
1021c86300 | ||
|
|
386516815a | ||
|
|
d5e5647bba | ||
|
|
a861ea7eaf | ||
|
|
cb844132e6 | ||
|
|
8050308706 | ||
|
|
e79f72bf88 | ||
|
|
07b6c9f563 | ||
|
|
8f4e61a474 | ||
|
|
54d37c60f5 | ||
|
|
a93d375acc | ||
|
|
d66f0e5eb9 | ||
|
|
e84db12783 | ||
|
|
1ad2ecefe8 | ||
|
|
08b68ec8cd | ||
|
|
48be9233d8 | ||
|
|
a512d9b47d | ||
|
|
62234cc106 | ||
|
|
ab61837885 | ||
|
|
7a5041bf9c | ||
|
|
4d219135b2 | ||
|
|
26eeb64640 | ||
|
|
5426eb0b62 | ||
|
|
b9e32ec2c4 | ||
|
|
32b8613708 | ||
|
|
90eddb3439 | ||
|
|
accd6180c1 | ||
|
|
e770b674ff | ||
|
|
4f7c4d1051 | ||
|
|
78845e7d23 | ||
|
|
93f1cecc92 | ||
|
|
8ccd89f6ad | ||
|
|
0a210cce7a | ||
|
|
f110248462 | ||
|
|
96e4014a70 | ||
|
|
2173d00829 | ||
|
|
3827f5ef8b | ||
|
|
73bed61afa | ||
|
|
ee89ff5026 | ||
|
|
8df9e745d5 | ||
|
|
c76e954504 | ||
|
|
136f054614 | ||
|
|
e9a4b4f8ff | ||
|
|
5415c98ff9 | ||
|
|
39efa452fc | ||
|
|
bddcdee3fe | ||
|
|
2e8d44b444 | ||
|
|
70d68419b0 | ||
|
|
26fab2a27d | ||
|
|
0f5560eebd | ||
|
|
170dcc2907 | ||
|
|
18d6c78ef4 | ||
|
|
1248f0338b | ||
|
|
b400814d00 | ||
|
|
a934929bb3 | ||
|
|
140efb574c | ||
|
|
3c0c96114f | ||
|
|
362c96c8c1 | ||
|
|
5db3d0f9fc | ||
|
|
80c814c393 | ||
|
|
bf66465937 | ||
|
|
72eea7e6cd | ||
|
|
8f4cd13c89 | ||
|
|
6556cefc71 | ||
|
|
7bfb58920a | ||
|
|
2e324ccf5f | ||
|
|
1a10ee76b3 | ||
|
|
619e6349f6 | ||
|
|
b1ff68548f | ||
|
|
376b6c3bad | ||
|
|
f4c8a59b17 | ||
|
|
e6129d8ba5 | ||
|
|
d2baf11b8a | ||
|
|
82b5b12ca7 | ||
|
|
c145ab9b81 | ||
|
|
c10223b803 | ||
|
|
1e09b54ad2 | ||
|
|
d073e0aeb5 | ||
|
|
ce7293de13 | ||
|
|
108e66c24b |
58
History.md
58
History.md
@@ -1,4 +1,62 @@
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
|
||||
19
Makefile
19
Makefile
@@ -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
|
||||
|
||||
13
bin/express
13
bin/express
@@ -19,7 +19,7 @@ program
|
||||
.option('-s, --sessions', 'add session support')
|
||||
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
|
||||
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
|
||||
.option('-h, --hjs', 'add hogan.js engine support')
|
||||
.option('-H, --hogan', 'add hogan.js engine support')
|
||||
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
|
||||
.option('-f, --force', 'force on non-empty directory')
|
||||
.parse(process.argv);
|
||||
@@ -37,7 +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.hjs) program.template = 'hjs';
|
||||
if (program.hogan) program.template = 'hjs';
|
||||
|
||||
/**
|
||||
* Routes index template.
|
||||
@@ -127,7 +127,7 @@ var jshtmlIndex = [
|
||||
/**
|
||||
* Hogan.js index template.
|
||||
*/
|
||||
var hjsIndex = [
|
||||
var hoganIndex = [
|
||||
'<!DOCTYPE html>'
|
||||
, '<html>'
|
||||
, ' <head>'
|
||||
@@ -195,7 +195,8 @@ var app = [
|
||||
, ''
|
||||
, 'var express = require(\'express\')'
|
||||
, ' , routes = require(\'./routes\')'
|
||||
, ' , http = require(\'http\');'
|
||||
, ' , http = require(\'http\')'
|
||||
, ' , path = require(\'path\');'
|
||||
, ''
|
||||
, 'var app = express();'
|
||||
, ''
|
||||
@@ -208,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(){'
|
||||
@@ -295,7 +296,7 @@ function createApplicationAt(path) {
|
||||
write(path + '/views/index.jshtml', jshtmlIndex);
|
||||
break;
|
||||
case 'hjs':
|
||||
write(path + '/views/index.hjs', hjsIndex);
|
||||
write(path + '/views/index.hjs', hoganIndex);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
506
docs/express.md
506
docs/express.md
@@ -1,506 +0,0 @@
|
||||
|
||||
# app
|
||||
|
||||
Application prototype.
|
||||
|
||||
# app.use()
|
||||
|
||||
Proxy `connect#use()` to apply settings to
|
||||
mounted applications.
|
||||
|
||||
# app.engine()
|
||||
|
||||
Register the given template engine callback `fn`
|
||||
as `ext`.
|
||||
|
||||
By default will `require()` the engine based on the
|
||||
file extension. For example if you try to render
|
||||
a "foo.jade" file Express will invoke the following internally:
|
||||
|
||||
app.engine('jade', require('jade').__express);
|
||||
|
||||
For engines that do not provide `.__express` out of the box,
|
||||
or if you wish to "map" a different extension to the template engine
|
||||
you may use this method. For example mapping the EJS template engine to
|
||||
".html" files
|
||||
|
||||
app.engine('html', require('ejs').renderFile);
|
||||
|
||||
In this case EJS provides a `.renderFile()` method with
|
||||
the same signature that Express expects: `(path, options, callback)`,
|
||||
though note that it aliases this method as `ejs.__express` internally
|
||||
so if you're using ".ejs" extensions you dont need to do anything.
|
||||
|
||||
Some template engines do not follow this convention, the
|
||||
[Consolidate.js](https://github.com/visionmedia/consolidate.js)
|
||||
library was created to map all of node's popular template
|
||||
engines to follow this convention, thus allowing them to
|
||||
work seemlessly within Express.
|
||||
|
||||
# app.param()
|
||||
|
||||
Map the given param placeholder `name`(s) to the given callback(s).
|
||||
|
||||
Parameter mapping is used to provide pre-conditions to routes
|
||||
which use normalized placeholders. For example a _:user_id_ parameter
|
||||
could automatically load a user's information from the database without
|
||||
any additional code,
|
||||
|
||||
The callback uses the samesignature as middleware, the only differencing
|
||||
being that the value of the placeholder is passed, in this case the _id_
|
||||
of the user. Once the `next()` function is invoked, just like middleware
|
||||
it will continue on to execute the route, or subsequent parameter functions.
|
||||
|
||||
app.param('user_id', function(req, res, next, id){
|
||||
User.find(id, function(err, user){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else if (user) {
|
||||
req.user = user;
|
||||
next();
|
||||
} else {
|
||||
next(new Error('failed to load user'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
# app.set()
|
||||
|
||||
Assign `setting` to `val`, or return `setting`'s value.
|
||||
|
||||
app.set('foo', 'bar');
|
||||
app.get('foo');
|
||||
// => "bar"
|
||||
|
||||
Mounted servers inherit their parent server's settings.
|
||||
|
||||
# app.enabled()
|
||||
|
||||
Check if `setting` is enabled (truthy).
|
||||
|
||||
app.enabled('foo')
|
||||
// => false
|
||||
|
||||
app.enable('foo')
|
||||
app.enabled('foo')
|
||||
// => true
|
||||
|
||||
# app.disabled()
|
||||
|
||||
Check if `setting` is disabled.
|
||||
|
||||
app.disabled('foo')
|
||||
// => true
|
||||
|
||||
app.enable('foo')
|
||||
app.disabled('foo')
|
||||
// => false
|
||||
|
||||
# app.enable()
|
||||
|
||||
Enable `setting`.
|
||||
|
||||
# app.disable()
|
||||
|
||||
Disable `setting`.
|
||||
|
||||
# app.configure()
|
||||
|
||||
Configure callback for zero or more envs,
|
||||
when no `env` is specified that callback will
|
||||
be invoked for all environments. Any combination
|
||||
can be used multiple times, in any order desired.
|
||||
|
||||
## Examples
|
||||
|
||||
app.configure(function(){
|
||||
// executed for all envs
|
||||
});
|
||||
|
||||
app.configure('stage', function(){
|
||||
// executed staging env
|
||||
});
|
||||
|
||||
app.configure('stage', 'production', function(){
|
||||
// executed for stage and production
|
||||
});
|
||||
|
||||
## Note
|
||||
|
||||
These callbacks are invoked immediately, and
|
||||
are effectively sugar for the following.
|
||||
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
switch (env) {
|
||||
case 'development':
|
||||
...
|
||||
break;
|
||||
case 'stage':
|
||||
...
|
||||
break;
|
||||
case 'production':
|
||||
...
|
||||
break;
|
||||
}
|
||||
|
||||
# app.all()
|
||||
|
||||
Special-cased "all" method, applying the given route `path`,
|
||||
middleware, and callback to _every_ HTTP method.
|
||||
|
||||
# app.render()
|
||||
|
||||
Render the given view `name` name with `options`
|
||||
and a callback accepting an error and the
|
||||
rendered template string.
|
||||
|
||||
## Example
|
||||
|
||||
app.render('email', { name: 'Tobi' }, function(err, html){
|
||||
// ...
|
||||
})
|
||||
|
||||
# app.listen()
|
||||
|
||||
Listen for connections.
|
||||
|
||||
A node `http.Server` is returned, with this
|
||||
application (which is a `Function`) as its
|
||||
callback. If you wish to create both an HTTP
|
||||
and HTTPS server you may do so with the "http"
|
||||
and "https" modules as shown here.
|
||||
|
||||
var http = require('http')
|
||||
, https = require('https')
|
||||
, express = require('express')
|
||||
, app = express();
|
||||
|
||||
http.createServer(app).listen(80);
|
||||
http.createServer({ ... }, app).listen(443);
|
||||
|
||||
|
||||
# req
|
||||
|
||||
Request prototype.
|
||||
|
||||
# req.get
|
||||
|
||||
Return request header.
|
||||
|
||||
The `Referrer` header field is special-cased,
|
||||
both `Referrer` and `Referer` are interchangeable.
|
||||
|
||||
## Examples
|
||||
|
||||
req.get('Content-Type');
|
||||
// => "text/plain"
|
||||
|
||||
req.get('content-type');
|
||||
// => "text/plain"
|
||||
|
||||
req.get('Something');
|
||||
// => undefined
|
||||
|
||||
Aliased as `req.header()`.
|
||||
|
||||
# req.accepts()
|
||||
|
||||
Check if the given `type(s)` is acceptable, returning
|
||||
the best match when true, otherwise `undefined`, in which
|
||||
case you should respond with 406 "Not Acceptable".
|
||||
|
||||
The `type` value may be a single mime type string
|
||||
such as "application/json", the extension name
|
||||
such as "json", a comma-delimted list such as "json, html, text/plain",
|
||||
or an array `["json", "html", "text/plain"]`. When a list
|
||||
or array is given the _best_ match, if any is returned.
|
||||
|
||||
## Examples
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
req.accepts('text/html');
|
||||
// => "text/html"
|
||||
req.accepts('json, text');
|
||||
// => "json"
|
||||
req.accepts('application/json');
|
||||
// => "application/json"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => undefined
|
||||
|
||||
// Accept: text/*;q=.5, application/json
|
||||
req.accepts(['html', 'json']);
|
||||
req.accepts('html, json');
|
||||
// => "json"
|
||||
|
||||
# req.acceptsCharset()
|
||||
|
||||
Check if the given `charset` is acceptable,
|
||||
otherwise you should respond with 406 "Not Acceptable".
|
||||
|
||||
# req.acceptsLanguage()
|
||||
|
||||
Check if the given `lang` is acceptable,
|
||||
otherwise you should respond with 406 "Not Acceptable".
|
||||
|
||||
# req.param()
|
||||
|
||||
Return the value of param `name` when present or `defaultValue`.
|
||||
|
||||
- Checks route placeholders, ex: _/user/:id_
|
||||
- Checks body params, ex: id=12, {"id":12}
|
||||
- Checks query string params, ex: ?id=12
|
||||
|
||||
To utilize request bodies, `req.body`
|
||||
should be an object. This can be done by using
|
||||
the `connect.bodyParser()` middleware.
|
||||
|
||||
# req.is()
|
||||
|
||||
Check if the incoming request contains the "Content-Type"
|
||||
header field, and it contains the give mime `type`.
|
||||
|
||||
## Examples
|
||||
|
||||
// With Content-Type: text/html; charset=utf-8
|
||||
req.is('html');
|
||||
req.is('text/html');
|
||||
req.is('text/*');
|
||||
// => true
|
||||
|
||||
// When Content-Type is application/json
|
||||
req.is('json');
|
||||
req.is('application/json');
|
||||
req.is('application/*');
|
||||
// => true
|
||||
|
||||
req.is('html');
|
||||
// => false
|
||||
|
||||
|
||||
# res
|
||||
|
||||
Response prototype.
|
||||
|
||||
# res.status()
|
||||
|
||||
Set status `code`.
|
||||
|
||||
# res.send()
|
||||
|
||||
Send a response.
|
||||
|
||||
## Examples
|
||||
|
||||
res.send(new Buffer('wahoo'));
|
||||
res.send({ some: 'json' });
|
||||
res.send('<p>some html</p>');
|
||||
res.send(404, 'Sorry, cant find that');
|
||||
res.send(404);
|
||||
|
||||
# res.json()
|
||||
|
||||
Send JSON response.
|
||||
|
||||
## Examples
|
||||
|
||||
res.json(null);
|
||||
res.json({ user: 'tj' });
|
||||
res.json(500, 'oh noes!');
|
||||
res.json(404, 'I dont have that');
|
||||
|
||||
# res.sendfile()
|
||||
|
||||
Transfer the file at the given `path`.
|
||||
|
||||
Automatically sets the _Content-Type_ response header field.
|
||||
The callback `fn(err)` is invoked when the transfer is complete
|
||||
or when an error occurs. Be sure to check `res.sentHeader`
|
||||
if you wish to attempt responding, as the header and some data
|
||||
may have already been transferred.
|
||||
|
||||
## Options
|
||||
|
||||
- `maxAge` defaulting to 0
|
||||
- `root` root directory for relative filenames
|
||||
|
||||
## Examples
|
||||
|
||||
The following example illustrates how `res.sendfile()` may
|
||||
be used as an alternative for the `static()` middleware for
|
||||
dynamic situations. The code backing `res.sendfile()` is actually
|
||||
the same code, so HTTP cache support etc is identical.
|
||||
|
||||
app.get('/user/:uid/photos/:file', function(req, res){
|
||||
var uid = req.params.uid
|
||||
, file = req.params.file;
|
||||
|
||||
req.user.mayViewFilesFrom(uid, function(yes){
|
||||
if (yes) {
|
||||
res.sendfile('/uploads/' + uid + '/' + file);
|
||||
} else {
|
||||
res.send(403, 'Sorry! you cant see that.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
# res.download()
|
||||
|
||||
Transfer the file at the given `path` as an attachment.
|
||||
|
||||
Optionally providing an alternate attachment `filename`,
|
||||
and optional callback `fn(err)`. The callback is invoked
|
||||
when the data transfer is complete, or when an error has
|
||||
ocurred. Be sure to check `res.headerSent` if you plan to respond.
|
||||
|
||||
This method uses `res.sendfile()`.
|
||||
|
||||
# res.format()
|
||||
|
||||
Respond to the Acceptable formats using an `obj`
|
||||
of mime-type callbacks.
|
||||
|
||||
This method uses `req.accepted`, an array of
|
||||
acceptable types ordered by their quality values.
|
||||
When "Accept" is not present the _first_ callback
|
||||
is invoked, otherwise the first match is used. When
|
||||
no match is performed the server responds with
|
||||
406 "Not Acceptable".
|
||||
|
||||
Content-Type is set for you, however if you choose
|
||||
you may alter this within the callback using `res.type()`
|
||||
or `res.set('Content-Type', ...)`.
|
||||
|
||||
res.format({
|
||||
'text/plain': function(){
|
||||
res.send('hey');
|
||||
},
|
||||
|
||||
'text/html': function(){
|
||||
res.send('<p>hey</p>');
|
||||
},
|
||||
|
||||
'appliation/json': function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
});
|
||||
|
||||
In addition to canonicalized MIME types you may
|
||||
also use extnames mapped to these types:
|
||||
|
||||
res.format({
|
||||
text: function(){
|
||||
res.send('hey');
|
||||
},
|
||||
|
||||
html: function(){
|
||||
res.send('<p>hey</p>');
|
||||
},
|
||||
|
||||
json: function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
});
|
||||
|
||||
By default Express passes an `Error`
|
||||
with a `.status` of 406 to `next(err)`
|
||||
if a match is not made, however you may
|
||||
provide an optional callback `fn` to
|
||||
be invoked instead.
|
||||
|
||||
# res.attachment()
|
||||
|
||||
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
|
||||
|
||||
# res.set
|
||||
|
||||
Set header `field` to `val`, or pass
|
||||
an object of header fields.
|
||||
|
||||
## Examples
|
||||
|
||||
res.set('Accept', 'application/json');
|
||||
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
||||
|
||||
Aliased as `res.header()`.
|
||||
|
||||
# res.get()
|
||||
|
||||
Get value for header `field`.
|
||||
|
||||
# res.clearCookie()
|
||||
|
||||
Clear cookie `name`.
|
||||
|
||||
# res.cookie()
|
||||
|
||||
Set cookie `name` to `val`, with the given `options`.
|
||||
|
||||
## Options
|
||||
|
||||
- `maxAge` max-age in milliseconds, converted to `expires`
|
||||
- `signed` sign the cookie
|
||||
- `path` defaults to "/"
|
||||
|
||||
## Examples
|
||||
|
||||
// "Remember Me" for 15 minutes
|
||||
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
|
||||
// save as above
|
||||
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
|
||||
|
||||
# res.redirect()
|
||||
|
||||
Redirect to the given `url` with optional response `status`
|
||||
defaulting to 302.
|
||||
|
||||
The given `url` can also be the name of a mapped url, for
|
||||
example by default express supports "back" which redirects
|
||||
to the _Referrer_ or _Referer_ headers or "/".
|
||||
|
||||
## Examples
|
||||
|
||||
res.redirect('/foo/bar');
|
||||
res.redirect('http://example.com');
|
||||
res.redirect(301, 'http://example.com');
|
||||
res.redirect('../login'); // /blog/post/1 -> /blog/login
|
||||
|
||||
## Mounting
|
||||
|
||||
When an application is mounted, and `res.redirect()`
|
||||
is given a path that does _not_ lead with "/". For
|
||||
example suppose a "blog" app is mounted at "/blog",
|
||||
the following redirect would result in "/blog/login":
|
||||
|
||||
res.redirect('login');
|
||||
|
||||
While the leading slash would result in a redirect to "/login":
|
||||
|
||||
res.redirect('/login');
|
||||
|
||||
# res.render()
|
||||
|
||||
Render `view` with the given `options` and optional callback `fn`.
|
||||
When a callback function is given a response will _not_ be made
|
||||
automatically, otherwise a response of _200_ and _text/html_ is given.
|
||||
|
||||
## Options
|
||||
|
||||
- `status` Response status code (`res.statusCode`)
|
||||
- `charset` Set the charset (`res.charset`)
|
||||
|
||||
## Reserved locals
|
||||
|
||||
- `cache` boolean hinting to the engine it should cache
|
||||
- `filename` filename of the view being rendered
|
||||
|
||||
107
docs/request.md
107
docs/request.md
@@ -1,107 +0,0 @@
|
||||
|
||||
# req
|
||||
|
||||
Request prototype.
|
||||
|
||||
# req.get
|
||||
|
||||
Return request header.
|
||||
|
||||
The `Referrer` header field is special-cased,
|
||||
both `Referrer` and `Referer` are interchangeable.
|
||||
|
||||
## Examples
|
||||
|
||||
req.get('Content-Type');
|
||||
// => "text/plain"
|
||||
|
||||
req.get('content-type');
|
||||
// => "text/plain"
|
||||
|
||||
req.get('Something');
|
||||
// => undefined
|
||||
|
||||
Aliased as `req.header()`.
|
||||
|
||||
# req.accepts()
|
||||
|
||||
Check if the given `type(s)` is acceptable, returning
|
||||
the best match when true, otherwise `undefined`, in which
|
||||
case you should respond with 406 "Not Acceptable".
|
||||
|
||||
The `type` value may be a single mime type string
|
||||
such as "application/json", the extension name
|
||||
such as "json", a comma-delimted list such as "json, html, text/plain",
|
||||
or an array `["json", "html", "text/plain"]`. When a list
|
||||
or array is given the _best_ match, if any is returned.
|
||||
|
||||
## Examples
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
req.accepts('text/html');
|
||||
// => "text/html"
|
||||
req.accepts('json, text');
|
||||
// => "json"
|
||||
req.accepts('application/json');
|
||||
// => "application/json"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => undefined
|
||||
|
||||
// Accept: text/*;q=.5, application/json
|
||||
req.accepts(['html', 'json']);
|
||||
req.accepts('html, json');
|
||||
// => "json"
|
||||
|
||||
# req.acceptsCharset()
|
||||
|
||||
Check if the given `charset` is acceptable,
|
||||
otherwise you should respond with 406 "Not Acceptable".
|
||||
|
||||
# req.acceptsLanguage()
|
||||
|
||||
Check if the given `lang` is acceptable,
|
||||
otherwise you should respond with 406 "Not Acceptable".
|
||||
|
||||
# req.param()
|
||||
|
||||
Return the value of param `name` when present or `defaultValue`.
|
||||
|
||||
- Checks route placeholders, ex: _/user/:id_
|
||||
- Checks body params, ex: id=12, {"id":12}
|
||||
- Checks query string params, ex: ?id=12
|
||||
|
||||
To utilize request bodies, `req.body`
|
||||
should be an object. This can be done by using
|
||||
the `connect.bodyParser()` middleware.
|
||||
|
||||
# req.is()
|
||||
|
||||
Check if the incoming request contains the "Content-Type"
|
||||
header field, and it contains the give mime `type`.
|
||||
|
||||
## Examples
|
||||
|
||||
// With Content-Type: text/html; charset=utf-8
|
||||
req.is('html');
|
||||
req.is('text/html');
|
||||
req.is('text/*');
|
||||
// => true
|
||||
|
||||
// When Content-Type is application/json
|
||||
req.is('json');
|
||||
req.is('application/json');
|
||||
req.is('application/*');
|
||||
// => true
|
||||
|
||||
req.is('html');
|
||||
// => false
|
||||
|
||||
218
docs/response.md
218
docs/response.md
@@ -1,218 +0,0 @@
|
||||
|
||||
# res
|
||||
|
||||
Response prototype.
|
||||
|
||||
# res.status()
|
||||
|
||||
Set status `code`.
|
||||
|
||||
# res.send()
|
||||
|
||||
Send a response.
|
||||
|
||||
## Examples
|
||||
|
||||
res.send(new Buffer('wahoo'));
|
||||
res.send({ some: 'json' });
|
||||
res.send('<p>some html</p>');
|
||||
res.send(404, 'Sorry, cant find that');
|
||||
res.send(404);
|
||||
|
||||
# res.json()
|
||||
|
||||
Send JSON response.
|
||||
|
||||
## Examples
|
||||
|
||||
res.json(null);
|
||||
res.json({ user: 'tj' });
|
||||
res.json(500, 'oh noes!');
|
||||
res.json(404, 'I dont have that');
|
||||
|
||||
# res.sendfile()
|
||||
|
||||
Transfer the file at the given `path`.
|
||||
|
||||
Automatically sets the _Content-Type_ response header field.
|
||||
The callback `fn(err)` is invoked when the transfer is complete
|
||||
or when an error occurs. Be sure to check `res.sentHeader`
|
||||
if you wish to attempt responding, as the header and some data
|
||||
may have already been transferred.
|
||||
|
||||
## Options
|
||||
|
||||
- `maxAge` defaulting to 0
|
||||
- `root` root directory for relative filenames
|
||||
|
||||
## Examples
|
||||
|
||||
The following example illustrates how `res.sendfile()` may
|
||||
be used as an alternative for the `static()` middleware for
|
||||
dynamic situations. The code backing `res.sendfile()` is actually
|
||||
the same code, so HTTP cache support etc is identical.
|
||||
|
||||
app.get('/user/:uid/photos/:file', function(req, res){
|
||||
var uid = req.params.uid
|
||||
, file = req.params.file;
|
||||
|
||||
req.user.mayViewFilesFrom(uid, function(yes){
|
||||
if (yes) {
|
||||
res.sendfile('/uploads/' + uid + '/' + file);
|
||||
} else {
|
||||
res.send(403, 'Sorry! you cant see that.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
# res.download()
|
||||
|
||||
Transfer the file at the given `path` as an attachment.
|
||||
|
||||
Optionally providing an alternate attachment `filename`,
|
||||
and optional callback `fn(err)`. The callback is invoked
|
||||
when the data transfer is complete, or when an error has
|
||||
ocurred. Be sure to check `res.headerSent` if you plan to respond.
|
||||
|
||||
This method uses `res.sendfile()`.
|
||||
|
||||
# res.format()
|
||||
|
||||
Respond to the Acceptable formats using an `obj`
|
||||
of mime-type callbacks.
|
||||
|
||||
This method uses `req.accepted`, an array of
|
||||
acceptable types ordered by their quality values.
|
||||
When "Accept" is not present the _first_ callback
|
||||
is invoked, otherwise the first match is used. When
|
||||
no match is performed the server responds with
|
||||
406 "Not Acceptable".
|
||||
|
||||
Content-Type is set for you, however if you choose
|
||||
you may alter this within the callback using `res.type()`
|
||||
or `res.set('Content-Type', ...)`.
|
||||
|
||||
res.format({
|
||||
'text/plain': function(){
|
||||
res.send('hey');
|
||||
},
|
||||
|
||||
'text/html': function(){
|
||||
res.send('<p>hey</p>');
|
||||
},
|
||||
|
||||
'appliation/json': function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
});
|
||||
|
||||
In addition to canonicalized MIME types you may
|
||||
also use extnames mapped to these types:
|
||||
|
||||
res.format({
|
||||
text: function(){
|
||||
res.send('hey');
|
||||
},
|
||||
|
||||
html: function(){
|
||||
res.send('<p>hey</p>');
|
||||
},
|
||||
|
||||
json: function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
});
|
||||
|
||||
By default Express passes an `Error`
|
||||
with a `.status` of 406 to `next(err)`
|
||||
if a match is not made, however you may
|
||||
provide an optional callback `fn` to
|
||||
be invoked instead.
|
||||
|
||||
# res.attachment()
|
||||
|
||||
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
|
||||
|
||||
# res.set
|
||||
|
||||
Set header `field` to `val`, or pass
|
||||
an object of header fields.
|
||||
|
||||
## Examples
|
||||
|
||||
res.set('Accept', 'application/json');
|
||||
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
||||
|
||||
Aliased as `res.header()`.
|
||||
|
||||
# res.get()
|
||||
|
||||
Get value for header `field`.
|
||||
|
||||
# res.clearCookie()
|
||||
|
||||
Clear cookie `name`.
|
||||
|
||||
# res.cookie()
|
||||
|
||||
Set cookie `name` to `val`, with the given `options`.
|
||||
|
||||
## Options
|
||||
|
||||
- `maxAge` max-age in milliseconds, converted to `expires`
|
||||
- `signed` sign the cookie
|
||||
- `path` defaults to "/"
|
||||
|
||||
## Examples
|
||||
|
||||
// "Remember Me" for 15 minutes
|
||||
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
|
||||
// save as above
|
||||
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
|
||||
|
||||
# res.redirect()
|
||||
|
||||
Redirect to the given `url` with optional response `status`
|
||||
defaulting to 302.
|
||||
|
||||
The given `url` can also be the name of a mapped url, for
|
||||
example by default express supports "back" which redirects
|
||||
to the _Referrer_ or _Referer_ headers or "/".
|
||||
|
||||
## Examples
|
||||
|
||||
res.redirect('/foo/bar');
|
||||
res.redirect('http://example.com');
|
||||
res.redirect(301, 'http://example.com');
|
||||
res.redirect('../login'); // /blog/post/1 -> /blog/login
|
||||
|
||||
## Mounting
|
||||
|
||||
When an application is mounted, and `res.redirect()`
|
||||
is given a path that does _not_ lead with "/". For
|
||||
example suppose a "blog" app is mounted at "/blog",
|
||||
the following redirect would result in "/blog/login":
|
||||
|
||||
res.redirect('login');
|
||||
|
||||
While the leading slash would result in a redirect to "/login":
|
||||
|
||||
res.redirect('/login');
|
||||
|
||||
# res.render()
|
||||
|
||||
Render `view` with the given `options` and optional callback `fn`.
|
||||
When a callback function is given a response will _not_ be made
|
||||
automatically, otherwise a response of _200_ and _text/html_ is given.
|
||||
|
||||
## Options
|
||||
|
||||
- `status` Response status code (`res.statusCode`)
|
||||
- `charset` Set the charset (`res.charset`)
|
||||
|
||||
## Reserved locals
|
||||
|
||||
- `cache` boolean hinting to the engine it should cache
|
||||
- `filename` filename of the view being rendered
|
||||
|
||||
@@ -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
examples/auth/pass.js
Normal file
46
examples/auth/pass.js
Normal file
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
45
examples/cors/index.js
Normal file
45
examples/cors/index.js
Normal file
@@ -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
examples/cors/public/index.html
Normal file
12
examples/cors/public/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var req = new XMLHttpRequest;
|
||||
req.open('POST', 'http://localhost:3001/user', false);
|
||||
req.setRequestHeader('Content-Type', 'application/json');
|
||||
req.send('{"name":"tobi","species":"ferret"}');
|
||||
console.log(req.responseText);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 });
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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');
|
||||
@@ -1,5 +0,0 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Partials Example
|
||||
body!= body
|
||||
@@ -1 +0,0 @@
|
||||
li= value
|
||||
@@ -1 +0,0 @@
|
||||
li.weapon= weapon
|
||||
@@ -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)
|
||||
@@ -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
examples/route-map/index.js
Normal file
63
examples/route-map/index.js
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, verbose = process.env.NODE_ENV != 'test'
|
||||
, app = module.exports = express();
|
||||
|
||||
app.map = function(a, route){
|
||||
route = route || '';
|
||||
for (var key in a) {
|
||||
switch (typeof a[key]) {
|
||||
// { '/path': { ... }}
|
||||
case 'object':
|
||||
app.map(a[key], route + key);
|
||||
break;
|
||||
// get: function(){ ... }
|
||||
case 'function':
|
||||
if (verbose) console.log('%s %s', key, route);
|
||||
app[key](route, a[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var users = {
|
||||
list: function(req, res){
|
||||
res.send('user list');
|
||||
},
|
||||
|
||||
get: function(req, res){
|
||||
res.send('user ' + req.params.uid);
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
res.send('delete users');
|
||||
}
|
||||
};
|
||||
|
||||
var pets = {
|
||||
list: function(req, res){
|
||||
res.send('user ' + req.params.uid + '\'s pets');
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
|
||||
}
|
||||
};
|
||||
|
||||
app.map({
|
||||
'/users': {
|
||||
get: users.list,
|
||||
del: users.del,
|
||||
'/:uid': {
|
||||
get: users.get,
|
||||
'/pets': {
|
||||
get: pets.list,
|
||||
'/:pid': {
|
||||
del: pets.del
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -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');
|
||||
@@ -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);
|
||||
@@ -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
examples/stylus/public/.gitignore
vendored
1
examples/stylus/public/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
*.css
|
||||
@@ -1,2 +0,0 @@
|
||||
h1 Stylus
|
||||
p Just an example of using Stylus with Express.
|
||||
@@ -1,6 +0,0 @@
|
||||
html
|
||||
head
|
||||
title Stylus Example
|
||||
link(rel='stylesheet', href='/reset.css')
|
||||
link(rel='stylesheet', href='/main.css')
|
||||
body!= body
|
||||
@@ -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));
|
||||
@@ -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');
|
||||
@@ -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
|
||||
|
||||
@@ -10,6 +10,7 @@ var http = require('http')
|
||||
, Router = require('./router')
|
||||
, req = require('./request')
|
||||
, res = require('./response')
|
||||
, send = require('send')
|
||||
, utils = connect.utils;
|
||||
|
||||
/**
|
||||
@@ -22,13 +23,13 @@ exports = module.exports = createApplication;
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
exports.version = '3.0.0beta3';
|
||||
exports.version = '3.0.0rc2';
|
||||
|
||||
/**
|
||||
* Expose mime.
|
||||
*/
|
||||
|
||||
exports.mime = connect.mime;
|
||||
exports.mime = send.mime;
|
||||
|
||||
/**
|
||||
* Create an express application.
|
||||
@@ -59,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.
|
||||
@@ -79,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';
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ var http = require('http')
|
||||
, utils = require('./utils')
|
||||
, connect = require('connect')
|
||||
, fresh = require('fresh')
|
||||
, parseRange = require('range-parser')
|
||||
, parse = connect.utils.parseUrl
|
||||
, mime = connect.mime;
|
||||
|
||||
@@ -132,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.
|
||||
@@ -341,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.
|
||||
*
|
||||
@@ -390,7 +444,18 @@ req.__defineGetter__('host', function(){
|
||||
*/
|
||||
|
||||
req.__defineGetter__('fresh', function(){
|
||||
return fresh(this.headers, this.res._headers);
|
||||
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;
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
189
lib/response.js
189
lib/response.js
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -13,6 +12,7 @@ var fs = require('fs')
|
||||
, statusCodes = http.STATUS_CODES
|
||||
, send = connect.static.send
|
||||
, cookie = require('cookie')
|
||||
, send = require('send')
|
||||
, crc = require('crc')
|
||||
, mime = connect.mime
|
||||
, basename = path.basename
|
||||
@@ -40,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.
|
||||
*
|
||||
@@ -64,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();
|
||||
|
||||
@@ -146,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 + ');';
|
||||
@@ -212,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) {
|
||||
@@ -220,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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -346,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');
|
||||
|
||||
@@ -541,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]) {
|
||||
@@ -554,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
|
||||
@@ -564,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;
|
||||
@@ -583,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
|
||||
@@ -609,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);
|
||||
};
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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'
|
||||
];
|
||||
19
lib/utils.js
19
lib/utils.js
@@ -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,8 +252,7 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
|
||||
path = path
|
||||
.concat(strict ? '' : '/?')
|
||||
.replace(/\/\(/g, '(?:/')
|
||||
.replace(/\+/g, '__plus__')
|
||||
.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 ''
|
||||
@@ -273,10 +260,10 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
|
||||
+ (optional || '');
|
||||
+ (optional || '')
|
||||
+ (star ? '(/*)?' : '');
|
||||
})
|
||||
.replace(/([\/.])/g, '\\$1')
|
||||
.replace(/__plus__/g, '(.+)')
|
||||
.replace(/\*/g, '(.*)');
|
||||
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
|
||||
}
|
||||
28
package.json
28
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Sinatra inspired web development framework",
|
||||
"version": "3.0.0beta3",
|
||||
"version": "3.0.0rc2",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
|
||||
@@ -10,12 +10,15 @@
|
||||
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
|
||||
],
|
||||
"dependencies": {
|
||||
"connect": "2.3.3",
|
||||
"connect": "2.4.2",
|
||||
"commander": "0.6.1",
|
||||
"mkdirp": "0.3.2",
|
||||
"cookie": "0.0.3",
|
||||
"range-parser": "0.0.4",
|
||||
"mkdirp": "0.3.3",
|
||||
"cookie": "0.0.4",
|
||||
"crc": "0.2.0",
|
||||
"fresh": "0.0.1",
|
||||
"fresh": "0.1.0",
|
||||
"methods": "0.0.1",
|
||||
"send": "0.0.3",
|
||||
"debug": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -26,10 +29,21 @@
|
||||
"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" },
|
||||
|
||||
@@ -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');
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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();
|
||||
});
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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();
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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
test/acceptance/route-map.js
Normal file
45
test/acceptance/route-map.js
Normal file
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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();
|
||||
});
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('app.all()', function(){
|
||||
});
|
||||
|
||||
request(app)
|
||||
.delete('/tobi')
|
||||
.del('/tobi')
|
||||
.expect(404, done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('app.del()', function(){
|
||||
});
|
||||
|
||||
request(app)
|
||||
.delete('/tobi')
|
||||
.del('/tobi')
|
||||
.expect('deleted tobi!', done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
|
||||
@@ -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();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -256,6 +259,23 @@ describe('app.router', function(){
|
||||
})
|
||||
})
|
||||
|
||||
it('should allow escaped regexp', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/user/\\d+', function(req, res){
|
||||
res.end('woot');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/10')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(200);
|
||||
request(app)
|
||||
.get('/user/tj')
|
||||
.expect(404, done);
|
||||
});
|
||||
})
|
||||
|
||||
it('should allow literal "."', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -271,30 +291,8 @@ describe('app.router', function(){
|
||||
.expect('users from 1 to 50', done);
|
||||
})
|
||||
|
||||
describe('+', function(){
|
||||
it('should denote a greedy capture group', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/blog+', function(req, res){
|
||||
res.end(req.params[0] || 'nothing');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/blog')
|
||||
.expect(404, function(){
|
||||
request(app)
|
||||
.get('/blog/post')
|
||||
.expect(200, function(){
|
||||
request(app)
|
||||
.get('/blog-admin')
|
||||
.expect(200, done)
|
||||
})
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe('*', function(){
|
||||
it('should denote an optional greedy capture group', function(done){
|
||||
it('should denote a greedy capture group', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/user/*.json', function(req, res){
|
||||
@@ -349,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();
|
||||
|
||||
|
||||
@@ -17,13 +17,6 @@ describe('exports', function(){
|
||||
express.mime.should.equal(require('connect').mime);
|
||||
})
|
||||
|
||||
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 Router', function(){
|
||||
express.Router.should.be.a('function');
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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,12 +41,8 @@ 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){
|
||||
@@ -64,10 +54,7 @@ describe('req', function(){
|
||||
|
||||
request(app)
|
||||
.get('/user/tj')
|
||||
.end(function(res){
|
||||
res.body.should.equal('undefinedtj');
|
||||
done();
|
||||
})
|
||||
.expect('undefinedtj', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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
test/req.range.js
Normal file
31
test/req.range.js
Normal file
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -29,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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,9 +13,9 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
var val = 'sid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
res.headers['set-cookie'].should.eql([val]);
|
||||
res.header['set-cookie'].should.eql([val]);
|
||||
done();
|
||||
})
|
||||
})
|
||||
@@ -31,9 +31,9 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
var val = 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
res.headers['set-cookie'].should.eql([val]);
|
||||
res.header['set-cookie'].should.eql([val]);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,8 +14,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
var val = ['user=j:{%22name%22:%22tobi%22}; Path=/'];
|
||||
.end(function(err, res){
|
||||
var val = ['user=' + encodeURIComponent('j:{"name":"tobi"}') + '; Path=/'];
|
||||
res.headers['set-cookie'].should.eql(val);
|
||||
done();
|
||||
})
|
||||
@@ -32,7 +32,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
var val = ['name=tobi; Path=/'];
|
||||
res.headers['set-cookie'].should.eql(val);
|
||||
done();
|
||||
@@ -50,7 +50,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
var val = ['name=tobi; Path=/', 'age=1; Path=/'];
|
||||
res.headers['set-cookie'].should.eql(val);
|
||||
done();
|
||||
@@ -69,7 +69,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
var val = ['name=tobi; Path=/; HttpOnly; Secure'];
|
||||
res.headers['set-cookie'].should.eql(val);
|
||||
done();
|
||||
@@ -87,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();
|
||||
})
|
||||
@@ -106,7 +106,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
var val = res.headers['set-cookie'][0];
|
||||
val = cookie.parse(val.split('.')[0]);
|
||||
val.user.should.equal('j:{"name":"tobi"}');
|
||||
@@ -127,7 +127,7 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
var val = ['name=tobi.xJjV2iZ6EI7C8E5kzwbfA9PVLl1ZR07UTnuTgQQ4EnQ; Path=/'];
|
||||
res.headers['set-cookie'].should.eql(val);
|
||||
done();
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
test/res.links.js
Normal file
19
test/res.links.js
Normal file
@@ -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"');
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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/<lame>">//http://example.com/<lame></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,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);
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
117
test/res.send.js
117
test/res.send.js
@@ -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,16 +270,16 @@ 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 freshness', function(done){
|
||||
it('should always check regardless of length', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
@@ -313,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);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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(){
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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,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');
|
||||
Reference in New Issue
Block a user