mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 18:15:04 +00:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
311e83e591 | ||
|
|
fb2d918056 | ||
|
|
dfefea5e9d | ||
|
|
3fbab91231 | ||
|
|
f7e73e2da0 | ||
|
|
867728b5ab | ||
|
|
87e02c30e7 | ||
|
|
c3470c9c96 | ||
|
|
7f049164b7 | ||
|
|
4e12a72873 | ||
|
|
91e0c27252 | ||
|
|
db4a061ed6 | ||
|
|
f6bbeafd26 | ||
|
|
f14e39d451 | ||
|
|
084f5d891b | ||
|
|
b0f72e13d9 | ||
|
|
8d7d80ef9d | ||
|
|
cf5de082b5 | ||
|
|
1944451082 | ||
|
|
602e5a8200 | ||
|
|
83b8b7acb7 | ||
|
|
e660f19507 | ||
|
|
ff412b927d | ||
|
|
392ef1eb06 | ||
|
|
dcecdc9be6 | ||
|
|
ed69b68892 | ||
|
|
42b982e13c | ||
|
|
e7e2592357 | ||
|
|
739586f96a | ||
|
|
4c0f1f53d3 | ||
|
|
359f12791a | ||
|
|
9354ab62dd | ||
|
|
23ff74bb3f | ||
|
|
98d17e2293 | ||
|
|
ababa6ae5b | ||
|
|
097cd0c242 | ||
|
|
b91cd66fc5 | ||
|
|
787d630157 | ||
|
|
1f938c560a | ||
|
|
a96924a555 | ||
|
|
33dc6629ff | ||
|
|
1b3fb0af8c | ||
|
|
12da523ff7 | ||
|
|
0f49d80623 | ||
|
|
1717516a78 | ||
|
|
328c6d3060 | ||
|
|
566720be15 | ||
|
|
65f13c3cc6 | ||
|
|
31b2e2d7b4 | ||
|
|
8fe8d74056 | ||
|
|
fcc4742056 | ||
|
|
d98e2e7498 | ||
|
|
d37ffa1149 | ||
|
|
cf709f3021 | ||
|
|
7515ee6a78 | ||
|
|
b8d6d258b0 | ||
|
|
35c50601bd | ||
|
|
f7be983a77 | ||
|
|
bc9bcb0317 | ||
|
|
a2553126dd | ||
|
|
0639c45acd | ||
|
|
e4302b2120 | ||
|
|
3d6b4ba013 | ||
|
|
920f46ad65 | ||
|
|
35a66d8a14 | ||
|
|
4e1e252e17 | ||
|
|
0461e55380 | ||
|
|
8dc4ff26f9 | ||
|
|
6f9e927633 | ||
|
|
40a43eb753 | ||
|
|
a8bb4bab2b | ||
|
|
417999884b | ||
|
|
17a739e35f | ||
|
|
555ffe37b2 | ||
|
|
165578a1da | ||
|
|
0bbbc84959 | ||
|
|
66b38b58bc | ||
|
|
4b646c2f8d | ||
|
|
35757ed1f5 | ||
|
|
fed0d5df5c | ||
|
|
0d468cbe6c | ||
|
|
f92ba6d0cf | ||
|
|
0408e3727e | ||
|
|
be997fd654 | ||
|
|
5c3852b91c | ||
|
|
3c7310ebcb |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
coverage.html
|
||||
coverage/
|
||||
.DS_Store
|
||||
lib-cov
|
||||
*.seed
|
||||
*.log
|
||||
*.csv
|
||||
@@ -13,7 +12,5 @@ benchmarks/graphs
|
||||
testing
|
||||
node_modules/
|
||||
testing
|
||||
.coverage_data
|
||||
cover_html
|
||||
test.js
|
||||
.idea
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.git*
|
||||
benchmarks/
|
||||
coverage/
|
||||
docs/
|
||||
examples/
|
||||
support/
|
||||
@@ -7,5 +8,4 @@ test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
.travis.yml
|
||||
coverage.html
|
||||
lib-cov
|
||||
Contributing.md
|
||||
|
||||
@@ -6,3 +6,5 @@ matrix:
|
||||
allow_failures:
|
||||
- node_js: "0.11"
|
||||
fast_finish: true
|
||||
script: "npm run-script test-travis"
|
||||
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
|
||||
|
||||
25
Contributing.md
Normal file
25
Contributing.md
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
## Website Issues
|
||||
|
||||
Issues for the expressjs.com website go here https://github.com/visionmedia/expressjs.com
|
||||
|
||||
## PRs and Code contributions
|
||||
|
||||
* Tests must pass.
|
||||
* Follow existing coding style.
|
||||
* If you fix a bug, add a test.
|
||||
|
||||
|
||||
## Issues which are questions
|
||||
|
||||
We will typically close any vague issues or questions that are specific to some app you are writing. Please double check the docs and other references before being trigger happy with posting a question issue.
|
||||
|
||||
Things that will help get your question issue looked at:
|
||||
|
||||
* Full and runnable JS code.
|
||||
* Clear description of the problem or unexpected behavior.
|
||||
* Clear description of the expected result.
|
||||
* Steps you have taken to debug it yourself.
|
||||
|
||||
If you post a question and do not outline the above items or make it easy for us to understand and reproduce your issue, it will be closed.
|
||||
|
||||
147
History.md
147
History.md
@@ -1,3 +1,54 @@
|
||||
4.3.2 / 2014-05-28
|
||||
==================
|
||||
|
||||
* fix handling of errors from `router.param()` callbacks
|
||||
|
||||
4.3.1 / 2014-05-23
|
||||
==================
|
||||
|
||||
* revert "fix behavior of multiple `app.VERB` for the same path"
|
||||
- this caused a regression in the order of route execution
|
||||
|
||||
4.3.0 / 2014-05-21
|
||||
==================
|
||||
|
||||
* add `req.baseUrl` to access the path stripped from `req.url` in routes
|
||||
* fix behavior of multiple `app.VERB` for the same path
|
||||
* fix issue routing requests among sub routers
|
||||
* invoke `router.param()` only when necessary instead of every match
|
||||
* proper proxy trust with `app.set('trust proxy', trust)`
|
||||
- `app.set('trust proxy', 1)` trust first hop
|
||||
- `app.set('trust proxy', 'loopback')` trust loopback addresses
|
||||
- `app.set('trust proxy', '10.0.0.1')` trust single IP
|
||||
- `app.set('trust proxy', '10.0.0.1/16')` trust subnet
|
||||
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
|
||||
- `app.set('trust proxy', false)` turn off
|
||||
- `app.set('trust proxy', true)` trust everything
|
||||
* set proper `charset` in `Content-Type` for `res.send`
|
||||
* update type-is to 1.2.0
|
||||
- support suffix matching
|
||||
|
||||
4.2.0 / 2014-05-11
|
||||
==================
|
||||
|
||||
* deprecate `app.del()` -- use `app.delete()` instead
|
||||
* deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead
|
||||
- the edge-case `res.json(status, num)` requires `res.status(status).json(num)`
|
||||
* deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead
|
||||
- the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)`
|
||||
* fix `req.next` when inside router instance
|
||||
* include `ETag` header in `HEAD` requests
|
||||
* keep previous `Content-Type` for `res.jsonp`
|
||||
* support PURGE method
|
||||
- add `app.purge`
|
||||
- add `router.purge`
|
||||
- include PURGE in `app.all`
|
||||
* update debug to 0.8.0
|
||||
- add `enable()` method
|
||||
- change from stderr to stdout
|
||||
* update methods to 1.0.0
|
||||
- add PURGE
|
||||
|
||||
4.1.2 / 2014-05-08
|
||||
==================
|
||||
|
||||
@@ -16,10 +67,31 @@
|
||||
* preserve casing of headers in `res.header` and `res.set`
|
||||
* support unicode file names in `res.attachment` and `res.download`
|
||||
* update accepts to 1.0.1
|
||||
- deps: negotiator@0.4.0
|
||||
* update cookie to 0.1.2
|
||||
- Fix for maxAge == 0
|
||||
- made compat with expires field
|
||||
* update send to 0.3.0
|
||||
- Accept API options in options object
|
||||
- Coerce option types
|
||||
- Control whether to generate etags
|
||||
- Default directory access to 403 when index disabled
|
||||
- Fix sending files with dots without root set
|
||||
- Include file path in etag
|
||||
- Make "Can't set headers after they are sent." catchable
|
||||
- Send full entity-body for multi range requests
|
||||
- Set etags to "weak"
|
||||
- Support "If-Range" header
|
||||
- Support multiple index paths
|
||||
- deps: mime@1.2.11
|
||||
* update serve-static to 1.1.0
|
||||
- Accept options directly to `send` module
|
||||
- Resolve relative paths at middleware setup
|
||||
- Use parseurl to parse the URL from request
|
||||
- deps: send@0.3.0
|
||||
* update type-is to 1.1.0
|
||||
- add non-array values support
|
||||
- add `multipart` as a shorthand
|
||||
|
||||
4.0.0 / 2014-04-09
|
||||
==================
|
||||
@@ -52,6 +124,81 @@
|
||||
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
|
||||
- Router & Route - public API
|
||||
|
||||
3.8.1 / 2014-05-27
|
||||
==================
|
||||
|
||||
* update connect to 2.17.3
|
||||
- deps: body-parser@1.2.2
|
||||
- deps: express-session@1.2.1
|
||||
- deps: method-override@1.0.2
|
||||
|
||||
3.8.0 / 2014-05-21
|
||||
==================
|
||||
|
||||
* keep previous `Content-Type` for `res.jsonp`
|
||||
* set proper `charset` in `Content-Type` for `res.send`
|
||||
* update connect to 2.17.1
|
||||
- fix `res.charset` appending charset when `content-type` has one
|
||||
- deps: express-session@1.2.0
|
||||
- deps: morgan@1.1.1
|
||||
- deps: serve-index@1.0.3
|
||||
|
||||
3.7.0 / 2014-05-18
|
||||
==================
|
||||
|
||||
* proper proxy trust with `app.set('trust proxy', trust)`
|
||||
- `app.set('trust proxy', 1)` trust first hop
|
||||
- `app.set('trust proxy', 'loopback')` trust loopback addresses
|
||||
- `app.set('trust proxy', '10.0.0.1')` trust single IP
|
||||
- `app.set('trust proxy', '10.0.0.1/16')` trust subnet
|
||||
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
|
||||
- `app.set('trust proxy', false)` turn off
|
||||
- `app.set('trust proxy', true)` trust everything
|
||||
* update connect to 2.16.2
|
||||
- deprecate `res.headerSent` -- use `res.headersSent`
|
||||
- deprecate `res.on("header")` -- use on-headers module instead
|
||||
- fix edge-case in `res.appendHeader` that would append in wrong order
|
||||
- json: use body-parser
|
||||
- urlencoded: use body-parser
|
||||
- dep: bytes@1.0.0
|
||||
- dep: cookie-parser@1.1.0
|
||||
- dep: csurf@1.2.0
|
||||
- dep: express-session@1.1.0
|
||||
- dep: method-override@1.0.1
|
||||
|
||||
3.6.0 / 2014-05-09
|
||||
==================
|
||||
|
||||
* deprecate `app.del()` -- use `app.delete()` instead
|
||||
* deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead
|
||||
- the edge-case `res.json(status, num)` requires `res.status(status).json(num)`
|
||||
* deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead
|
||||
- the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)`
|
||||
* support PURGE method
|
||||
- add `app.purge`
|
||||
- add `router.purge`
|
||||
- include PURGE in `app.all`
|
||||
* update connect to 2.15.0
|
||||
* Add `res.appendHeader`
|
||||
* Call error stack even when response has been sent
|
||||
* Patch `res.headerSent` to return Boolean
|
||||
* Patch `res.headersSent` for node.js 0.8
|
||||
* Prevent default 404 handler after response sent
|
||||
* dep: compression@1.0.2
|
||||
* dep: connect-timeout@1.1.0
|
||||
* dep: debug@^0.8.0
|
||||
* dep: errorhandler@1.0.1
|
||||
* dep: express-session@1.0.4
|
||||
* dep: morgan@1.0.1
|
||||
* dep: serve-favicon@2.0.0
|
||||
* dep: serve-index@1.0.2
|
||||
* update debug to 0.8.0
|
||||
* add `enable()` method
|
||||
* change from stderr to stdout
|
||||
* update methods to 1.0.0
|
||||
- add PURGE
|
||||
* update mkdirp to 0.5.0
|
||||
|
||||
3.5.3 / 2014-05-08
|
||||
==================
|
||||
|
||||
|
||||
34
Makefile
34
Makefile
@@ -1,34 +0,0 @@
|
||||
|
||||
MOCHA_OPTS= --check-leaks
|
||||
REPORTER = dot
|
||||
|
||||
check: test
|
||||
|
||||
test: test-unit test-acceptance
|
||||
|
||||
test-unit:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
--globals setImmediate,clearImmediate \
|
||||
$(MOCHA_OPTS)
|
||||
|
||||
test-acceptance:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
--bail \
|
||||
test/acceptance/*.js
|
||||
|
||||
test-cov: lib-cov
|
||||
@EXPRESS_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
|
||||
|
||||
lib-cov:
|
||||
@jscoverage lib lib-cov
|
||||
|
||||
bench:
|
||||
@$(MAKE) -C benchmarks
|
||||
|
||||
clean:
|
||||
rm -f coverage.html
|
||||
rm -fr lib-cov
|
||||
|
||||
.PHONY: test test-unit test-acceptance bench clean
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
|
||||
[](https://travis-ci.org/visionmedia/express) [](https://www.gittip.com/visionmedia/)
|
||||
[](http://badge.fury.io/js/express) [](https://travis-ci.org/visionmedia/express) [](https://coveralls.io/r/visionmedia/express) [](https://www.gittip.com/visionmedia/)
|
||||
|
||||
```js
|
||||
var express = require('express');
|
||||
@@ -95,7 +95,9 @@ To run the test suite, first invoke the following command within the repo, insta
|
||||
|
||||
Then run the tests:
|
||||
|
||||
$ make test
|
||||
```sh
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## Contributors
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ function restrict(req, res, next) {
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('login');
|
||||
res.redirect('/login');
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, function(req, res){
|
||||
@@ -116,7 +116,7 @@ app.post('/login', function(req, res){
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
+ ' username and password.'
|
||||
+ ' (use "tj" and "foobar")';
|
||||
res.redirect('login');
|
||||
res.redirect('/login');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,14 +3,10 @@
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var favicon = require('static-favicon');
|
||||
var cookie-parser = require('cookie-parser');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// ignore GET /favicon.ico
|
||||
app.use(favicon());
|
||||
|
||||
// pass a secret to cookieParser() for signed cookies
|
||||
app.use(cookieParser('manny is cool'));
|
||||
|
||||
|
||||
@@ -4,18 +4,10 @@
|
||||
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var favicon = require('static-favicon');
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// add favicon() before logger() so
|
||||
// GET /favicon.ico requests are not
|
||||
// logged, because this middleware
|
||||
// reponds to /favicon.ico and does not
|
||||
// call next()
|
||||
app.use(favicon());
|
||||
|
||||
// custom log format
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
app.use(logger(':method :url'));
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var favicon = require('static-favicon');
|
||||
var silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
// general config
|
||||
@@ -23,8 +22,6 @@ if ('production' == app.settings.env) {
|
||||
app.disable('verbose errors');
|
||||
}
|
||||
|
||||
app.use(favicon());
|
||||
|
||||
silent || app.use(logger('dev'));
|
||||
|
||||
// Routes
|
||||
|
||||
@@ -17,7 +17,7 @@ app.resource = function(path, obj) {
|
||||
obj.range(req, res, a, b, format);
|
||||
});
|
||||
this.get(path + '/:id', obj.show);
|
||||
this.del(path + '/:id', obj.destroy);
|
||||
this.delete(path + '/:id', obj.destroy);
|
||||
};
|
||||
|
||||
// Fake records
|
||||
|
||||
@@ -34,7 +34,7 @@ var users = {
|
||||
res.send('user ' + req.params.uid);
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
delete: function(req, res){
|
||||
res.send('delete users');
|
||||
}
|
||||
};
|
||||
@@ -44,7 +44,7 @@ var pets = {
|
||||
res.send('user ' + req.params.uid + '\'s pets');
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
delete: function(req, res){
|
||||
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
|
||||
}
|
||||
};
|
||||
@@ -52,13 +52,13 @@ var pets = {
|
||||
app.map({
|
||||
'/users': {
|
||||
get: users.list,
|
||||
del: users.del,
|
||||
delete: users.delete,
|
||||
'/:uid': {
|
||||
get: users.get,
|
||||
'/pets': {
|
||||
get: pets.list,
|
||||
'/:pid': {
|
||||
del: pets.del
|
||||
delete: pets.delete
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
|
||||
res.send('Editing user ' + req.user.name);
|
||||
});
|
||||
|
||||
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
|
||||
app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
|
||||
res.send('Deleted user ' + req.user.name);
|
||||
});
|
||||
|
||||
|
||||
4
index.js
4
index.js
@@ -1,4 +1,2 @@
|
||||
|
||||
module.exports = process.env.EXPRESS_COV
|
||||
? require('./lib-cov/express')
|
||||
: require('./lib/express');
|
||||
module.exports = require('./lib/express');
|
||||
|
||||
@@ -11,6 +11,8 @@ var query = require('./middleware/query');
|
||||
var debug = require('debug')('express:application');
|
||||
var View = require('./view');
|
||||
var http = require('http');
|
||||
var compileTrust = require('./utils').compileTrust;
|
||||
var deprecate = require('./utils').deprecate;
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
@@ -48,6 +50,7 @@ app.defaultConfiguration = function(){
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
this.set('env', env);
|
||||
this.set('subdomain offset', 2);
|
||||
this.set('trust proxy', false);
|
||||
|
||||
debug('booting in %s mode', env);
|
||||
|
||||
@@ -306,6 +309,12 @@ app.set = function(setting, val){
|
||||
return this.settings[setting];
|
||||
} else {
|
||||
this.settings[setting] = val;
|
||||
|
||||
if (setting === 'trust proxy') {
|
||||
debug('compile trust proxy %j', val);
|
||||
this.set('trust proxy fn', compileTrust(val));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
@@ -432,7 +441,7 @@ app.all = function(path){
|
||||
|
||||
// del -> delete alias
|
||||
|
||||
app.del = app.delete;
|
||||
app.del = deprecate(app.delete, 'app.del: Use app.delete instead');
|
||||
|
||||
/**
|
||||
* Render the given view `name` name with `options`
|
||||
|
||||
@@ -8,6 +8,7 @@ var http = require('http');
|
||||
var fresh = require('fresh');
|
||||
var parseRange = require('range-parser');
|
||||
var parse = require('parseurl');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
|
||||
/**
|
||||
* Request prototype.
|
||||
@@ -239,19 +240,26 @@ req.is = function(types){
|
||||
/**
|
||||
* Return the protocol string "http" or "https"
|
||||
* when requested with TLS. When the "trust proxy"
|
||||
* setting is enabled the "X-Forwarded-Proto" header
|
||||
* field will be trusted. If you're running behind
|
||||
* a reverse proxy that supplies https for you this
|
||||
* may be enabled.
|
||||
* setting trusts the socket address, the
|
||||
* "X-Forwarded-Proto" header field will be trusted.
|
||||
* If you're running behind a reverse proxy that
|
||||
* supplies https for you this may be enabled.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('protocol', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
if (this.connection.encrypted) return 'https';
|
||||
if (!trustProxy) return 'http';
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
|
||||
if (!trust(this.connection.remoteAddress)) {
|
||||
return this.connection.encrypted
|
||||
? 'https'
|
||||
: 'http';
|
||||
}
|
||||
|
||||
// Note: X-Forwarded-Proto is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
var proto = this.get('X-Forwarded-Proto') || 'http';
|
||||
return proto.split(/\s*,\s*/)[0];
|
||||
});
|
||||
@@ -270,36 +278,36 @@ req.__defineGetter__('secure', function(){
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the remote address, or when
|
||||
* "trust proxy" is `true` return
|
||||
* the upstream addr.
|
||||
* Return the remote address from the trusted proxy.
|
||||
*
|
||||
* The is the remote address on the socket unless
|
||||
* "trust proxy" is set.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ip', function(){
|
||||
return this.ips[0] || this.connection.remoteAddress;
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
return proxyaddr(this, trust);
|
||||
});
|
||||
|
||||
/**
|
||||
* When "trust proxy" is `true`, parse
|
||||
* the "X-Forwarded-For" ip address list.
|
||||
* When "trust proxy" is set, trusted proxy addresses + client.
|
||||
*
|
||||
* For example if the value were "client, proxy1, proxy2"
|
||||
* you would receive the array `["client", "proxy1", "proxy2"]`
|
||||
* where "proxy2" is the furthest down-stream.
|
||||
* where "proxy2" is the furthest down-stream and "proxy1" and
|
||||
* "proxy2" were trusted.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ips', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
var val = this.get('X-Forwarded-For');
|
||||
return trustProxy && val
|
||||
? val.split(/ *, */)
|
||||
: [];
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var addrs = proxyaddr.all(this, trust);
|
||||
return addrs.slice(1).reverse();
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -339,19 +347,30 @@ req.__defineGetter__('path', function(){
|
||||
/**
|
||||
* Parse the "Host" header field hostname.
|
||||
*
|
||||
* When the "trust proxy" setting trusts the socket
|
||||
* address, the "X-Forwarded-Host" header field will
|
||||
* be trusted.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('host', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
var host = trustProxy && this.get('X-Forwarded-Host');
|
||||
host = host || this.get('Host');
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var host = this.get('X-Forwarded-Host');
|
||||
|
||||
if (!host || !trust(this.connection.remoteAddress)) {
|
||||
host = this.get('Host');
|
||||
}
|
||||
|
||||
if (!host) return;
|
||||
|
||||
// IPv6 literal support
|
||||
var offset = host[0] === '['
|
||||
? host.indexOf(']') + 1
|
||||
: 0;
|
||||
var index = host.indexOf(':', offset);
|
||||
|
||||
return ~index
|
||||
? host.substring(0, index)
|
||||
: host;
|
||||
|
||||
@@ -9,7 +9,9 @@ var escapeHtml = require('escape-html');
|
||||
var sign = require('cookie-signature').sign;
|
||||
var normalizeType = require('./utils').normalizeType;
|
||||
var normalizeTypes = require('./utils').normalizeTypes;
|
||||
var setCharset = require('./utils').setCharset;
|
||||
var contentDisposition = require('./utils').contentDisposition;
|
||||
var deprecate = require('./utils').deprecate;
|
||||
var etag = require('./utils').etag;
|
||||
var statusCodes = http.STATUS_CODES;
|
||||
var cookie = require('cookie');
|
||||
@@ -82,6 +84,8 @@ res.links = function(links){
|
||||
res.send = function(body){
|
||||
var req = this.req;
|
||||
var head = 'HEAD' == req.method;
|
||||
var type;
|
||||
var encoding;
|
||||
var len;
|
||||
|
||||
// settings
|
||||
@@ -130,12 +134,23 @@ res.send = function(body){
|
||||
|
||||
// ETag support
|
||||
// TODO: W/ support
|
||||
if (app.settings.etag && len && 'GET' == req.method) {
|
||||
if (app.settings.etag && len && ('GET' == req.method || 'HEAD' == req.method)) {
|
||||
if (!this.get('ETag')) {
|
||||
this.set('ETag', etag(body));
|
||||
}
|
||||
}
|
||||
|
||||
// write strings in utf-8
|
||||
if ('string' === typeof body) {
|
||||
encoding = 'utf8';
|
||||
type = this.get('Content-Type');
|
||||
|
||||
// reflect this in content-type
|
||||
if ('string' === typeof type) {
|
||||
this.set('Content-Type', setCharset(type, 'utf-8'));
|
||||
}
|
||||
}
|
||||
|
||||
// freshness
|
||||
if (req.fresh) this.statusCode = 304;
|
||||
|
||||
@@ -148,7 +163,8 @@ res.send = function(body){
|
||||
}
|
||||
|
||||
// respond
|
||||
this.end(head ? null : body);
|
||||
this.end((head ? null : body), encoding);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -174,6 +190,9 @@ res.json = function(obj){
|
||||
// res.json(body, status) backwards compat
|
||||
if ('number' == typeof arguments[1]) {
|
||||
this.statusCode = arguments[1];
|
||||
return 'number' === typeof obj
|
||||
? jsonNumDeprecated.call(this, obj)
|
||||
: jsonDeprecated.call(this, obj);
|
||||
} else {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
@@ -192,6 +211,12 @@ res.json = function(obj){
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
var jsonDeprecated = deprecate(res.json,
|
||||
'res.json(obj, status): Use res.json(status, obj) instead');
|
||||
|
||||
var jsonNumDeprecated = deprecate(res.json,
|
||||
'res.json(num, status): Use res.status(status).json(num) instead');
|
||||
|
||||
/**
|
||||
* Send JSON response with JSONP callback support.
|
||||
*
|
||||
@@ -214,6 +239,9 @@ res.jsonp = function(obj){
|
||||
// res.json(body, status) backwards compat
|
||||
if ('number' == typeof arguments[1]) {
|
||||
this.statusCode = arguments[1];
|
||||
return 'number' === typeof obj
|
||||
? jsonpNumDeprecated.call(this, obj)
|
||||
: jsonpDeprecated.call(this, obj);
|
||||
} else {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
@@ -230,7 +258,7 @@ res.jsonp = function(obj){
|
||||
var callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
this.set('Content-Type', 'application/json');
|
||||
this.get('Content-Type') || this.set('Content-Type', 'application/json');
|
||||
|
||||
// fixup callback
|
||||
if (Array.isArray(callback)) {
|
||||
@@ -247,6 +275,12 @@ res.jsonp = function(obj){
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
var jsonpDeprecated = deprecate(res.json,
|
||||
'res.jsonp(obj, status): Use res.jsonp(status, obj) instead');
|
||||
|
||||
var jsonpNumDeprecated = deprecate(res.json,
|
||||
'res.jsonp(num, status): Use res.status(status).jsonp(num) instead');
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
|
||||
@@ -7,6 +7,7 @@ var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
var debug = require('debug')('express:router');
|
||||
var parseUrl = require('parseurl');
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
@@ -121,6 +122,7 @@ proto.handle = function(req, res, done) {
|
||||
var idx = 0;
|
||||
var removed = '';
|
||||
var slashAdded = false;
|
||||
var paramcalled = {};
|
||||
|
||||
// store options for OPTIONS request
|
||||
// only used if OPTIONS request
|
||||
@@ -129,23 +131,36 @@ proto.handle = function(req, res, done) {
|
||||
// middleware and routes
|
||||
var stack = self.stack;
|
||||
|
||||
// manage inter-router variables
|
||||
var parent = req.next;
|
||||
var parentUrl = req.baseUrl || '';
|
||||
done = wrap(done, function(old, err) {
|
||||
req.baseUrl = parentUrl;
|
||||
req.next = parent;
|
||||
old(err);
|
||||
});
|
||||
req.next = next;
|
||||
|
||||
// for options requests, respond with a default if nothing else responds
|
||||
if (method === 'options') {
|
||||
var old = done;
|
||||
done = function(err) {
|
||||
done = wrap(done, function(old, err) {
|
||||
if (err || options.length === 0) return old(err);
|
||||
|
||||
var body = options.join(',');
|
||||
return res.set('Allow', body).send(body);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
(function next(err) {
|
||||
next();
|
||||
|
||||
function next(err) {
|
||||
if (err === 'route') {
|
||||
err = undefined;
|
||||
}
|
||||
|
||||
var layer = stack[idx++];
|
||||
var layerPath;
|
||||
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
}
|
||||
@@ -155,6 +170,7 @@ proto.handle = function(req, res, done) {
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
removed = '';
|
||||
@@ -183,10 +199,12 @@ proto.handle = function(req, res, done) {
|
||||
}
|
||||
}
|
||||
|
||||
// Capture one-time layer values
|
||||
req.params = layer.params;
|
||||
layerPath = layer.path;
|
||||
|
||||
// this should be done for the layer
|
||||
return self.process_params(layer, req, res, function(err) {
|
||||
return self.process_params(layer, paramcalled, req, res, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -203,22 +221,29 @@ proto.handle = function(req, res, done) {
|
||||
}
|
||||
|
||||
function trim_prefix() {
|
||||
var c = path[layer.path.length];
|
||||
var c = path[layerPath.length];
|
||||
if (c && '/' != c && '.' != c) return next(err);
|
||||
|
||||
// Trim off the part of the url that matches the route
|
||||
// middleware (.use stuff) needs to have the path stripped
|
||||
debug('trim prefix (%s) from url %s', removed, req.url);
|
||||
removed = layer.path;
|
||||
removed = layerPath;
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
|
||||
// Ensure leading slash
|
||||
if (!fqdn && '/' != req.url[0]) {
|
||||
if (!fqdn && req.url[0] !== '/') {
|
||||
req.url = '/' + req.url;
|
||||
slashAdded = true;
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.handle.name || 'anonymous', layer.path, req.originalUrl);
|
||||
// Setup base URL (no trailing slash)
|
||||
if (removed.length && removed.substr(-1) === '/') {
|
||||
req.baseUrl = parentUrl + removed.substring(0, removed.length - 1);
|
||||
} else {
|
||||
req.baseUrl = parentUrl + removed;
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.handle.name || 'anonymous', layerPath, req.originalUrl);
|
||||
var arity = layer.handle.length;
|
||||
if (err) {
|
||||
if (arity === 4) {
|
||||
@@ -232,21 +257,27 @@ proto.handle = function(req, res, done) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
function wrap(old, fn) {
|
||||
return function () {
|
||||
var args = [old].concat(slice.call(arguments));
|
||||
fn.apply(this, args);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Process any parameters for the route.
|
||||
* Process any parameters for the layer.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
proto.process_params = function(route, req, res, done) {
|
||||
proto.process_params = function(layer, called, req, res, done) {
|
||||
var params = this.params;
|
||||
|
||||
// captured parameters from the route, keys and values
|
||||
var keys = route.keys;
|
||||
// captured parameters from the layer, keys and values
|
||||
var keys = layer.keys;
|
||||
|
||||
// fast track
|
||||
if (!keys || keys.length === 0) {
|
||||
@@ -254,10 +285,12 @@ proto.process_params = function(route, req, res, done) {
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var name;
|
||||
var paramIndex = 0;
|
||||
var key;
|
||||
var paramVal;
|
||||
var paramCallbacks;
|
||||
var paramCalled;
|
||||
|
||||
// process params in order
|
||||
// param callbacks can be async
|
||||
@@ -272,24 +305,41 @@ proto.process_params = function(route, req, res, done) {
|
||||
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
paramVal = key && req.params[key.name];
|
||||
paramCallbacks = key && params[key.name];
|
||||
|
||||
if (!key) {
|
||||
return done();
|
||||
}
|
||||
|
||||
name = key.name;
|
||||
paramVal = req.params[name];
|
||||
paramCallbacks = params[name];
|
||||
paramCalled = called[name];
|
||||
|
||||
if (paramVal === undefined || !paramCallbacks) {
|
||||
return param();
|
||||
}
|
||||
|
||||
// param previously called with same value or error occurred
|
||||
if (paramCalled && (paramCalled.err || paramCalled.val === paramVal)) {
|
||||
return param(paramCalled.err);
|
||||
}
|
||||
|
||||
called[name] = paramCalled = { val: paramVal };
|
||||
|
||||
try {
|
||||
if (paramCallbacks && undefined !== paramVal) {
|
||||
return paramCallback();
|
||||
} else if (key) {
|
||||
return param();
|
||||
}
|
||||
return paramCallback();
|
||||
} catch (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
if (err && paramCalled) {
|
||||
// store error
|
||||
paramCalled.err = err;
|
||||
}
|
||||
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
if (err || !fn) return param(err);
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
|
||||
91
lib/utils.js
91
lib/utils.js
@@ -5,6 +5,37 @@
|
||||
var mime = require('send').mime;
|
||||
var crc32 = require('buffer-crc32');
|
||||
var basename = require('path').basename;
|
||||
var deprecate = require('util').deprecate;
|
||||
var proxyaddr = require('proxy-addr');
|
||||
|
||||
/**
|
||||
* Simple detection of charset parameter in content-type
|
||||
*/
|
||||
var charsetRegExp = /;\s*charset\s*=/;
|
||||
|
||||
/**
|
||||
* Deprecate function, like core `util.deprecate`,
|
||||
* but with NODE_ENV and color support.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @param {String} msg
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.deprecate = function(fn, msg){
|
||||
if (process.env.NODE_ENV === 'test') return fn;
|
||||
|
||||
// prepend module name
|
||||
msg = 'express: ' + msg;
|
||||
|
||||
if (process.stderr.isTTY) {
|
||||
// colorize
|
||||
msg = '\x1b[31;1m' + msg + '\x1b[0m';
|
||||
}
|
||||
|
||||
return deprecate(fn, msg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return ETag for `body`.
|
||||
@@ -132,3 +163,63 @@ function acceptParams(str, index) {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile "proxy trust" value to function.
|
||||
*
|
||||
* @param {Boolean|String|Number|Array|Function} val
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.compileTrust = function(val) {
|
||||
if (typeof val === 'function') return val;
|
||||
|
||||
if (val === true) {
|
||||
// Support plain true/false
|
||||
return function(){ return true };
|
||||
}
|
||||
|
||||
if (typeof val === 'number') {
|
||||
// Support trusting hop count
|
||||
return function(a, i){ return i < val };
|
||||
}
|
||||
|
||||
if (typeof val === 'string') {
|
||||
// Support comma-separated values
|
||||
val = val.split(/ *, */);
|
||||
}
|
||||
|
||||
return proxyaddr.compile(val || []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the charset in a given Content-Type string.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {String} charset
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.setCharset = function(type, charset){
|
||||
if (!type || !charset) return type;
|
||||
|
||||
var exists = charsetRegExp.test(type);
|
||||
|
||||
// removing existing charset
|
||||
if (exists) {
|
||||
var parts = type.split(';');
|
||||
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
if (charsetRegExp.test(';' + parts[i])) {
|
||||
parts.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type = parts.join(';');
|
||||
}
|
||||
|
||||
return type + '; charset=' + charset;
|
||||
};
|
||||
|
||||
92
package.json
92
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Sinatra inspired web development framework",
|
||||
"version": "4.1.2",
|
||||
"version": "4.3.2",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
{
|
||||
@@ -16,6 +16,10 @@
|
||||
"name": "Ciaran Jessup",
|
||||
"email": "ciaranj@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Douglas Christopher Wilson",
|
||||
"email": "doug@somethingdoug.com"
|
||||
},
|
||||
{
|
||||
"name": "Guillermo Rauch",
|
||||
"email": "rauchg@gmail.com"
|
||||
@@ -29,43 +33,6 @@
|
||||
"email": "shtylman+expressjs@gmail.com"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"parseurl": "1.0.1",
|
||||
"accepts": "1.0.1",
|
||||
"type-is": "1.1.0",
|
||||
"range-parser": "1.0.0",
|
||||
"cookie": "0.1.2",
|
||||
"buffer-crc32": "0.2.1",
|
||||
"fresh": "0.2.2",
|
||||
"methods": "0.1.0",
|
||||
"send": "0.3.0",
|
||||
"cookie-signature": "1.0.3",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"utils-merge": "1.0.0",
|
||||
"escape-html": "1.0.1",
|
||||
"qs": "0.6.6",
|
||||
"serve-static": "1.1.0",
|
||||
"path-to-regexp": "0.1.2",
|
||||
"debug": ">= 0.7.3 < 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.18.2",
|
||||
"body-parser": "1.0.2",
|
||||
"connect-redis": "~2.0.0",
|
||||
"ejs": "~1.0.0",
|
||||
"express-session": "1.0.3",
|
||||
"jade": "~0.35.0",
|
||||
"marked": "0.3.2",
|
||||
"multiparty": "~3.2.4",
|
||||
"static-favicon": "1.0.2",
|
||||
"hjs": "~0.0.6",
|
||||
"should": "~3.3.1",
|
||||
"supertest": "~0.11.0",
|
||||
"method-override": "1.0.0",
|
||||
"cookie-parser": "1.0.1",
|
||||
"morgan": "1.0.0",
|
||||
"vhost": "1.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"express",
|
||||
"framework",
|
||||
@@ -78,12 +45,53 @@
|
||||
"api"
|
||||
],
|
||||
"repository": "git://github.com/visionmedia/express",
|
||||
"scripts": {
|
||||
"prepublish": "npm prune",
|
||||
"test": "make test"
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "1.0.1",
|
||||
"parseurl": "1.0.1",
|
||||
"proxy-addr": "1.0.0",
|
||||
"range-parser": "1.0.0",
|
||||
"type-is": "1.2.0",
|
||||
"cookie": "0.1.2",
|
||||
"buffer-crc32": "0.2.1",
|
||||
"fresh": "0.2.2",
|
||||
"methods": "1.0.0",
|
||||
"send": "0.3.0",
|
||||
"cookie-signature": "1.0.3",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"utils-merge": "1.0.0",
|
||||
"escape-html": "1.0.1",
|
||||
"qs": "0.6.6",
|
||||
"serve-static": "1.1.0",
|
||||
"path-to-regexp": "0.1.2",
|
||||
"debug": "0.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.1",
|
||||
"istanbul": "0.2.10",
|
||||
"mocha": "~1.19.0",
|
||||
"should": "~3.3.1",
|
||||
"supertest": "~0.12.0",
|
||||
"connect-redis": "~2.0.0",
|
||||
"ejs": "~1.0.0",
|
||||
"jade": "~0.35.0",
|
||||
"marked": "0.3.2",
|
||||
"multiparty": "~3.2.4",
|
||||
"hjs": "~0.0.6",
|
||||
"body-parser": "1.2.2",
|
||||
"cookie-parser": "1.1.0",
|
||||
"express-session": "1.2.1",
|
||||
"method-override": "1.0.2",
|
||||
"morgan": "1.1.1",
|
||||
"vhost": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
"scripts": {
|
||||
"prepublish": "npm prune",
|
||||
"test": "mocha --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
|
||||
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
|
||||
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
var after = require('after');
|
||||
var express = require('../')
|
||||
, Router = express.Router
|
||||
, methods = require('methods')
|
||||
@@ -183,5 +184,97 @@ describe('Router', function(){
|
||||
|
||||
router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done);
|
||||
});
|
||||
|
||||
it('should only call once per request', function(done) {
|
||||
var count = 0;
|
||||
var req = { url: '/foo/bob/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('user', function(req, res, next, user) {
|
||||
count++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/foo/:user/', new Router());
|
||||
router.use('/foo/:user/', sub);
|
||||
|
||||
router.handle(req, {}, function(err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(count, 1);
|
||||
assert.equal(req.user, 'bob');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call when values differ', function(done) {
|
||||
var count = 0;
|
||||
var req = { url: '/foo/bob/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('user', function(req, res, next, user) {
|
||||
count++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/foo/:user/', new Router());
|
||||
router.use('/:user/bob/', sub);
|
||||
|
||||
router.handle(req, {}, function(err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(count, 2);
|
||||
assert.equal(req.user, 'foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parallel requests', function() {
|
||||
it('should not mix requests', function(done) {
|
||||
var req1 = { url: '/foo/50/bar', method: 'get' };
|
||||
var req2 = { url: '/foo/10/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
done = after(2, done);
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('ms', function(req, res, next, ms) {
|
||||
ms = parseInt(ms, 10);
|
||||
req.ms = ms;
|
||||
setTimeout(next, ms);
|
||||
});
|
||||
|
||||
router.use('/foo/:ms/', new Router());
|
||||
router.use('/foo/:ms/', sub);
|
||||
|
||||
router.handle(req1, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(req1.ms, 50);
|
||||
assert.equal(req1.originalUrl, '/foo/50/bar');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle(req2, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(req2.ms, 10);
|
||||
assert.equal(req2.originalUrl, '/foo/10/bar');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
var app = require('../../examples/auth/app')
|
||||
, request = require('supertest');
|
||||
|
||||
function redirects(to, fn){
|
||||
return function(err, res){
|
||||
res.statusCode.should.equal(302)
|
||||
res.headers.should.have.property('location').match(to);
|
||||
fn()
|
||||
}
|
||||
}
|
||||
var request = require('supertest')
|
||||
|
||||
function getCookie(res) {
|
||||
return res.headers['set-cookie'][0].split(';')[0];
|
||||
@@ -18,25 +10,93 @@ describe('auth', function(){
|
||||
it('should redirect to /login', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(redirects(/login$/, done))
|
||||
.expect('Location', '/login')
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /restricted (w/o cookie)',function(){
|
||||
it('should redirect to /login', function(done){
|
||||
describe('GET /login',function(){
|
||||
it('should render login form', function(done){
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.end(redirects(/login$/,done))
|
||||
.get('/login')
|
||||
.expect(200, /<form/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /login', function(){
|
||||
it('should fail without proper credentials', function(done){
|
||||
it('should display login error', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=not-tj&password=foobar')
|
||||
.end(redirects(/login$/, done))
|
||||
.expect('Location', '/login')
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/login')
|
||||
.set('Cookie', getCookie(res))
|
||||
.expect(200, /Authentication failed/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /logout',function(){
|
||||
it('should redirect to /', function(done){
|
||||
request(app)
|
||||
.get('/logout')
|
||||
.expect('Location', '/')
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /restricted',function(){
|
||||
it('should redirect to /login without cookie', function(done){
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.expect('Location', '/login')
|
||||
.expect(302, done)
|
||||
})
|
||||
|
||||
it('should succeed with proper cookie', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=tj&password=foobar')
|
||||
.expect('Location', '/')
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.set('Cookie', getCookie(res))
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /login', function(){
|
||||
it('should fail without proper username', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=not-tj&password=foobar')
|
||||
.expect('Location', '/login')
|
||||
.expect(302, done)
|
||||
})
|
||||
|
||||
it('should fail without proper password', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=tj&password=baz')
|
||||
.expect('Location', '/login')
|
||||
.expect(302, done)
|
||||
})
|
||||
|
||||
it('should succeed with proper credentials', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=tj&password=foobar')
|
||||
.expect('Location', '/')
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,16 +7,43 @@ describe('content-negotiation', function(){
|
||||
it('should default to text/html', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>')
|
||||
.end(done);
|
||||
.expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)
|
||||
})
|
||||
|
||||
it('should accept to text/plain', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'text/plain')
|
||||
.expect(' - Tobi\n - Loki\n - Jane\n')
|
||||
.end(done);
|
||||
.expect(200, ' - Tobi\n - Loki\n - Jane\n', done)
|
||||
})
|
||||
|
||||
it('should accept to application/json', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users', function(){
|
||||
it('should default to text/html', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)
|
||||
})
|
||||
|
||||
it('should accept to text/plain', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.set('Accept', 'text/plain')
|
||||
.expect(200, ' - Tobi\n - Loki\n - Jane\n', done)
|
||||
})
|
||||
|
||||
it('should accept to application/json', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,6 +18,35 @@ describe('cookies', function(){
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should respond to cookie', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.send({ remember: 1 })
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Cookie', res.headers['set-cookie'][0])
|
||||
.expect(200, /Remembered/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /forget', function(){
|
||||
it('should clear cookie', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.send({ remember: 1 })
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/forget')
|
||||
.set('Cookie', res.headers['set-cookie'][0])
|
||||
.expect('Set-Cookie', /remember=;/)
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /', function(){
|
||||
@@ -25,10 +54,20 @@ describe('cookies', function(){
|
||||
request(app)
|
||||
.post('/')
|
||||
.send({ remember: 1 })
|
||||
.end(function(err, res){
|
||||
.expect(302, function(err, res){
|
||||
res.headers.should.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should no set cookie w/o reminder', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.send({})
|
||||
.expect(302, function(err, res){
|
||||
res.headers.should.not.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB |
@@ -7,10 +7,38 @@ describe('mvc', function(){
|
||||
it('should redirect to /users', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Location', '/users')
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /pet/0', function(){
|
||||
it('should get pet', function(done){
|
||||
request(app)
|
||||
.get('/pet/0')
|
||||
.expect(200, /Tobi/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /pet/0/edit', function(){
|
||||
it('should get pet edit page', function(done){
|
||||
request(app)
|
||||
.get('/pet/0/edit')
|
||||
.expect(/<form/)
|
||||
.expect(200, /Tobi/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /pet/2', function(){
|
||||
it('should update the pet', function(done){
|
||||
request(app)
|
||||
.put('/pet/3')
|
||||
.send({ pet: { name: 'Boots' } })
|
||||
.end(function(err, res){
|
||||
res.should.have.status(302);
|
||||
res.headers.location.should.include('/users');
|
||||
done();
|
||||
if (err) return done(err);
|
||||
request(app)
|
||||
.get('/pet/3/edit')
|
||||
.expect(200, /Boots/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -65,11 +93,8 @@ describe('mvc', function(){
|
||||
it('should display the edit form', function(done){
|
||||
request(app)
|
||||
.get('/user/1/edit')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>Guillermo</h1>');
|
||||
res.text.should.include('value="put"');
|
||||
done();
|
||||
})
|
||||
.expect(/Guillermo/)
|
||||
.expect(200, /<form/, done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -79,13 +104,26 @@ describe('mvc', function(){
|
||||
.put('/user/1')
|
||||
.send({ user: { name: 'Tobo' }})
|
||||
.end(function(err, res){
|
||||
if (err) return done(err);
|
||||
request(app)
|
||||
.get('/user/1/edit')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>Tobo</h1>');
|
||||
done();
|
||||
})
|
||||
.expect(200, /Tobo/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /user/:id/pet', function(){
|
||||
it('should create a pet for user', function(done){
|
||||
request(app)
|
||||
.post('/user/2/pet')
|
||||
.send({ pet: { name: 'Snickers' }})
|
||||
.expect('Location', '/user/2')
|
||||
.expect(302, function(err, res){
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/user/2')
|
||||
.expect(200, /Snickers/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -24,11 +24,72 @@ describe('web-service', function(){
|
||||
it('should respond users json', function(done){
|
||||
request(app)
|
||||
.get('/api/users?api-key=foo')
|
||||
.end(function(err, res){
|
||||
res.should.be.json;
|
||||
res.text.should.equal('[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/repos', function(){
|
||||
describe('without an api key', function(){
|
||||
it('should respond with 400 bad request', function(done){
|
||||
request(app)
|
||||
.get('/api/repos')
|
||||
.expect(400, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with an invalid api key', function(){
|
||||
it('should respond with 401 unauthorized', function(done){
|
||||
request(app)
|
||||
.get('/api/repos?api-key=rawr')
|
||||
.expect(401, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a valid api key', function(){
|
||||
it('should respond repos json', function(done){
|
||||
request(app)
|
||||
.get('/api/repos?api-key=foo')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(/"name":"express"/)
|
||||
.expect(/"url":"http:\/\/github.com\/visionmedia\/express"/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/user/:name/repos', function(){
|
||||
describe('without an api key', function(){
|
||||
it('should respond with 400 bad request', function(done){
|
||||
request(app)
|
||||
.get('/api/user/loki/repos')
|
||||
.expect(400, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with an invalid api key', function(){
|
||||
it('should respond with 401 unauthorized', function(done){
|
||||
request(app)
|
||||
.get('/api/user/loki/repos?api-key=rawr')
|
||||
.expect(401, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a valid api key', function(){
|
||||
it('should respond user repos json', function(done){
|
||||
request(app)
|
||||
.get('/api/user/loki/repos?api-key=foo')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(/"name":"stylus"/)
|
||||
.expect(/"url":"http:\/\/github.com\/learnboost\/stylus"/)
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should 404 with unknown user', function(done){
|
||||
request(app)
|
||||
.get('/api/user/bob/repos?api-key=foo')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -45,4 +106,4 @@ describe('web-service', function(){
|
||||
});
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,6 +16,29 @@ describe('HEAD', function(){
|
||||
.head('/tobi')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should output the same headers as GET requests', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/tobi', function(req, res){
|
||||
// send() detects HEAD
|
||||
res.send('tobi');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/tobi')
|
||||
.expect(200, function(err, res){
|
||||
if (err) return done(err);
|
||||
var headers = res.headers;
|
||||
request(app)
|
||||
.get('/tobi')
|
||||
.expect(200, function(err, res){
|
||||
if (err) return done(err);
|
||||
assert.deepEqual(res.headers, headers);
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe('app.head()', function(){
|
||||
|
||||
@@ -84,3 +84,12 @@ describe('in production', function(){
|
||||
process.env.NODE_ENV = 'test';
|
||||
})
|
||||
})
|
||||
|
||||
describe('without NODE_ENV', function(){
|
||||
it('should default to development', function(){
|
||||
process.env.NODE_ENV = '';
|
||||
var app = express();
|
||||
app.get('env').should.equal('development');
|
||||
process.env.NODE_ENV = 'test';
|
||||
})
|
||||
})
|
||||
|
||||
@@ -37,6 +37,11 @@ describe('app', function(){
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
it('should fail if not given fn', function(){
|
||||
var app = express();
|
||||
app.param.bind(app, ':name', 'bob').should.throw();
|
||||
})
|
||||
})
|
||||
|
||||
describe('.param(names, fn)', function(){
|
||||
@@ -95,5 +100,124 @@ describe('app', function(){
|
||||
.get('/user/123')
|
||||
.expect('123', done);
|
||||
})
|
||||
|
||||
it('should only call once per request', function(done) {
|
||||
var app = express();
|
||||
var called = 0;
|
||||
var count = 0;
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
called++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.use(function(req, res) {
|
||||
res.end([count, called, req.user].join(' '));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bob')
|
||||
.expect('2 1 bob', done);
|
||||
})
|
||||
|
||||
it('should call when values differ', function(done) {
|
||||
var app = express();
|
||||
var called = 0;
|
||||
var count = 0;
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
called++;
|
||||
req.users = (req.users || []).concat(user);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/:user/bob', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.use(function(req, res) {
|
||||
res.end([count, called, req.users.join(',')].join(' '));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bob')
|
||||
.expect('2 2 foo,bob', done);
|
||||
})
|
||||
|
||||
it('should catch thrown error', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('id', function(req, res, next, id){
|
||||
throw new Error('err!');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
var id = req.params.id;
|
||||
res.send('' + id);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.expect(500, done);
|
||||
})
|
||||
|
||||
it('should defer to next route', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('id', function(req, res, next, id){
|
||||
next('route');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
var id = req.params.id;
|
||||
res.send('' + id);
|
||||
});
|
||||
|
||||
app.get('/:name/123', function(req, res){
|
||||
res.send('name');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.expect('name', done);
|
||||
})
|
||||
|
||||
it('should defer all the param routes', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('id', function(req, res, next, val){
|
||||
if (val === 'new') return next('route');
|
||||
return next();
|
||||
});
|
||||
|
||||
app.all('/user/:id', function(req, res){
|
||||
res.send('all.id');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
res.send('get.id');
|
||||
});
|
||||
|
||||
app.get('/user/new', function(req, res){
|
||||
res.send('get.new');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/new')
|
||||
.expect('get.new', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -54,6 +54,27 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle render error throws', function(done){
|
||||
var app = express();
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'fale';
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
throw new Error('err!');
|
||||
};
|
||||
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', function(err, str){
|
||||
err.should.be.ok;
|
||||
err.message.should.equal('err!');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the file does not exist', function(){
|
||||
it('should provide a helpful error', function(done){
|
||||
var app = express();
|
||||
@@ -132,6 +153,68 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('caching', function(){
|
||||
it('should always lookup view without cache', function(done){
|
||||
var app = express();
|
||||
var count = 0;
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'fake';
|
||||
count++;
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
fn(null, 'abstract engine');
|
||||
};
|
||||
|
||||
app.set('view cache', false);
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(2);
|
||||
str.should.equal('abstract engine');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should cache with "view cache" setting', function(done){
|
||||
var app = express();
|
||||
var count = 0;
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'fake';
|
||||
count++;
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
fn(null, 'abstract engine');
|
||||
};
|
||||
|
||||
app.set('view cache', true);
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.render(name, options, fn)', function(){
|
||||
@@ -175,5 +258,37 @@ describe('app', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('caching', function(){
|
||||
it('should cache with cache option', function(done){
|
||||
var app = express();
|
||||
var count = 0;
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'fake';
|
||||
count++;
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
fn(null, 'abstract engine');
|
||||
};
|
||||
|
||||
app.set('view cache', false);
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', {cache: true}, function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
app.render('something', {cache: true}, function(err, str){
|
||||
if (err) return done(err);
|
||||
count.should.equal(1);
|
||||
str.should.equal('abstract engine');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,11 +6,10 @@ var express = require('../')
|
||||
|
||||
describe('app.router', function(){
|
||||
describe('methods supported', function(){
|
||||
methods.forEach(function(method){
|
||||
methods.concat('del').forEach(function(method){
|
||||
if (method === 'connect') return;
|
||||
|
||||
it('should include ' + method.toUpperCase(), function(done){
|
||||
if (method == 'delete') method = 'del';
|
||||
var app = express();
|
||||
var calls = [];
|
||||
|
||||
@@ -589,6 +588,45 @@ describe('app.router', function(){
|
||||
.expect('editing user 12', done);
|
||||
})
|
||||
|
||||
it('should run in order added', function(done){
|
||||
var app = express();
|
||||
var path = [];
|
||||
|
||||
app.get('*', function(req, res, next){
|
||||
path.push(0);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res, next){
|
||||
path.push(1);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(req, res, next){
|
||||
path.push(2);
|
||||
next();
|
||||
});
|
||||
|
||||
app.all('/user/:id', function(req, res, next){
|
||||
path.push(3);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('*', function(req, res, next){
|
||||
path.push(4);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(req, res, next){
|
||||
path.push(5);
|
||||
res.end(path.join(','))
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/1')
|
||||
.expect(200, '0,1,2,3,4,5', done);
|
||||
})
|
||||
|
||||
it('should be chainable', function(){
|
||||
var app = express();
|
||||
app.get('/', function(){}).should.equal(app);
|
||||
|
||||
@@ -41,4 +41,4 @@ describe('middleware', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
36
test/req.acceptsEncoding.js
Normal file
36
test/req.acceptsEncoding.js
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptsEncodings', function(){
|
||||
it('should be true if encoding accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsEncoding('gzip').should.be.ok;
|
||||
req.acceptsEncoding('deflate').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', ' gzip, deflate')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should be false if encoding not accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsEncoding('bogus').should.not.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', ' gzip, deflate')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
53
test/req.acceptsLanguage.js
Normal file
53
test/req.acceptsLanguage.js
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptsLanguage', function(){
|
||||
it('should be true if language accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguage('en-us').should.be.ok;
|
||||
req.acceptsLanguage('en').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Language', 'en;q=.5, en-us')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should be false if language not accpeted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguage('es').should.not.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Language', 'en;q=.5, en-us')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
describe('when Accept-Language is not present', function(){
|
||||
it('should always return true', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptsLanguage('en').should.be.ok;
|
||||
req.acceptsLanguage('es').should.be.ok;
|
||||
req.acceptsLanguage('jp').should.be.ok;
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
87
test/req.baseUrl.js
Normal file
87
test/req.baseUrl.js
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
var express = require('..')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('req', function(){
|
||||
describe('.baseUrl', function(){
|
||||
it('should be empty for top-level route', function(done){
|
||||
var app = express()
|
||||
|
||||
app.get('/:a', function(req, res){
|
||||
res.end(req.baseUrl)
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect(200, '', done)
|
||||
})
|
||||
|
||||
it('should contain lower path', function(done){
|
||||
var app = express()
|
||||
var sub = express.Router()
|
||||
|
||||
sub.get('/:b', function(req, res){
|
||||
res.end(req.baseUrl)
|
||||
})
|
||||
app.use('/:a', sub)
|
||||
|
||||
request(app)
|
||||
.get('/foo/bar')
|
||||
.expect(200, '/foo', done);
|
||||
})
|
||||
|
||||
it('should contain full lower path', function(done){
|
||||
var app = express()
|
||||
var sub1 = express.Router()
|
||||
var sub2 = express.Router()
|
||||
var sub3 = express.Router()
|
||||
|
||||
sub3.get('/:d', function(req, res){
|
||||
res.end(req.baseUrl)
|
||||
})
|
||||
sub2.use('/:c', sub3)
|
||||
sub1.use('/:b', sub2)
|
||||
app.use('/:a', sub1)
|
||||
|
||||
request(app)
|
||||
.get('/foo/bar/baz/zed')
|
||||
.expect(200, '/foo/bar/baz', done);
|
||||
})
|
||||
|
||||
it('should travel through routers correctly', function(done){
|
||||
var urls = []
|
||||
var app = express()
|
||||
var sub1 = express.Router()
|
||||
var sub2 = express.Router()
|
||||
var sub3 = express.Router()
|
||||
|
||||
sub3.get('/:d', function(req, res, next){
|
||||
urls.push('0@' + req.baseUrl)
|
||||
next()
|
||||
})
|
||||
sub2.use('/:c', sub3)
|
||||
sub1.use('/', function(req, res, next){
|
||||
urls.push('1@' + req.baseUrl)
|
||||
next()
|
||||
})
|
||||
sub1.use('/bar', sub2)
|
||||
sub1.use('/bar', function(req, res, next){
|
||||
urls.push('2@' + req.baseUrl)
|
||||
next()
|
||||
})
|
||||
app.use(function(req, res, next){
|
||||
urls.push('3@' + req.baseUrl)
|
||||
next()
|
||||
})
|
||||
app.use('/:a', sub1)
|
||||
app.use(function(req, res, next){
|
||||
urls.push('4@' + req.baseUrl)
|
||||
res.end(urls.join(','))
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/foo/bar/baz/zed')
|
||||
.expect(200, '3@,1@/foo,0@/foo/bar/baz,2@/foo/bar,4@', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -69,5 +69,70 @@ describe('req', function(){
|
||||
.set('Host', '[::1]:3000')
|
||||
.expect('[::1]', done);
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is enabled', function(){
|
||||
it('should respect X-Forwarded-Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'example.com')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
|
||||
it('should ignore X-Forwarded-Host if socket addr not trusted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', '10.0.0.1');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'example.com')
|
||||
.expect('localhost', done);
|
||||
})
|
||||
|
||||
it('should default to Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'example.com')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
it('should ignore X-Forwarded-Host', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'localhost')
|
||||
.set('X-Forwarded-Host', 'evil')
|
||||
.expect('localhost', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -20,6 +20,21 @@ describe('req', function(){
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('client', done);
|
||||
})
|
||||
|
||||
it('should return the addr after trusted proxy', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', 2);
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ip);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('p1', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
|
||||
@@ -20,6 +20,21 @@ describe('req', function(){
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('["client","p1","p2"]', done);
|
||||
})
|
||||
|
||||
it('should stop at first untrusted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', 2);
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ips);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('["p1","p2"]', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
|
||||
@@ -32,6 +32,21 @@ describe('req', function(){
|
||||
.expect('https', done);
|
||||
})
|
||||
|
||||
it('should ignore X-Forwarded-Proto if socket addr not trusted', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('trust proxy', '10.0.0.1');
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.protocol);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-Proto', 'https')
|
||||
.expect('http', done);
|
||||
})
|
||||
|
||||
it('should default to http', function(done){
|
||||
var app = express();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
var express = require('../');
|
||||
var assert = require('assert');
|
||||
var express = require('..');
|
||||
|
||||
function req(ret) {
|
||||
return {
|
||||
@@ -27,5 +28,11 @@ describe('req', function(){
|
||||
ret.type = 'users';
|
||||
req('users=0-').range(Infinity).should.eql(ret);
|
||||
})
|
||||
|
||||
it('should return undefined if no range', function(){
|
||||
var ret = [{ start: 0, end: 50 }, { start: 60, end: 100 }];
|
||||
ret.type = 'bytes';
|
||||
assert(req('').range(120) === undefined);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('res', function(){
|
||||
|
||||
app.use(function(req, res){
|
||||
res.attachment('/path/to/image.png');
|
||||
res.send('foo');
|
||||
res.send(new Buffer(4));
|
||||
});
|
||||
|
||||
request(app)
|
||||
|
||||
@@ -4,9 +4,9 @@ var express = require('../')
|
||||
, utils = require('../lib/utils')
|
||||
, assert = require('assert');
|
||||
|
||||
var app = express();
|
||||
var app1 = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
app1.use(function(req, res, next){
|
||||
res.format({
|
||||
'text/plain': function(){
|
||||
res.send('hey');
|
||||
@@ -25,7 +25,7 @@ app.use(function(req, res, next){
|
||||
});
|
||||
});
|
||||
|
||||
app.use(function(err, req, res, next){
|
||||
app1.use(function(err, req, res, next){
|
||||
if (!err.types) throw err;
|
||||
res.send(err.status, 'Supports: ' + err.types.join(', '));
|
||||
})
|
||||
@@ -53,10 +53,24 @@ app3.use(function(req, res, next){
|
||||
})
|
||||
});
|
||||
|
||||
var app4 = express();
|
||||
|
||||
app4.get('/', function(req, res, next){
|
||||
res.format({
|
||||
text: function(){ res.send('hey') },
|
||||
html: function(){ res.send('<p>hey</p>') },
|
||||
json: function(){ res.send({ message: 'hey' }) }
|
||||
});
|
||||
});
|
||||
|
||||
app4.use(function(err, req, res, next){
|
||||
res.send(err.status, 'Supports: ' + err.types.join(', '));
|
||||
})
|
||||
|
||||
describe('res', function(){
|
||||
describe('.format(obj)', function(){
|
||||
describe('with canonicalized mime types', function(){
|
||||
test(app);
|
||||
test(app1);
|
||||
})
|
||||
|
||||
describe('with extnames', function(){
|
||||
@@ -71,6 +85,31 @@ describe('res', function(){
|
||||
.expect('default', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('in router', function(){
|
||||
test(app4);
|
||||
})
|
||||
|
||||
describe('in router', function(){
|
||||
var app = express();
|
||||
var router = express.Router();
|
||||
|
||||
router.get('/', function(req, res, next){
|
||||
res.format({
|
||||
text: function(){ res.send('hey') },
|
||||
html: function(){ res.send('<p>hey</p>') },
|
||||
json: function(){ res.send({ message: 'hey' }) }
|
||||
});
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
res.send(err.status, 'Supports: ' + err.types.join(', '));
|
||||
})
|
||||
|
||||
app.use(router)
|
||||
|
||||
test(app)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
114
test/res.json.js
114
test/res.json.js
@@ -17,8 +17,22 @@ describe('res', function(){
|
||||
.expect('{"foo":"bar"}', done);
|
||||
})
|
||||
|
||||
it('should not override previous Content-Types', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.type('application/vnd.example+json');
|
||||
res.json({ hello: 'world' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/vnd.example+json; charset=utf-8')
|
||||
.expect(200, '{"hello":"world"}', done);
|
||||
})
|
||||
|
||||
describe('when given primitives', function(){
|
||||
it('should respond with json', function(done){
|
||||
it('should respond with json for null', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
@@ -27,11 +41,34 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('null');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, 'null', done)
|
||||
})
|
||||
|
||||
it('should respond with json for Number', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.json(300);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '300', done)
|
||||
})
|
||||
|
||||
it('should respond with json for String', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.json('str');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '"str"', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -45,11 +82,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('["foo","bar","baz"]');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '["foo","bar","baz"]', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -63,11 +97,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -87,10 +118,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -111,10 +140,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('{\n "name": "tobi",\n "age": 2\n}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{\n "name": "tobi",\n "age": 2\n}', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -129,12 +156,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"id":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '{"id":1}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -148,30 +171,21 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"id":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '{"id":1}', done)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not override previous Content-Types', function(done){
|
||||
var app = express();
|
||||
it('should use status as second number for backwards compat', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.type('application/vnd.example+json');
|
||||
res.json({ hello: 'world' });
|
||||
});
|
||||
app.use(function(req, res){
|
||||
res.json(200, 201);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(200);
|
||||
res.headers.should.have.property('content-type', 'application/vnd.example+json');
|
||||
res.text.should.equal('{"hello":"world"}');
|
||||
done();
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '200', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -46,11 +46,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/?callback[a]=something')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"count":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"count":1}', done)
|
||||
})
|
||||
|
||||
it('should allow renaming callback', function(done){
|
||||
@@ -119,6 +116,34 @@ describe('res', function(){
|
||||
});
|
||||
});
|
||||
|
||||
it('should not override previous Content-Types with no callback', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.type('application/vnd.example+json');
|
||||
res.jsonp({ hello: 'world' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/vnd.example+json; charset=utf-8')
|
||||
.expect(200, '{"hello":"world"}', done);
|
||||
})
|
||||
|
||||
it('should override previous Content-Types with callback', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.type('application/vnd.example+json');
|
||||
res.jsonp({ hello: 'world' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/?callback=cb')
|
||||
.expect('Content-Type', 'text/javascript; charset=utf-8')
|
||||
.expect(200, /cb\(\{"hello":"world"\}\);$/, done);
|
||||
})
|
||||
|
||||
describe('when given primitives', function(){
|
||||
it('should respond with json', function(done){
|
||||
var app = express();
|
||||
@@ -129,11 +154,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('null');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, 'null', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -147,11 +169,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('["foo","bar","baz"]');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '["foo","bar","baz"]', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -165,11 +184,49 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when given primitives', function(){
|
||||
it('should respond with json for null', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.jsonp(null);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, 'null', done)
|
||||
})
|
||||
|
||||
it('should respond with json for Number', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.jsonp(300);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '300', done)
|
||||
})
|
||||
|
||||
it('should respond with json for String', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.jsonp('str');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '"str"', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -189,10 +246,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -213,10 +268,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('{\n "name": "tobi",\n "age": 2\n}');
|
||||
done();
|
||||
});
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{\n "name": "tobi",\n "age": 2\n}', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -231,12 +284,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"id":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '{"id":1}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -250,12 +299,35 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(201);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"id":1}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '{"id":1}', done)
|
||||
})
|
||||
|
||||
it('should use status as second number for backwards compat', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.jsonp(200, 201);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(201, '200', done)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not override previous Content-Types', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.type('application/vnd.example+json');
|
||||
res.jsonp({ hello: 'world' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('content-type', 'application/vnd.example+json; charset=utf-8')
|
||||
.expect(200, '{"hello":"world"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -140,8 +140,33 @@ describe('res', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'text/plain; charset=utf-8')
|
||||
.expect('hey')
|
||||
.expect(200, done);
|
||||
.expect(200, 'hey', done);
|
||||
})
|
||||
|
||||
it('should override charset in Content-Type', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.set('Content-Type', 'text/plain; charset=iso-8859-1').send('hey');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'text/plain; charset=utf-8')
|
||||
.expect(200, 'hey', done);
|
||||
})
|
||||
|
||||
it('should keep charset in Content-Type for Buffers', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(new Buffer('hi'));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'text/plain; charset=iso-8859-1')
|
||||
.expect(200, 'hi', done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -205,11 +230,8 @@ describe('res', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.text.should.equal('{"name":"tobi"}');
|
||||
done();
|
||||
})
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"name":"tobi"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
2
test/support/env.js
Normal file
2
test/support/env.js
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
Reference in New Issue
Block a user